Analytics
Retrieve engagement metrics for published posts.
Get Post Analytics
GET /api/v1/analytics/posts/:id
Authorization: Bearer chirpie_sk_YOUR_KEYReturns engagement metrics for a published post. Metrics are cached for 1 hour, so subsequent requests within that window return cached data.
The dashboard shows the same numbers: open a published post from the schedule and its Analytics section reports exactly what this endpoint returns.
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
refresh | boolean | No | true asks the platform for the current numbers instead of serving the stored snapshot. Floored at one forced refresh per post every 5 minutes |
Force a refresh
Without refresh, a snapshot under an hour old is served as it is, which is why calling
this endpoint often costs nothing and never touches the platform. ?refresh=true skips the
snapshot and asks the platform now:
curl "https://chirpie.ai/api/v1/analytics/posts/POST_ID?refresh=true" \
-H "Authorization: Bearer chirpie_sk_YOUR_KEY"A forced refresh is allowed once per post every 5 minutes, on every platform. Past
that floor the request returns 429 analytics_refresh_rate_limited, carrying
Retry-After in seconds:
{
"error": {
"code": "analytics_refresh_rate_limited",
"message": "This post's metrics were refreshed less than 5 minutes ago. Read it without refresh to get the stored numbers, or try again in 180 seconds."
}
}The floor is per post; the
burst limit is per API key. They are separate
limits, so a refresh still costs a request against the burst allowance like any other call,
and one post being inside its floor never slows down a refresh of a different post. The
stored numbers stay available throughout: drop refresh and the same call answers 200
from the snapshot. Poll on the snapshot and reserve refresh=true for the moment a human is
actually looking.
The floor exists because every platform meters the calls Chirpie makes on your behalf. A
dashboard polling refresh=true every few seconds would spend that allowance on numbers
nobody reads between refreshes.
Every client carries it: SDK getPostAnalytics(postId, { refresh: true }), CLI
chirpie analytics POST_ID --refresh, MCP chirpie_analytics with refresh: true, and the
Refresh option on the n8n node's Analytics > Get.
Response 200 OK
Every platform with a metrics API returns the same object. Metrics that platform does
not expose come back as 0, so the shape never changes. Telegram is the exception: it
has no metrics API at all, and returns 502 instead.
{
"data": {
"post_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"platform": "x",
"impressions": 1250,
"likes": 42,
"retweets": 8,
"replies": 3,
"quotes": 1,
"bookmarks": 5,
"clicks": 15,
"fetched_at": "2026-03-23T10:30:00.000Z"
}
}Metrics
| Metric | X | Bluesky | Threads | Mastodon | Description | |||
|---|---|---|---|---|---|---|---|---|
impressions | Yes | No | Pages only | Yes | No | Yes | Yes | Views |
likes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Likes/favourites/reactions |
retweets | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Reposts/shares/saves |
replies | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Replies/comments |
quotes | Yes | Yes | No | Yes | No | No | No | Quote posts |
bookmarks | Yes | No | No | No | No | Yes | No | Bookmarks/saves |
clicks | Yes | No | Pages only | No | No | No | Yes | Clicks |
fetched_at | Yes | Yes | Yes | Yes | Yes | Yes | Yes | When fetched |
More platforms coming soon.
Each platform names these things differently. Chirpie maps them onto one set of fields:
LinkedIn comments, Mastodon replies and Facebook comments all arrive as replies;
Mastodon reblogs, LinkedIn shares, Facebook shares and Instagram shares all arrive as
retweets; Threads views, Instagram views and Facebook media views all arrive as
impressions; Mastodon favourites and Facebook reactions (every reaction type added
together) arrive as likes; Instagram saves arrive as bookmarks.
On Instagram and Facebook, impressions is a view count, not a separate
impressions figure: Meta retired its impressions metrics and now reports views, so
impressions carries the number of times the post was played or displayed.
LinkedIn impressions and clicks come from LinkedIn Page statistics, so they are
reported for an account with account_type: "organization" and are always 0 for a
personal profile, which LinkedIn publishes no reach numbers for. Page figures are
lifetime totals covering the past twelve months.
Telegram exposes no metrics API. An analytics call on a Telegram post returns
502 upstream_error with Could not fetch metrics.
Error Responses
| Status | Code | Condition |
|---|---|---|
400 | bad_request | Post not yet published or missing platform ID |
403 | insufficient_scope | The API key does not hold analytics:read. See Scopes |
404 | not_found | Post not found |
429 | analytics_refresh_rate_limited | ?refresh=true inside the 5-minute floor for this post. Carries Retry-After; the stored numbers are still one ordinary call away |
502 | upstream_error | Could not fetch metrics from the platform, and no cached snapshot exists. Telegram always lands here |
Analytics Retention
| Plan | Retention |
|---|---|
| Free | 48 hours |
| Agent | 48 hours |
| Starter | 30 days |
| Pro | 90 days |
| Scale | 365 days |
| Enterprise | 365 days |