Analytics
Get top posts
Cached top posts with full metrics and time-limited media URLs, sortable including outlier sorts that surface LinkedIn posts that over-performed the account's own baseline. Workspace-wide lists mix LinkedIn and X posts. For an X thread the metrics cover its first tweet only, not the whole thread.
/api/public/analytics/top-postscurl "https://postiv.ai/api/public/analytics/top-posts?accountId=all&limit=10&sort=date" \
-H "Authorization: Bearer pk_postiv_..."{
"success": true,
"posts": [
{
"id": "ap1...",
"hook": "I lost my biggest client...",
"postUrl": "https://linkedin.com/...",
"mediaType": "text",
"isLeadMagnet": false,
"mediaUrls": [],
"metrics": {
"impressions": 12400,
"engagements": 640,
"engagementRate": 5.2
},
"outliers": {
"engagementMultiplier": 2.1
}
}
]
}Authorization
Requires an organization API key with the analytics:read scope, sent as Authorization: Bearer or x-api-key.
Query parameters
accountIduuid|'all'requiredAlias: account_id. One LinkedIn profile/page or X account from list-analytics-accounts, or 'all' for a workspace-wide list that mixes channels.
channelstringNarrows accountId 'all' to one channel. With a specific accountId it must match that account's channel (400 channel_mismatch).
linkedinxlimitintdefault: 251-100.
sortstringdefault: "date"Outlier sorts are LinkedIn only: 400 unsupported_sort when only X is in scope, and in a mixed list LinkedIn posts rank first. X posts support date, impressions and engagements.
dateengagementsimpressionsengagement_outlierreach_outliertype_outliertype_reach_outlierperiodintTrailing days, 1-365. Default is all-time (no filter) if omitted and no start/end.
startstringendstringResponse
Top posts with metrics and outlier scores.
posts[].iduuidposts[].platformstringThe post's channel.
posts[].accountIduuidWorkspace-wide lists only: the analytics account the post belongs to.
posts[].hook / postUrl / linkedinUrn / createdAt / mediaType / category / isLeadMagnet / lastFetchedAtmixedposts[].xPostId / syncStatusmixedX only: the tweet id and where its stored metrics stand in the periodic X sync.
posts[].mediaUrlsstring[]Refreshed signed URLs. Always empty for X: X post images are Postiv assets, read them with GET /api/public/posts/:id.
posts[].metricsobject{ impressions, engagements, engagementRate, reactions, comments, reshares, membersReached, postSaves, postSends, linkClicks, premiumCtaClicks, followersGainedFromContent, profileViewsFromContent, videoPlays, videoWatchTime, videoViewers } for LinkedIn. For X: impressions, engagements, engagementRate, linkClicks and profileClicks, with reactions = likes, comments = replies, reshares = reposts, plus quotes and bookmarks.
posts[].outliersobjectLinkedIn only: { weightedEngagementScore, impressionsMultiplier, engagementMultiplier, mediaTypeImpressionsMultiplier, mediaTypeEngagementMultiplier, calculatedAt, overallBaselineSampleSize, mediaTypeBaselineSampleSize }
posts[].metadataobject{ postText, postLink }
Errors
| 400 | missing_account_id |
| 400 | invalid_channel / channel_mismatch: channel is not linkedin or x, or contradicts the channel of accountId |
| 400 | unsupported_sort: an outlier sort when only X posts are in scope; sort X by date, impressions or engagements |
| 404 | analytics_account_not_found |
Good to know
- Rows with no analytics id AND all-null impressions/engagements (queue-only, never imported) are filtered out: this only ranks posts that actually have analytics.