Account & Settings
Read-only API
In this article
Read-only API
Metricgram includes a basic read-only API so external tools can query group data without accessing your dashboard.
Beta: The API is currently in testing and is available from the Pro plan and above.
Create an API token
- Go to Settings in your Metricgram dashboard.
- Open the API tokens section.
- Choose the group that the token should access.
- Click Create token.
- Copy the token immediately. It is only shown once.
Each token is linked to one group. For now, you can have one active token per group, and you can revoke it at any time.
Authentication
Send the token as a Bearer token in the Authorization header:
curl -H "Authorization: Bearer YOUR_API_TOKEN" \
"https://metricgram.com/api/v1/members"
Successful responses are returned in JSON format.
Rate limit
Each token is limited to 60 requests per hour. When the limit is reached, the API returns 429 rate_limited.
GET /api/v1/members
Returns the members of the group linked to the token.
Supported parameters:
status: all, active, inactive, left, or unbanedadmin: true or falseis_bot: true or false. This parameter only filters boolean values; unknown values (null) are returned only when this filter is not used.query: search by Telegram ID, username, or first namelimit: default 100, maximum 500page: default 1include_email: true or false. Default: false. When true, Metricgram uses the subscriber email first, then the email stored on the group member record. It can benull.
Example:
curl -G "https://metricgram.com/api/v1/members" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
--data-urlencode "status=active" \
--data-urlencode "limit=100" \
--data-urlencode "include_email=true" \
--data-urlencode "page=1"
Example JSON response:
{
"data": [
{
"telegram_id": "123456789",
"username": "ana",
"first_name": "Ana",
"language_code": "es",
"is_bot": false,
"status": "active",
"joined_at": "2026-04-10T12:30:00Z",
"admin": false,
"email": "ana@example.com"
}
],
"meta": {
"group_id": 42,
"group_title": "My group",
"page": 1,
"limit": 100,
"count": 1,
"has_more": false,
"filters": {
"status": "active"
}
}
}
Main response fields:
telegram_id: Telegram ID as text. It can benullif Metricgram does not have it stored.username,first_name, andlanguage_code: public Telegram data. They can benull.is_bot:true,false, ornullwhen Telegram has not provided that value.status:active,inactive,left,unbaned, ornullif no status is stored.admin:trueorfalse.joined_at: join date in ISO 8601 format, ornull.email: only returned wheninclude_email=true. Metricgram uses the subscriber email first, then the email stored on the group member record. It can benull.meta: pagination and filter data for the request.
GET /api/v1/active_users
Returns non-admin members with messages or reactions recorded in the selected date range. The group owner is excluded from this list. Results are sorted by descending activity: first by activity_count, then by last_activity_at.
Supported parameters:
start_date: start date inYYYY-MM-DDformat. Default: 30 days ago.end_date: end date inYYYY-MM-DDformat. Default: today.limit: default 100, maximum 500page: default 1include_email: true or false. Default: false. When true, Metricgram uses the subscriber email first, then the email stored on the group member record. It can benull.
Example:
curl -G "https://metricgram.com/api/v1/active_users" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
--data-urlencode "start_date=2026-04-01" \
--data-urlencode "end_date=2026-04-30" \
--data-urlencode "limit=100" \
--data-urlencode "include_email=true" \
--data-urlencode "page=1"
Example JSON response:
{
"data": [
{
"telegram_id": "123456789",
"username": "ana",
"first_name": "Ana",
"language_code": "es",
"is_bot": null,
"member_status": "active",
"admin": false,
"joined_at": "2026-04-10T12:30:00Z",
"email": "ana@example.com",
"message_count": 18,
"reaction_count": 4,
"activity_count": 22,
"last_message_at": "2026-04-30T19:15:00Z",
"last_reaction_at": null,
"last_activity_at": "2026-04-30T19:15:00Z"
}
],
"meta": {
"group_id": 42,
"group_title": "My group",
"start_date": "2026-04-01",
"end_date": "2026-04-30",
"page": 1,
"limit": 100,
"count": 1,
"has_more": false
}
}
Main response fields:
telegram_id: Telegram ID as text. It can benullif Metricgram does not have it stored.username,first_name, andlanguage_code: public Telegram data. They can benull.is_bot:true,false, ornullwhen Telegram has not provided that value.member_status: same text values asstatusin/api/v1/members; it can benullif no status is stored.admin:trueorfalse; in this endpoint it is normallyfalsebecause admins are excluded.joined_at: join date in ISO 8601 format, ornull.email: only returned wheninclude_email=true. Metricgram uses the subscriber email first, then the email stored on the group member record. It can benull.message_count,reaction_count, andactivity_count: integer counters calculated only within the selected date range, not the user's full history.activity_countismessage_count + reaction_count.last_message_atandlast_reaction_at: ISO 8601 datetime, ornullwhen there was no activity of that type.last_activity_at: ISO 8601 datetime for the latest recorded activity.meta: group, date range, and pagination data for the request.
GET /api/v1/subscriber_health
Returns the same data as the Subscriber health panel: active linked subscribers, recent activity, risk signals, upcoming renewals, and plan breakdown.
By default it uses the same windows as the panel: 30 days for activity and 14 days for upcoming renewals. If you change these parameters, the results may not exactly match the panel.
Supported parameters:
activity_days: number of days for recent activity. Default 30, maximum 365; values above 365 are capped to 365.renewal_days: number of days for upcoming renewals. Default 14, maximum 90; values above 90 are capped to 90.include_email: true or false. Default: false. When true, at-risk subscribers include the email stored on the subscription. It can benull.
Example:
curl -G "https://metricgram.com/api/v1/subscriber_health" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
--data-urlencode "activity_days=30" \
--data-urlencode "renewal_days=14" \
--data-urlencode "include_email=true"
Example JSON response:
{
"data": {
"total_subscribers": 24,
"activity_days": 30,
"renewal_days": 14,
"activity_range": { "start_date": "2026-04-01", "end_date": "2026-04-30" },
"previous_activity_range": { "start_date": "2026-03-02", "end_date": "2026-03-31" },
"compare_activity_drop": true,
"metrics": {
"engaged": { "count": 18, "percentage": 75 },
"at_risk": { "count": 3, "percentage": 13 },
"upcoming_renewals": { "count": 5, "percentage": 21 }
},
"at_risk_subscribers": [
{
"telegram_id": "123456789",
"username": "ana",
"first_name": "Ana",
"language_code": "es",
"is_bot": false,
"plan_lines": ["Pro monthly", "29 EUR / month"],
"subscribed_until": "2026-05-08T10:00:00Z",
"subscription_attention": "renews_on",
"subscription_attention_days": 2,
"risk_indicators": ["activity_drop"],
"risk_score": 1,
"risk_level": "low",
"current_message_count": 2,
"current_reaction_count": 0,
"current_activity_count": 2,
"previous_message_count": 12,
"previous_reaction_count": 1,
"previous_activity_count": 13,
"email": "ana@example.com"
}
],
"plan_rows": [
{
"plan_lines": ["Pro monthly", "29 EUR / month"],
"active_count": 12,
"engaged_count": 9,
"at_risk_count": 2,
"engaged_percentage": 75,
"at_risk_percentage": 17
}
]
},
"meta": {
"group_id": 42,
"group_title": "My group"
}
}
Main fields:
total_subscribers: active linked subscribers included in the calculation.activity_range/previous_activity_range: current and previous activity windows.metrics: summary counters and percentages.at_risk_subscribers: subscribers with risk signals;risk_indicatorscan beno_activity,activity_drop, orcanceled;risk_levelcan below,medium, orhigh.plan_rows: active linked subscribers grouped by subscription plan.email: only returned insideat_risk_subscriberswheninclude_email=true.
Common errors
401 unauthorized: the token is missing or invalid.403 forbidden: the token does not have permission or the group is not available for the account.400 invalid_date_range: the date range is invalid.429 rate_limited: the hourly limit has been reached.
Was this article helpful?
Thank you for your feedback!