Social media analytics API: How to pull post performance data programmatically
How to retrieve engagement metrics from Instagram, X, LinkedIn, TikTok, YouTube, and other platforms via API — what each platform exposes, the pain of normalizing it, and a unified approach.
The need is simple: how did this post perform?
You published content to five platforms. Now you need the numbers. Impressions, likes, comments, shares — the data your reporting dashboard, client portal, or internal analytics system runs on.
Every major social platform exposes engagement metrics through its API. The problem is not availability. The problem is that each platform exposes different metrics, through different endpoints, with different authentication requirements, in different response formats, on different timelines.
Pulling analytics from one platform is straightforward. Pulling analytics from five platforms and presenting them in a unified view is an integration project.
What each platform exposes natively
Instagram (Graph API)
Instagram’s Insights API requires a Business or Creator account connected to a Facebook Page. You need the instagram_basic and instagram_manage_insights permissions, which require Meta app review.
Media-level metrics: impressions, reach, engagement, saved, video_views (for video content)
Account-level metrics: impressions, reach, follower_count, profile_views
The endpoint is GET /{media-id}/insights with metric names passed as a parameter:
curl "https://graph.facebook.com/v21.0/{media-id}/insights?metric=impressions,reach,saved&access_token={token}"Limitations: Stories insights expire after 24 hours. Reels have a different metric set than feed posts. Carousel posts return metrics for the carousel as a whole, not per-card. Rate limits are shared across all Graph API calls for the same app, not isolated to insights.
X / Twitter (API v2)
X exposes public metrics on the tweet object itself. When fetching a tweet via GET /2/tweets/{id}, include tweet.fields=public_metrics:
curl "https://api.x.com/2/tweets/1234567890?tweet.fields=public_metrics" \ -H "Authorization: Bearer {token}"Returns retweet_count, reply_count, like_count, quote_count, and impression_count.
The catch: impression_count requires the tweet author’s authentication context. Public metrics from a third-party app return everything except impressions. To get impressions, you need OAuth 2.0 with the tweet owner’s token — which means different auth flows depending on which metric you need.
Rate limits depend on your API tier. Free tier gets 1 tweet lookup per 15-minute window. Basic ($200/month) gets 15,000 tweet reads. Pro tier and above gives higher limits.
LinkedIn (Marketing API)
LinkedIn provides share statistics through the Marketing API, not the Community Management API. This requires a Marketing Developer Platform application with the r_organization_social_feed permission for organization posts, or r_member_social_feed for personal posts.
curl "https://api.linkedin.com/rest/organizationalEntityShareStatistics?q=organizationalEntity&organizationalEntity=urn:li:organization:12345&shares=urn:li:share:67890" \ -H "Authorization: Bearer {token}" \ -H "LinkedIn-Version: 202401"Returns totalShareStatistics with impressionCount, clickCount, likeCount, commentCount, shareCount.
The pain point: LinkedIn requires a versioned header (LinkedIn-Version) on every request, and available metrics differ between the Marketing API and the Community Management API. Many developers request the wrong API and get blank responses.
TikTok (Content Posting API)
TikTok’s video query endpoint returns basic metrics if the video was published through the Content Posting API and has a public post ID:
curl "https://open.tiktokapis.com/v2/video/query/?fields=like_count,comment_count,share_count,view_count" \ -H "Authorization: Bearer {token}" \ -d '{"filters": {"video_ids": ["1234567890"]}}'Limitation: Analytics are only available for videos posted through TikTok’s API (not manual uploads). The video must be public. Historical metrics for older posts require a separate Research API with additional approval.
YouTube (Data API v3)
YouTube’s Analytics API and Data API provide different metric sets. For basic video stats, the Data API is simpler:
curl "https://www.googleapis.com/youtube/v3/videos?part=statistics&id=VIDEO_ID&key={api_key}"Returns viewCount, likeCount, favoriteCount, commentCount.
For deeper analytics (watch time, audience retention, traffic sources), you need the YouTube Analytics API, which requires OAuth and has a different endpoint structure entirely. Quota costs matter — each API call consumes quota units from a daily budget of 10,000 units, and video queries cost 1-3 units depending on parts requested.
Facebook (Graph API)
Page post insights use a similar pattern to Instagram:
curl "https://graph.facebook.com/v21.0/{post-id}/insights?metric=post_impressions,post_clicks,post_reactions_like_total&access_token={token}"Requires a Page access token with the read_insights permission. Metrics are returned as time-series data with period values (day, week, days_28, lifetime).
Threads (API)
Threads exposes media insights through Meta’s API:
curl "https://graph.threads.net/v1.0/{media-id}/insights?metric=views,likes,replies,reposts,quotes,shares&access_token={token}"The API is newer and still evolving. Available metrics are views, likes, replies, reposts, quotes, and shares. Rate limits follow Meta’s standard Graph API limits.
The normalization problem
Each platform returns a different shape. Instagram calls it impressions. X calls it impression_count. YouTube calls it viewCount. LinkedIn wraps it in totalShareStatistics.impressionCount. TikTok calls it view_count.
A table of “likes” across platforms:
| Platform | Field name | Endpoint | Auth required |
|---|---|---|---|
likes (insights) | /{media-id}/insights | Page token + app review | |
| X | like_count | /2/tweets/{id} | Bearer token (any tier) |
likeCount | /organizationalEntityShareStatistics | Marketing API token | |
| TikTok | like_count | /v2/video/query/ | OAuth token |
| YouTube | likeCount | /youtube/v3/videos | API key or OAuth |
post_reactions_like_total | /{post-id}/insights | Page token | |
| Threads | likes | /{media-id}/insights | User token |
Seven platforms, seven field names, seven endpoint patterns, seven auth models. And this is just for “likes” — one of the simpler metrics.
Building a unified analytics view means writing a normalization layer that:
- Authenticates differently per platform
- Calls different endpoints with different parameter formats
- Parses different response structures
- Maps platform-specific metric names to your internal schema
- Handles different rate limit windows and retry logic
- Accounts for metrics that exist on some platforms but not others (Pinterest has
outbound_clicks; no other platform does)
This normalization layer is tedious to build and expensive to maintain. Every time a platform updates its API version, renames a field, or changes its rate limits, your layer needs updating.
Pulling unified analytics through Postproxy
Postproxy’s stats endpoint returns normalized engagement data for any post published through the API, across all platforms, in one request:
curl "https://api.postproxy.dev/api/posts/stats?post_ids=post_abc123,post_def456" \ -H "Authorization: Bearer YOUR_API_KEY"{ "data": { "post_abc123": { "platforms": [ { "profile_id": "prof_ig1", "platform": "instagram", "records": [ { "stats": { "impressions": 4521, "likes": 187, "comments": 23, "saved": 45, "profile_visits": 12, "follows": 3 }, "recorded_at": "2026-03-08T14:00:00Z" }, { "stats": { "impressions": 5102, "likes": 214, "comments": 31, "saved": 52, "profile_visits": 18, "follows": 5 }, "recorded_at": "2026-03-09T06:00:00Z" } ] }, { "profile_id": "prof_tw1", "platform": "twitter", "records": [ { "stats": { "impressions": 12840, "likes": 92, "retweets": 34, "comments": 15, "quotes": 8, "saved": 11 }, "recorded_at": "2026-03-08T14:00:00Z" } ] } ] } }}One request. Both platforms. Consistent envelope. Each platform returns its native metric set — Instagram includes saved and profile_visits, X includes retweets and quotes — but the structure is identical across platforms.
Snapshots, not just latest values
The stats endpoint returns all recorded snapshots, not just the current numbers. Each snapshot includes a recorded_at timestamp, so you can build trend lines:
Day 1: impressions: 1,200 → likes: 45Day 2: impressions: 3,800 → likes: 112Day 3: impressions: 5,100 → likes: 214Filter by time range to get exactly the window you need:
curl "https://api.postproxy.dev/api/posts/stats?post_ids=post_abc123&from=2026-03-01T00:00:00Z&to=2026-03-09T00:00:00Z" \ -H "Authorization: Bearer YOUR_API_KEY"This matters for reporting tools. Clients do not just want to know a post got 5,000 impressions — they want to see how quickly it got there, when engagement peaked, and how it compared to last week’s post over the same timeframe.
Thread analytics
For thread posts (X and Threads), the stats endpoint aggregates metrics across the entire thread — parent post plus all replies — under the parent post’s ID. You do not need to query each tweet in a thread individually.
A five-tweet thread on X returns combined impressions, likes, and retweets for the full conversation. This matches how most reporting tools want to present thread performance: as a single content unit, not five separate data points.
Building a cross-platform analytics dashboard
A practical implementation pulls stats on a schedule, stores them in your database, and renders them in your UI:
// Pull stats for recent posts every 8 hoursasync function syncPostStats(apiKey, postIds) { const batchSize = 50; // API max per request
for (let i = 0; i < postIds.length; i += batchSize) { const batch = postIds.slice(i, i + batchSize);
const response = await fetch( `https://api.postproxy.dev/api/posts/stats?post_ids=${batch.join(',')}`, { headers: { 'Authorization': `Bearer ${apiKey}` } } );
const { data } = await response.json();
for (const [postId, postData] of Object.entries(data)) { for (const platform of postData.platforms) { const latestRecord = platform.records[platform.records.length - 1];
await db.upsertPostStats({ post_id: postId, platform: platform.platform, profile_id: platform.profile_id, stats: latestRecord.stats, recorded_at: latestRecord.recorded_at }); } } }}Or skip the polling entirely — subscribe to the platform_post.insights webhook event and get notified when new analytics are available:
{ "id": "evt_ins_abc123", "type": "platform_post.insights", "created_at": "2026-03-09T06:00:00Z", "data": { "post_id": "post_abc123", "platform": "instagram", "insights": { "impressions": 5102, "likes": 214 } }}Your system updates in near-real-time without polling. No wasted API calls on posts that have not changed.
What the native APIs require vs. what Postproxy handles
| Concern | Native APIs (build yourself) | Postproxy stats endpoint |
|---|---|---|
| Authentication | Different OAuth flow per platform | One API key |
| Endpoint format | 7 different URL patterns | One endpoint: GET /api/posts/stats |
| Response normalization | Custom parser per platform | Consistent JSON envelope |
| Rate limits | Per-platform, shared with other API calls | Per-post batch (50 per request) |
| Historical data | Most platforms return latest only | Snapshots stored over time |
| Thread aggregation | Query each post individually | Aggregated under parent post ID |
| Webhooks for new data | Platform-specific (if available) | platform_post.insights event |
| Metrics available | Varies by platform, permission, and tier | Platform-native metrics, consistent structure |
Metrics available per platform
| Platform | impressions | likes | comments | saved | shares | retweets | quotes | replies | reposts | clicks | profile_visits | follows | outbound_clicks |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| x | x | x | x | x | x | ||||||||
| x | x | x | |||||||||||
| X (Twitter) | x | x | x | x | x | x | |||||||
| x | |||||||||||||
| TikTok | x | x | x | x | |||||||||
| YouTube | x | x | x | x | |||||||||
| Threads | x | x | x | x | x | x | |||||||
| x | x | x | x | x |
Each platform returns its native metric set. Postproxy does not fabricate metrics that a platform does not provide — LinkedIn genuinely only exposes impressions through its API. But the data that is available comes back in the same structure, from the same endpoint, with the same auth.
When to use the stats endpoint vs. native APIs
Use Postproxy’s stats endpoint if you publish through Postproxy and need cross-platform analytics in a consistent format. One integration covers all platforms. Historical snapshots are stored for you.
Use native platform APIs directly if you need metrics for content not published through Postproxy, need real-time analytics (sub-hour freshness), or need platform-specific metrics that go beyond post-level engagement (audience demographics, traffic sources, watch-time percentiles).
For most teams building dashboards, reporting tools, or client portals, the stats endpoint eliminates the normalization layer entirely. You write one integration instead of seven.
Start pulling cross-platform analytics through the Postproxy API.
Postproxy
One API for every social platform
Publish to Instagram, X, LinkedIn, TikTok, YouTube and more with a single request. Free plan, no credit card required.