Social Media Analytics API: Post & Account Metrics
Read the performance of everything you publish: per post, per platform, per account, and as a workspace roll-up. The API also computes recommended posting times from your own history.
All analytics endpoints require the analytics:read scope on your API key. Every endpoint reads stored snapshots from the OmniSocials database. No endpoint calls the social platforms live, so responses are fast and never consume platform rate limits.
| Endpoint | What it returns |
|---|---|
GET /analytics/posts/:id | Latest per-platform metrics for one post |
GET /analytics/posts?ids= | Bulk version: up to 100 posts in one call |
GET /analytics/overview | Workspace totals, engagement rate, platform breakdown |
GET /analytics/accounts | Account-level snapshots (followers, profile views, and more) |
GET /analytics/best-times | Recommended posting slots per platform |
How metrics are collected
OmniSocials collects metrics on a schedule, starting right after a post publishes:
| Post age | Refresh interval |
|---|---|
| 0-7 days | Every 2 hours |
| 8-30 days | Every 24 hours |
| 31-365 days | Every 7 days |
| Stories | Every hour for the story's 24-hour lifetime |
Two consequences:
- A post published minutes ago can legitimately report zeros. The platforms themselves need time to count views, so check back after the first refresh window.
- Metrics for a 6-month-old post can be up to a week old.
collected_aton every response tells you exactly when the numbers were captured.
Account-level snapshots (followers, profile views) are collected once per day.
Get analytics for one post
Use the numeric OmniSocials post id (the id field from GET /posts), not a platform post id such as a tweet id or YouTube video id. A platform id returns 400 invalid_post_id.
Code
Response:
Code
One entry per platform the post went to. The metric fields inside metrics are platform-specific, stored as the platform reports them. A missing key means the platform does not report it; a 0 is a measured zero. A post with no collected analytics yet returns an empty platforms object, not a 404.
Metric keys by platform
| Platform | Keys you can expect |
|---|---|
views, reach, likes, comments, saves, shares, reposts, engagement, profile_visits, follows, profile_activity + profile_activity_breakdown. Reels add avg_watch_time, total_watch_time, watch_time_percentage, skip_rate, duration. Stories add replies, navigation + navigation_breakdown, link_clicks, completion_analysis. | |
views, reach, likes, total_reactions, reactions (by type, including care), comments, shares, clicks, clicks_breakdown, video_views, avg_watch_time, total_watch_time. Reels add replays, follows. | |
| LinkedIn profile | impressions, reach, likes, reactions (by type), comments, top_level_comments, shares, saves, sends, clicks, follows, profile_visits. |
| LinkedIn page | impressions, unique_impressions, likes, reactions, comments, shares, clicks, engagement_rate, plus video keys for video posts. |
| YouTube | views, likes, dislikes, comments, duration. With the Analytics API enabled: shares, engaged_views, estimated_minutes_watched, average_view_duration, average_view_percentage, subscribers_gained, subscribers_lost, playlist_adds, traffic_sources. |
| TikTok | views, likes, comments, shares, duration, share_url. With the Business API authorization: average_time_watched, full_video_watched_rate, completion_rate, watch_time_percentage, total_time_watched, favorites, reach, follows, profile_visits, impression_sources, audience_types, audience_countries (see TikTok watch-depth analytics). |
| X | views, likes, comments, reposts, quotes, bookmarks. For posts under 30 days old: link_clicks, profile_visits, engagement. Video posts add video_views, completion_rate, duration. |
impressions, saves, pin_clicks, outbound_clicks, likes, comments, profile_visits, follows, save_rate, pin_click_rate, outbound_click_rate, engagement. Video pins add video_views, video_starts, completion_rate, avg_watch_time, total_watch_time. | |
| Threads | views, likes, replies, reposts, quotes, shares, engagement. |
| Bluesky | likes, comments, reposts, quotes, bookmarks. Bluesky exposes no views. |
| Mastodon | likes, comments, reposts, quotes, poll (votes per option), tags. Mastodon exposes no views. |
| Google Business | No per-post metrics (Google discontinued them in 2023). The entry carries state, search_url, metrics_unavailable: true and a note. |
Durations are in seconds, rates in percent (0 to 100). Breakdown objects (reactions, traffic_sources, impression_sources, ...) map a category to a count or a percentage.
Threads count as one post
On X, Bluesky, Mastodon, and Threads a post can publish as a thread of several parts. The API sums each part's metrics into the platform entry, so you get the thread's true total (the same number the OmniSocials dashboard shows). thread_parts tells you how many parts were summed, 1 for a normal post, and platform_post_id is always the thread's root part.
Multi-slide stories work the same way: each slide is its own story on Instagram or Facebook, so the platform entry sums the slides and thread_parts is the number of slides with stats. The entry also carries a story_slides array with each slide's own metrics in publish order:
Code
story_slides is absent on single-slide stories and on every other post type.
Bulk: up to 100 posts per call
One request to GET /analytics/posts replaces up to 100 single-post calls. It exists so a nightly sync doesn't burn the 100 requests/minute rate limit.
Code
The response data array returns one entry per requested id, in request order. Ids with no analytics yet come back with an empty platforms object rather than being omitted, so you can map results 1:1 to what you sent. More than 100 ids returns 400 too_many_ids; page through larger sets in batches.
Workspace overview
Roll-up across every platform for a time window:
Code
Code
Pick the window one of two ways:
period:7d,30d(default), or90d.start_date+end_date: acceptsYYYY-MM-DD, aYYYY-MMmonth shorthand (2026-04expands to the whole month), or a full ISO 8601 datetime.
The response echoes the resolved start_date, end_date, and the server's current_date, so a client (or an AI agent) can verify what was actually queried instead of guessing.
Account analytics
Daily snapshots of account-level metrics for every connected account. Each snapshot holds stock values (followers, following, posts, and platform extras such as Pinterest monthly_views, YouTube total_views, X listed, Google Business average_rating and review_count) and day values for the snapshot date (impressions, reach, engagement, likes, comments, shares, profile_views, link_clicks, follows_gained, follows_lost, and platform extras such as Google Business calls, direction_requests, website_clicks). Rows with day values carry period: "daily".
Audience data, when the platform provides it: demographics (age, gender, country, city on Instagram, Threads and TikTok, which need 100 or more followers; function, seniority, industry and company size on LinkedIn pages, no minimum) with demographics_unit, and online_followers (UTC hour to followers online) for Instagram and TikTok Business accounts.
Code
date (YYYY-MM-DD) returns the latest snapshot on or before that date; it defaults to today. To measure growth over a window, diff two snapshots. For example, compare date=2026-08-01 against date=2026-07-01.
Metric semantics vary by platform
The scope of an account metric is platform-defined, and it differs. Where the semantics are non-obvious, the metrics object carries a note field explaining them. The important ones:
- LinkedIn profile impressions. Profiles keep a lifetime cumulative total under
impressions_lifetime(all of the account's content, including posts published outside OmniSocials). When LinkedIn serves a daily breakdown,impressionsis that day's value and the row carriesperiod: "daily"; otherwise it carriesperiod: "lifetime". Pages always report day values. - Instagram and Threads day values come from the platform insights for that date. A missing field means "not reported by the platform for this account", not zero. Accounts under 100 followers get no demographics or audience-online data.
- YouTube and TikTok day values need the YouTube Analytics API scope, or the TikTok Business authorization. Without them the snapshot holds the stock values only.
- Google Business Profile has no follower concept, and Google deprecated per-post insights. Account rows hold a 30-day
dailyseries (impressions split by surface inimpressions_breakdown, calls, direction requests, website clicks, bookings),average_rating,review_count, and monthlysearch_keywords. Google's data lags 2 to 3 days;data_datesays which day the top-level values describe.
Best posting times
Recommended day/hour slots for a platform, computed from your workspace's own posting history: publish time crossed with engagement for every analyzed post, recency-weighted and outlier-damped.
Code
The response contains a grid (one scored cell per day/hour that has data, scores relative 0-100 within the platform) and recommendations: the top 3 slots, never two on the same day within 3 hours of each other.
Check the basis field before trusting the numbers:
own_data: computed from your workspace's posts.own_data_and_audience: your posts blended with when your followers are online (Instagram, TikTok Business). The response also carriesaudience_online(hour to followers online, in the response timezone).audience: fewer than 15 analyzed posts, so the recommendation comes from the audience-online profile only.defaults: a static industry-average table, returned when the workspace has fewer than 15 analyzed posts on the platform and no audience data.posts_neededsays how many more published posts unlock personalized recommendations.
Times are expressed in the timezone you pass (any IANA identifier). It defaults to your account's timezone, then UTC.
How engagement is calculated
Every OmniSocials surface (dashboard, API, CLI, MCP server) normalizes metrics the same way:
- Engagement is the sum of the platform's real interaction fields. Where a platform provides a precomputed total (Instagram's
total_interactions, for example) we use it; otherwise we sum likes, comments, shares, reposts, quotes, and the platform's other interaction counts. For LinkedIn this includes link clicks, matching LinkedIn's own definition. For X it includes quotes and bookmarks. - Impressions is the best available "how many times was this seen" number: total views where the platform provides them, falling back to impressions, then reach.
- Engagement rate is engagement divided by impressions, as a percentage capped at 100. Below 10 impressions the rate is reported as 0, because a tiny denominator produces a meaningless percentage, not a signal.
Troubleshooting
| Symptom | Cause |
|---|---|
400 invalid_post_id | You passed a platform post id (tweet id, YouTube video id). Use the numeric id from GET /posts. |
Empty platforms object | No analytics collected yet. The post is very fresh, still scheduled, or failed to publish. |
| Zeros right after publishing | Normal. Platforms need time to count; the first refresh lands within 2 hours. |
Google Business entry has metrics_unavailable: true | Google deprecated per-post insights; see the note in the response. Account-level metrics still work. |
| A metric key is missing for one platform | The platform does not report it (for example views on Bluesky and Mastodon), or the account lacks the permission (YouTube Analytics scope, TikTok Business authorization). |
LinkedIn impressions_lifetime looks far too big | It is a lifetime total, not a window. Use impressions with period: "daily", or diff two snapshots. |
Other ways to read analytics
- CLI:
omnisocials analytics:overview,analytics:post,analytics:best-timesreturn the same data in your terminal, with--jsonfor pipelines. - MCP server: the
get_post_analytics,get_posts_analytics,get_analytics_overview,get_account_analytics, andget_best_timestools let Claude, ChatGPT, and other AI agents answer "how did last week perform?" directly. - Webhooks: subscribe to post events instead of polling. See Webhooks.