Blog

Social Media Analytics API: Metrics, Post History, and Exports (2026)

Erwan Prost

Erwan Prost

· 16 min read

On this page

A social media analytics API returns engagement numbers for a published post through one interface instead of nine. The interface is the easy half. The hard half is that the nine platforms answer different questions, and they no longer even agree inside one company: Instagram still returns reach for a feed post, Facebook marks every post-level unique-reach metric deprecated above Graph API v25, and Threads never shipped a reach concept at all.

So build against the floor and treat everything above it as a per-platform bonus you verify before you promise it. Likes, comments, and shares are the only trio that seven of the nine platforms all report for a single post. Views narrow that to five. Unique reach narrows it to two. Saves, also two. Telegram and Google Business Profile return nothing at post level whatsoever, which is a product constraint rather than a gap in your integration.

Which metrics each platform actually returns

The table below covers one organic post owned by the connected account. Ad-product metrics are excluded, and so are fields that still ship in a payload while being permanently meaningless: YouTube's statistics.favoriteCount has been hard-coded to 0 since 28 August 2015 and is still in the response today. Every row links to the platform's own current reference, checked on 3 August 2026.

PlatformWhat you callWhat it returns for one postWhat it withholds
InstagramGET /{ig-media-id}/insightsviews, reach, likes, comments, saved, shares, reposts, profile_visits, follows, total_interactions on a feed post (Meta)impressions, deprecated at v22.0 and enforced on every version from 21 April 2025 (Meta). Reels drop profile_visits and follows; Stories drop likes, comments, and saved.
Facebook PageGET /{post-id}/insightspost_impressions_organic, post_impressions_paid, post_impressions_viral, post_clicks, post_reactions_by_type_total, post_video_views, post_video_avg_time_watched, post_media_view (Meta)Every post-level unique variant. post_impressions_unique and its siblings are marked deprecated above Graph API v25, and v26.0 released on 29 July 2026 (Meta). No saves. Comment and share counts come off the post object, not this endpoint.
ThreadsGET /{threads-media-id}/insightsviews, likes, replies, reposts, quotes, shares (Meta)No reach, no impressions, no saves. clicks exists at user level only. User-level since and until refuse anything below Unix 1712991600, which is 13 April 2024.
TikTokPOST /v2/video/query/, scope video.listview_count, like_count, comment_count, share_count (TikTok)Everything else. The Video Object is a closed 15-field table: no impressions, no reach, no watch time, no completion rate, no saves.
YouTubevideos.list?part=statistics, plus Analytics reports.queryviewCount, likeCount, commentCount to any API key (Google). The channel owner adds shares, estimatedMinutesWatched, averageViewDuration, averageViewPercentage, and dislikes (Google)dislikeCount is owner-only since 13 December 2021. favoriteCount is always 0. Studio's headline Impressions figure has no Analytics metric at all; adImpressions is an ads number renamed from impressions.
X / TwitterGET /2/tweets?tweet.fields=public_metricsimpression_count, like_count, reply_count, retweet_count, quote_count, bookmark_count, on an app-only Bearer token (X)non_public_metrics (url_link_clicks, user_profile_clicks, engagements) and organic_metrics need user-context OAuth on a post you own, and cover only posts created in the last 30 days. public_metrics is all or nothing; you cannot request a single subfield.
LinkedInGET /rest/organizationalEntityShareStatisticsimpressionCount, uniqueImpressionsCount, clickCount, likeCount, commentCount, shareCount, and engagement, which is a ratio rather than a count (LinkedIn)No saves, and no reaction breakdown here: typed reactions live on socialMetadata (LinkedIn). Time-bound statistics are unsupported for a single share, the window is the past 12 months, and posts with zero activity are dropped from elements entirely.
TelegramBot API, no metric method existsNothing you can pull. Reaction totals arrive only as message_reaction_count push updates, and only to a bot that is a chat administrator and names that update in allowed_updates (Telegram)View count, forward count, and any on-demand read. The Message object carries no counter field, and there is no getMessageViews method.
Google Business Profilelocations.getDailyMetricsTimeSeriesLocation-level daily metrics only: BUSINESS_IMPRESSIONS_MOBILE_SEARCH, BUSINESS_IMPRESSIONS_DESKTOP_MAPS, CALL_CLICKS, WEBSITE_CLICKS, BUSINESS_DIRECTION_REQUESTS and six more, each deduplicated per user per day (Google)Anything per post. localPosts.reportInsights was discontinued on 20 February 2023, and the replacement column of Google's own sunset table reads None (Google).

Meta is the row group worth rereading, because the three platforms one company owns now give three different answers about exposure. Instagram still returns reach at media level. Facebook cut every post-level unique variant in the v25 to v26 cycle. Threads never had reach or impressions and publishes views alone. One vendor, one quarter, three positions.

Meta's own two pages also disagree about when the Facebook cut binds every API version. The Pages guide puts a set of Page metrics out of service for all versions by 15 June 2026; the v25.0 changelog says the same deprecations apply to all versions when v26.0 is released, which happened six weeks later on 29 July 2026. Plan the migration against the earlier date. A metric that answers with an invalid-metric error is not a metric you schedule around.

The four fields we normalize, and the escape hatch for everything else

Folding nine metric vocabularies into one shape means picking the intersection and staying honest about the remainder. GET /v1/posts/{pid}/metrics fetches live from each platform and returns one entry per delivery target. Four fields are named: likes, comments, shares, saves. Whatever a platform reports beyond those lands in extra under that platform's own field name, so a TikTok target carries view_count and a LinkedIn target carries impressionCount without either one pretending to be the other.

bash
curl "https://api.social-api.ai/v1/posts/pst_01HZ9X3Q4R5M6N7P8V2K0W1J/metrics" \
  -H "Authorization: Bearer $SOCAPI_KEY"

# {
#   "data": {
#     "post_id": "pst_01HZ9X3Q4R5M6N7P8V2K0W1J",
#     "targets": [
#       {
#         "platform": "instagram",
#         "platform_post_id": "17895695668004550",
#         "media_type": "reel",
#         "status": "published",
#         "permalink": "https://www.instagram.com/reel/C8xQ2mLtP1k/",
#         "metrics_synced_at": "2026-08-03T11:42:07Z",
#         "metrics": {
#           "likes": 1284, "comments": 96, "shares": 41, "saves": 233,
#           "extra": {
#             "views": 58210,
#             "reach": 41387,
#             "ig_reels_avg_watch_time": 7420
#           }
#         }
#       }
#       // ... one entry per delivery target. The LinkedIn target's
#       // extra carries impressionCount and uniqueImpressionsCount
#       // instead: same envelope, different vocabulary.
#     ]
#   }
# }

metrics_synced_at on each target tells you how stale that number is. An error object on one target tells you which platform refused, without failing the whole call. X/Twitter and Trustpilot metrics are marked coming soon in our API reference, so check the target list before you build a chart that assumes every platform answered. Surfacing a metric you cannot map is worse than surfacing none, which is why extra stays unflattened on purpose.

Counting raw field names would reward whichever platform splits one number into six. Facebook documents post_reactions_like_total, _love_total, _wow_total, _haha_total, _sorry_total and _anger_total as separate metrics; TikTok documents like_count. Both answer the same question, which is how many people liked this. So the chart scores nine metric concepts instead, one point each.

Metric concepts answered per platform, out of nine
Only two report unique reachNobody answers all nineInstagram7Facebook7X / Twitter6LinkedIn6YouTube5Threads4TikTok4

Nine concepts scored a point each: views, impressions, unique reach, likes or reactions, comments or replies, shares or reposts, saves, link or profile clicks, and watch time. Scored for one organic post the connected account owns, against each platform's current API reference on 3 August 2026. Telegram and Google Business Profile score zero at post level and are not plotted. Instagram is scored on a feed post; a Reel trades profile clicks for watch time and lands on seven either way.

Instagram and Facebook tie at seven, and they get there through opposite holes: Instagram has no impressions number left and no watch time on a feed post, Facebook has no unique reach and no saves. The floor is Threads and TikTok at four, which is the universal intersection plus views. Nothing on the chart clears eight.

Pulling post history and back-filling an account

Back-filling is two different jobs that teams routinely merge into one ticket. Reading posts you published through us is a database read: GET /v1/posts filters on status, platform, account_ids, a from and to range and free-text search, and pages with pagination.next_cursor at up to 100 per request. Reading what existed before the account was connected is a platform read, and every platform prices that differently.

The distinction shows up at onboarding, always on the same day. A customer connects an Instagram account with four years of posts and expects four years of charts by morning. What you can actually deliver is set by the platform's history surface, not by how many workers you throw at it. These are the ceilings, each from the platform's own reference.

  • LinkedIn has the hardest ceiling. organizationalEntityShareStatistics returns share data only within the past 12 months, and it refuses time-bound statistics for a single share outright (LinkedIn), so an older post gives you lifetime totals or nothing.
  • X caps private history at 30 days: non_public_metrics, organic_metrics and promoted_metrics cover only posts created in the last 30 days (X). public_metrics has no such window, which is why an X back-fill returns impressions but no link clicks.
  • Threads floors user-level history at Unix 1712991600, which is 13 April 2024. since and until do not work below it, and Meta separately declines to guarantee anything before 1 June 2024 (Meta).
  • Instagram has a date seam rather than a window. impressions kept answering for media created on or before 1 July 2024 on v21.0 and older, and from 21 April 2025 the same request against media created on or after 2 July 2024 errors (Meta). A four-year-old account straddles that seam.
  • Facebook's Page /feed edge is capped around 600 ranked posts per year at 100 per request (Meta), so a decade-old Page does not hand back a decade.

Design the back-fill as a job with a per-platform budget rather than a loop that runs until it runs out. Cursor forward, checkpoint the cursor, stop when the platform stops, then tell the customer where the wall was. An onboarding screen that says Instagram history starts 2 July 2024 on this API version earns more trust than a spinner that quietly returns four months and calls it done.

javascript
// Walk everything published through SocialAPI for one account, then
// hydrate live metrics per post. Checkpoint the cursor: a back-fill that
// restarts from page one burns the platform's quota, not ours.
let cursor = await checkpoint.read("ig-backfill");

do {
  const page = await sapi(
    "/posts?account_ids=" + accountId +
    "&status=published&from=2026-01-01T00:00:00Z&limit=100" +
    (cursor ? "&cursor=" + cursor : "")
  );

  for (const post of page.data) {
    const { data } = await sapi("/posts/" + post.id + "/metrics");

    for (const t of data.targets) {
      if (t.error) { log.warn(t.platform, t.error.code); continue; }

      // Never merge extra into the top level. platform + field name is
      // the only safe key: TikTok view_count is not X impression_count.
      await store.upsert(post.id, t.platform, t.metrics, t.metrics_synced_at);
    }
  }

  cursor = page.pagination.next_cursor;
  await checkpoint.write("ig-backfill", cursor);
} while (cursor);

Enterprise social media platforms with an API and exportable reports

Buyers who search that phrase want two things at once, and the two pull against each other: programmatic access, plus a file somebody in marketing can open without a token. Live endpoints answer one post at a time in milliseconds. A report spans an account across months, takes real time to assemble, and cannot be a synchronous request without blowing through a gateway timeout.

So exports run as an async job, and the flow is three calls. POST /v1/accounts/{id}/export returns 202 with an id, account_id, platform, status, and created_at. GET /v1/exports/{id} reports the live status plus processed_videos out of total_videos. A completed job carries result.excel_url and result.report_url, and GET /v1/exports lists your 50 most recent jobs when you need to reconcile.

javascript
// 1. enqueue. Returns 202 immediately; nothing is computed yet.
const job = await sapi("/accounts/acc_01HZ9X3Q4R5M6N7P8V2K0W1J/export", {
  method: "POST",
  body: JSON.stringify({ include_transcript: true }),
});
// { "id": "exp_01J7KQ...", "platform": "tiktok", "status": "pending" }

// 2. poll with backoff. The job walks the account's videos, so the
//    runtime tracks total_videos, not a fixed SLA.
let state = job;
for (let wait = 2000; ; wait = Math.min(wait * 2, 30000)) {
  await sleep(wait);
  state = await sapi("/exports/" + job.id);
  if (state.status === "completed" || state.status === "failed") break;
  log.info(state.processed_videos + "/" + state.total_videos);
}

if (state.status === "failed") throw new Error(state.error);

// 3. fetch the bytes NOW: excel_url is presigned and dies in 15 minutes.
//    Storing that URL in a database row is a support ticket in waiting.
await pipeToStorage(state.result.excel_url);

// The same job, machine-readable: per-video views, likes, comments,
// shares, duration, plus transcript when include_transcript was set.
const rows = await sapi("/exports/" + job.id + "/videos");

Two details bite in production. The Excel link is presigned and valid for 15 minutes, so pull the bytes the moment you see completed instead of handing the URL to a queue that drains tomorrow. And exports meter per plan rather than per request: the free tier allows 2 exports a month, a 7-day cooldown on the same account, a 30-video cap, and exactly one job in flight. Design the retry around the cooldown, or your retry is the thing that trips it.

GET /v1/exports/{id}/videos returns the cached video array from a finished job with per-video views, likes, comments, shares and duration, plus the transcript and vision fields when include_transcript or include_vision were set. That endpoint is the machine-readable half of the work the spreadsheet does for a human. A weekly digest reads the JSON; the account manager opens the file. Same job, one run.

A report that takes ninety seconds to assemble cannot be a synchronous GET. Async is the only shape that survives a real account.

Where engagement numbers disagree between platforms

Two platforms hand you a number called views and mean different things by it. That is the reconciliation problem, and it has no technical fix. It has a documentation fix: write down what each number counts, once, and make every chart label point back at that definition.

YouTube is the sharpest case, because it disagrees with itself. The Data API publishes statistics.likeCount to anyone holding an API key, hides statistics.dislikeCount from everybody except the video owner (private since 13 December 2021), and then the Analytics API hands that same owner dislikes back on a different endpoint under a different name. Two APIs, one video, three access rules.

Exposure counts are worse. impression_count on X counts how often a post was seen. LinkedIn's impressionCount counts the same idea but ships beside uniqueImpressionsCount, which counts people. Google deduplicates every BUSINESS_IMPRESSIONS_* metric per user per day, so a customer who checks your hours three times on Tuesday is one impression. Averaging those into a single dashboard tile produces a number that means nothing.

The trap that costs a sprint is YouTube Studio. Its headline Impressions figure has no matching field on the Analytics API metric list. adImpressions exists, is an ads metric, and was renamed from impressions; cardImpressions counts card overlays. Neither is the thumbnail number a creator sees in Studio. Build the dashboard mock from the API reference, never from a screenshot of somebody's Studio tab.

So pick one canonical definition per concept, map each platform onto it in code, and refuse to render a cross-platform average for anything you had to bend to get there. Likes, comments and shares average cleanly across seven platforms. Views and impressions do not. If the caption under a chart cannot say which platform contributed what, the chart is decoration with axes.

Questions developers ask about social media analytics APIs

What is a social media analytics API?
A social media analytics API returns engagement data for social posts through one authenticated interface instead of one client per platform. It covers three jobs that get confused for each other: live metrics for a single published post, historical back-fill for an account you just connected, and account-level reporting exported as a file. SocialAPI.ai answers the first with GET /v1/posts/{pid}/metrics, the second with cursor-paginated GET /v1/posts, and the third with an async export job. The limits you inherit belong to the platform, not to the wrapper.
Which social media metrics can you actually get from an API?
Likes, comments and shares on seven of the nine platforms we connect. Views on five. Unique reach on two: Instagram's reach at media level and LinkedIn's uniqueImpressionsCount. Saves on two: Instagram's saved and X's bookmark_count. Watch time on three, measured three different ways. Telegram's Bot API returns no post counters at all, and Google Business Profile's per-post metrics were discontinued in February 2023 with no successor endpoint.
Does Facebook still return post reach through the API?
Not on the current version. Meta's Graph API metric reference marks post_impressions_unique and every other post-level unique variant deprecated above Graph API v25, and v26.0 released on 29 July 2026. The non-unique breakdowns survive: post_impressions_organic, post_impressions_paid, post_impressions_viral, post_impressions_nonviral and post_impressions_fan. Meta points Page-level callers at page_total_media_view_unique and page_media_view as replacements but publishes no equivalent mapping at post level, so treat the post-level successor as inferred rather than documented.
Can you get historical analytics for an account you just connected?
Partly, and the ceiling is per platform. LinkedIn returns organization share statistics only within the past 12 months. X restricts non_public_metrics and organic_metrics to posts created in the last 30 days, though public_metrics carries no such window. Threads user-level since and until do not work below Unix 1712991600, which is 13 April 2024. Facebook's Page /feed edge caps around 600 ranked posts per year at 100 per request. Budget the back-fill per platform and show the customer where their history actually begins.
How do analytics exports work, and how long do they take?
As an async job. POST /v1/accounts/{id}/export returns 202 immediately with an export id and status, a worker collects the account's posts and per-video metrics, and GET /v1/exports/{id} reports pending, processing, completed or failed along with processed_videos out of total_videos. Runtime tracks the video count rather than a fixed SLA. A finished export exposes result.excel_url, a presigned link valid for 15 minutes, and result.report_url for a shareable page. Free tier: 2 exports a month, a 7-day cooldown per account, 30 videos, one concurrent job.
Why do the same metrics differ between two platforms?
Because the words are shared and the definitions are not. X's impression_count counts how often a post was seen. LinkedIn ships impressionCount beside uniqueImpressionsCount, which counts people. Google deduplicates its BUSINESS_IMPRESSIONS_* metrics per user per day, so three visits on one Tuesday register as one impression. YouTube goes further and disagrees with itself: the Data API hides statistics.dislikeCount from everyone but the owner, then the Analytics API returns dislikes to that same owner. Pick one canonical definition per concept and decline to average across the ones you had to bend.
Is there a free tier for testing a social media analytics API?
SocialAPI.ai's free tier covers 2 brands, 10 posts a month and 50 interactions a month with no credit card, plus 2 analytics exports a month under a 7-day cooldown and a 30-video cap. It hits the same endpoints as the paid plans, so a proof of concept is not a separate code path. Paid plans start at $29/mo for 10 brands and lift the post and interaction caps entirely.

Metrics are one face of the same connected account. The identity data beside them (handle, avatar, follower count) is covered in the social media profile API guide, the inbound side lives in social listening API, and the cost of wiring all nine platforms yourself is priced out in social media API integration. Or start on the free tier and pull live metrics off a real post in about ten minutes.

Primary platform documentation cited above, all accessed 3 August 2026: Meta: Instagram media insights reference (feed, Reel and Story metric sets) · Meta: Instagram Platform changelog (impressions deprecation, v22.0) · Meta: Graph API insights metric reference (post-level enum and deprecation flags) · Meta: Graph API v25.0 changelog (unique post metrics deprecated) · Meta: Graph API v26.0 changelog (released 29 July 2026) · Meta: Pages API insights guide (15 June 2026 all-versions date) · Meta: Page /feed edge (~600 ranked posts per year, 100 per request) · Meta: Threads insights (media and user metrics, since/until floor) · TikTok: Video Object (the closed 15-field set) · TikTok: query videos endpoint (video.list scope) · Google: YouTube Data API videos resource (statistics, favoriteCount, dislikeCount) · Google: YouTube Analytics API metrics (watch time, shares, adImpressions) · Google: YouTube Analytics reports.query (owner scope, data availability) · X: post metrics (public, non-public, organic, and the 30-day window) · LinkedIn: organization share statistics (field set, 12-month window) · LinkedIn: social metadata API (typed reaction summaries) · Telegram: Bot API (Message fields, message_reaction_count update) · Google: Business Profile DailyMetric enum (location-level, per-user dedupe) · Google: Business Profile API sunset dates (localPosts.reportInsights, replacement None) · SocialAPI.ai: get post metrics reference · SocialAPI.ai: create analytics export reference

Get started today

Ready to unify your social interactions?

Free tier available · No credit card required · Ships with MCP server

We use essential cookies for security, and analytics cookies with your consent. Privacy Policy.