Analytics

Analytics

Retrieve TokPortal Analytics v2 data via API, including connected social analytics, public fallback metrics, post intelligence, comments, exports, and raw provider payloads.

Markdown

Analytics

TokPortal Analytics v2 exposes account, portfolio, post-level, time-series, comment, export, and raw provider analytics for delivered accounts.

Data is owner-scoped by API key and can come from multiple sources:

  • connected account analytics when the account is connected through TokPortal's supported social integrations
  • public fallback analytics when connected analytics are unavailable
  • TokPortal historical cache and post metadata

All examples use the public API base URL:

https://app.tokportal.com/api/ext

Access Levels

Analytics responses are plan-aware:

LevelAccess
StarterBasic totals and limited post/account visibility.
CreatorCore performance, time series, CSV export, and web reports.
Marketer/Farmer+Full analytics, raw provider payloads, demographics, comments, and advanced post intelligence where available.

Every Analytics v2 response includes contract metadata or the X-TokPortal-Analytics-Contract header.


Portfolio Dashboard

GET /analytics

Returns the Analytics v2 dashboard for the API key owner: totals, accounts, top posts, audience summary, facets, access metadata, and the current data contract.

Optional filters:

QueryDescription
workspaceWorkspace ID.
platformRepeatable platform filter, e.g. platform=tiktok&platform=instagram.
countryRepeatable country filter.
accountRepeatable saved account ID filter.
fromStart date or ISO timestamp.
toEnd date or ISO timestamp.
curl -X GET "https://app.tokportal.com/api/ext/analytics?platform=tiktok" \
  -H "X-API-Key: sk_xxx"

Response shape:

{
  "tier": "full",
  "access": {
    "level": "full",
    "planTier": "farmer",
    "reason": "full_access"
  },
  "totals": {
    "account_count": 99,
    "views": 648356,
    "impressions": 619676,
    "tracked_posts": 4876,
    "post_views": 2181245,
    "avg_fyp_pct": null
  },
  "accounts": [],
  "top_posts": [],
  "audience": {
    "age": [],
    "gender": [],
    "country": [],
    "city": [],
    "unit": "unknown"
  },
  "contract": {
    "name": "tokportal.analytics.v2",
    "version": "2026-09-22.1",
    "accessLevel": "full"
  }
}

Account Drilldown

GET /analytics/accounts/:id

Returns one account's Analytics v2 drilldown: account header, latest account snapshot, posts, daily series, demographics, derived stats, intelligence, and contract metadata.

curl -X GET https://app.tokportal.com/api/ext/analytics/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c \
  -H "X-API-Key: sk_xxx"

Important fields:

FieldDescription
latestLatest account-level metrics.
postsTracked post list with latest post snapshot metrics.
daily_seriesStored daily points for charts.
demographicsParsed account or post audience demographics when available.
derived_statsDerived lifetime stats from tracked posts.
intelligenceCached content/account intelligence when available.

Time Series

GET /analytics/series

Returns stored chart points. This endpoint does not live-refresh data.

Query parameters:

QueryDescription
metricviews, likes, comments, shares, or followers. Default: views.
granularityday or week. Default: day.
modecumulative, gained, or snapshot. Default: cumulative.
accountRepeatable saved account ID.
fromStart date or ISO timestamp.
toEnd date or ISO timestamp.
curl -X GET "https://app.tokportal.com/api/ext/analytics/series?metric=views&granularity=day&mode=cumulative&account=9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c&from=2026-05-01&to=2026-05-10" \
  -H "X-API-Key: sk_xxx"

Account Compatibility Endpoint

GET /accounts/:id/analytics

This older public path now uses Analytics v2. It keeps legacy-shaped fields for existing integrations and also exposes the full v2 payload under data.analytics_v2.

curl -X GET https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/analytics \
  -H "X-API-Key: sk_xxx"

Response:

{
  "data": {
    "account_id": "9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c",
    "followers_count": 12400,
    "total_videos_tracked": 48,
    "total_views": 580000,
    "total_likes": 42000,
    "average_engagement_rate": 7.24,
    "analytics_version": "v2",
    "analytics_v2": {
      "account": {},
      "latest": {},
      "posts": []
    }
  }
}

Post Analytics by Account

GET /accounts/:id/analytics/videos

Returns Analytics v2 tracked posts in the older paginated list shape.

Query parameters:

ParameterTypeDefaultDescription
sort_bystringupload_dateviews, likes, engagement_rate, or upload_date.
sort_orderstringdescasc or desc.
pageinteger1Page number.
per_pageinteger50Results per page, max 100.
curl -X GET "https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/analytics/videos?sort_by=views&sort_order=desc" \
  -H "X-API-Key: sk_xxx"

Rows include tracked_post_id, analytics_version: "v2", and the full v2 row under analytics_v2.


Single Post Analytics

GET /videos/:id/analytics

Pass an Analytics v2 tracked_post_id when available. For older integrations, this endpoint can still fall back to a legacy video_analytics.id.

curl -X GET https://app.tokportal.com/api/ext/videos/8c2d0f2e-.../analytics \
  -H "X-API-Key: sk_xxx"

Response fields include normalized post metrics, analytics_version, and for v2 records an analytics_v2 object with the tracked post and latest snapshot, plus platform_signals and signal_insights (see Platform Signals).


Platform Signals

Every post row returned by the account drilldown, the dashboard top posts, GET /videos/:id/analytics and the videos endpoints carries a signals / platform_signals object describing what the platform did with the live post. Fields are null until the post has been checked (about 48 hours after posting, again at 7 days, and on every later scrape) — null means not checked yet, never no. Signals are health information and are visible on every tier.

FieldMeaning
not_recommendedTikTok Not For Feed / Not Recommended: the post stays online but is excluded from the For You feed and typically sits at 0 views. On a large sample this flag explains 86 % of posts stuck at 0 views.
ai_labelcreator (declared at posting) or platform (applied by TikTok moderation). Visible to viewers; can reduce distribution.
soundok, non_commercial (cannot be promoted), removed (pulled for copyright, plays muted), muted (Instagram).

Why TikTok does it, in its own words: content is "ineligible for the FYF if it includes unoriginal or reused material without anything new" (For You feed eligibility standards), and "if you repeatedly post content that is unsuitable for the For You feed, your account and posts won't appear in the For You feed" (Why is my account not being recommended?). AI labels follow TikTok's AI-generated content rules; sound restrictions follow the commercial use of music rules. A flag is a content decision of the platform: it is not a defect of the account, and it is not a ground for replacement or refund under TokPortal's Ban & Replacement Policy.

From the Creator tier, post rows also carry connected-API depth (TikTok accounts connected through TokPortal):

FieldMeaning
new_followersFollowers gained from this post.
audience_types{ new_viewer, returning_viewer, follower, non_follower }, shares 0..1.
impression_sourcesFull breakdown: for_you, follow, search, personal_profile, sound, direct_message, others.
retention_curve[{ second, percentage }] — share of viewers still watching at each second.
engagement_curve[{ second, percentage }] — when likes were given.

The account drilldown adds account_signals: private / business / "joined recently" flags, the share of the last 5 checked posts not recommended (recent_not_recommended / recent_posts_checked), sound issues and AI-labelled counts, and on Instagram the 30-day reach split reach_followers_30d / reach_non_followers_30d. Instagram daily_series points now carry reach from the official Meta reach/day insight.

Flagged posts

GET /analytics/signals

Owner-scoped list of every post carrying a signal, newest first.

QueryDescription
signalnot_recommended, ai_label or sound_status. Omit for any signal.
accountRepeatable saved account ID filter.
from, toOnly signals updated inside the window (ISO timestamps).
limit, offsetPagination, limit 1..200 (default 50).
curl -X GET "https://app.tokportal.com/api/ext/analytics/signals?signal=not_recommended&limit=20" \
  -H "X-API-Key: sk_xxx"
{
  "data": [
    {
      "tracked_post_id": "31bbd0c0-4fd1-446f-9f19-d453536c9694",
      "video_listing_id": "2dd8a805-3a24-4e84-bbba-4b31f805b34d",
      "saved_account_id": "f92b2e59-7ec2-45d0-9a88-a8abaa6a8851",
      "username": "lena.huber_",
      "platform": "tiktok",
      "country": "DE",
      "platform_post_id": "7653808499427790102",
      "permalink": "https://www.tiktok.com/@lena.huber_/video/7653808499427790102",
      "published_at": "2026-06-21T00:00:00Z",
      "platform_signals": {
        "checked_at": "2026-09-19T05:28:00Z",
        "not_recommended": true,
        "not_recommended_since": "2026-07-06T15:27:30Z",
        "ai_label": null,
        "sound": "non_commercial",
        "sound_reason": "Can't promote due to audio copyright issue"
      },
      "signal_insights": null,
      "updated_at": "2026-09-22T05:47:52Z"
    }
  ],
  "pagination": { "limit": 20, "offset": 0, "total": 9 },
  "contract": { "name": "tokportal.analytics.v2", "version": "2026-09-22.1", "accessLevel": "starter" }
}

Webhooks push the same observations as they happen: video.not_recommended, video.ai_labeled, video.sound_restricted, account.distribution_warning. Clients also get a grouped in-app notification per order per day and an optional daily e-mail digest (Settings → Notifications).


Comment Pulse

GET /analytics/comments

Returns a dashboard-level comment pulse across top posts in the selected scope. Full-tier only. The endpoint serves cached comments and cached semantic analysis, hydrating comments only when the cache is empty or stale.

Query parameters:

ParameterDescription
limitTotal comments to return, 1..40. Default: 24.
postLimitNumber of top posts to sample, 1..10. Default: 6.
commentsPerPostComments to hydrate per post when needed, 1..12. Default: 8.
postRepeatable tracked post ID to force a specific sample.
workspaceWorkspace ID.
accountRepeatable saved account ID.
platformRepeatable platform filter.
countryRepeatable country filter.
fromStart date or ISO timestamp.
toEnd date or ISO timestamp.
curl -X GET "https://app.tokportal.com/api/ext/analytics/comments?platform=tiktok&limit=24" \
  -H "X-API-Key: sk_xxx"

The response echoes the scope it actually sampled under applied_filters. When you pass post, it forces that specific sample and overrides the dashboard filters (workspace, account, platform, country, from, to) — the response states when that happened, so read applied_filters instead of assuming your filter set was honoured.


Post Comments

GET /analytics/accounts/:id/comments

Returns cached comments for one tracked post. Full-tier only.

Query parameters:

ParameterDescription
trackedPostIdAnalytics v2 tracked post ID.
postIdConnected provider post ID fallback.
limitNumber of comments, max 50.
curl -X GET "https://app.tokportal.com/api/ext/analytics/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/comments?trackedPostId=8c2d0f2e-..." \
  -H "X-API-Key: sk_xxx"

CSV Export

GET /analytics/export/videos

Returns text/csv for all videos in scope. The exported columns are plan-aware.

Optional filters:

QueryDescription
workspaceWorkspace ID.
accountRepeatable saved account ID.
platformRepeatable platform filter.
countryRepeatable country filter.
qSearch text over account, post text, URLs, and platform IDs.
fromStart date or ISO timestamp.
toEnd date or ISO timestamp.
curl -X GET "https://app.tokportal.com/api/ext/analytics/export/videos?account=9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c" \
  -H "X-API-Key: sk_xxx"

HTML Reports

POST /analytics/export/reports
POST /analytics/export/reports/html

POST /analytics/export/reports creates a shareable Analytics v2 web report; the response includes a report token and public url. POST /analytics/export/reports/html takes the same body and returns a standalone downloadable HTML document. Creator and full-tier plans can generate reports. Custom branding is available for full/farmer plans.

Do not send Idempotency-Key to analytics report creation or HTML report export. These responses can contain a private report token or artifact, so TokPortal rejects the header before execution with 400 IDEMPOTENCY_KEY_NOT_ALLOWED_FOR_SENSITIVE_RESPONSE. If the request outcome is uncertain, list or reconcile the workspace's reports before creating another one. Never include report tokens in support logs.

curl -X POST https://app.tokportal.com/api/ext/analytics/export/reports \
  -H "X-API-Key: sk_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "April performance report",
    "template": "executive",
    "accountIds": ["9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c"],
    "from": "2026-04-01",
    "to": "2026-04-30"
  }'

Request Body

FieldTypeDescription
titlestring | nullReport title. Max 120 characters.
templatestring | nullLayout: executive, agency, creator, minimal, growth, or board.
brandNamestring | nullWhite-label brand name. Max 80 characters.
brandAccentstring | nullWhite-label accent colour, #rrggbb.
accountIdsstring[]Saved account IDs to include.
workspaceIdstring | nullWorkspace ID.
platformsstring[]Repeatable platform filter: tiktok, instagram.
countriesstring[]Country codes.
querystring | nullFree-text filter over accounts, post text, URLs, and platform IDs. Max 120 characters.
fromstring | nullInclusive start date, YYYY-MM-DD.
tostring | nullInclusive end date, YYYY-MM-DD.

Validation

Both routes validate this body rather than coercing it.

  • template — any value outside the six listed above is rejected with INVALID_FIELD. It is not replaced by executive.
  • from / to — must be YYYY-MM-DD. A full ISO timestamp is accepted and truncated to its date; anything else is rejected with INVALID_FIELD rather than being treated as no filter at all, which used to widen the report to the account's entire history. from must not be after to.
  • brandAccent — #rrggbb, or a #rgb short form which is expanded to six digits. Named colours (red), rgb() notation, and 8-digit hex are rejected with INVALID_FIELD.
  • Lengths — title 120, brandName 80, query 120 characters.

Reading back what was applied

The settings that actually took effect are echoed, so you never have to inspect the rendered report to find out what the server used.

On POST /analytics/export/reports they come back on the JSON response under applied:

FieldDescription
applied.templateThe layout actually used.
applied.brand_nameThe brand name actually rendered.
applied.brand_accentThe accent colour actually used.
applied.filters.fromThe start date actually applied.
applied.filters.toThe end date actually applied.

On POST /analytics/export/reports/html the same information comes back as X-TokPortal-Report-* response headers (for example X-TokPortal-Report-Template).

INFO: White-labelling is plan-gated brandName and brandAccent are only applied on the full analytics tier with a Farmer plan. On every other plan the brand is reset to TokPortal, and applied.brand_name together with canBrand say so — read them before promising a client a white-labelled document.


Raw Provider Payloads

Full-tier clients can retrieve stored raw provider payloads. These endpoints are useful when you need fields that TokPortal has stored but has not yet normalized into product UI fields.

Account Raw Snapshots

GET /analytics/accounts/:id/raw

Returns rows from account_analytics_snapshots, including normalized metrics and the stored raw JSON payload.

curl -X GET "https://app.tokportal.com/api/ext/analytics/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/raw?source=bundle_social&limit=3" \
  -H "X-API-Key: sk_xxx"

Post Raw Snapshots

GET /analytics/posts/:tracked_post_id/raw

Returns rows from post_analytics_snapshots, including normalized metrics, raw per-post analytics, and tracked post metadata.

curl -X GET "https://app.tokportal.com/api/ext/analytics/posts/8c2d0f2e-.../raw?source=bundle_social&limit=3" \
  -H "X-API-Key: sk_xxx"

Raw endpoint filters:

QueryDescription
sourcebundle_social, apify, or manual.
limit1..30, default 5.
fromStart date or ISO timestamp.
toEnd date or ISO timestamp.

Refresh Analytics

POST /accounts/:id/analytics/refresh

Triggers a targeted Analytics v2 refresh for one account. Background sync is the primary refresh path; use this endpoint for targeted repair or bootstrap flows.

Body:

{
  "includePosts": true,
  "includeComments": true,
  "postLimit": 20,
  "force": true,
  "forcePosts": false,
  "bootstrapPosts": false
}

WARNING: includeComments requires force Comment hydration only takes effect when force is true, includePosts is not false, and the workspace holds the full analytics tier. Sending includeComments: true without force is refused for strict callers (MCP by default) and reported back in applied for the rest — it is no longer dropped without a word.

The server dedupes repeated requests:

SourceDedupe window
Connected analytics6 hours
Public fallback analytics24 hours

Response:

{
  "data": {
    "account_id": "9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c",
    "platform": "tiktok",
    "status": "refreshed",
    "analytics_version": "v2"
  }
}

Check Refresh Availability

GET /accounts/:id/analytics/can-refresh

Returns whether targeted refresh is currently available for an account under the Analytics v2 dedupe policy.

curl -X GET https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/analytics/can-refresh \
  -H "X-API-Key: sk_xxx"

Response:

{
  "data": {
    "account_id": "9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c",
    "can_refresh": true,
    "next_refresh_at": null,
    "hours_remaining": 0,
    "source": "connected",
    "analytics_version": "v2"
  }
}

Contract

GET /analytics/contract

Returns the global Analytics v2 contract, the current API key's access payload, metric semantics, endpoint metadata, freshness targets, and redaction rules.

Connected Instagram and TikTok insights

GET /analytics/accounts/{id}/insights returns the same connected_analytics block as the account drilldown. The MCP tool is getAnalyticsAccountInsights (remote MCP at https://app.tokportal.com/api/ext/mcp). All reads are scoped to the API key owner and read stored observations; opening a dashboard or calling this endpoint never starts a provider refresh.

Requires Creator or higher. Starter receives 403 plan_required from the insights endpoint, and the block is omitted from the normal drilldown. Existing quotas and refresh policies are unchanged.

curl -H "X-API-Key: $API_KEY" \
  "https://api.tokportal.com/analytics/accounts/$ACCOUNT_ID/insights"

When no recently verified active Zernio connection exists, the endpoint returns:

{"account_id":"saved-account-uuid","available":false,"connected_analytics":null}

The normal drilldown omits connected_analytics in this case. The dashboard also hides the new block, including after a recorded disconnection. A pinned provider ID alone, or an old analytics snapshot, is not proof of an active connection. Proof older than 24 hours is treated as unavailable until a server check succeeds. A delayed disconnection webhook with an older provider timestamp does not override a newer verified check. Legacy analytics and historical snapshots remain available through their existing contracts.

When available, connected_analytics contains:

FieldMeaning
platforminstagram or tiktok.
connection_checked_atLatest accepted server connection proof.
captured_atStored account observation time, not a claim of instant platform freshness.
account.period.from, account.period.toExact account insight window.
account.metricsAvailable, tier-aware account counts. Instagram reach, views, engaged accounts and interactions cover the period. TikTok follower/following/video/like counters are lifetime counters; follower gains/losses cover the window.
account.daily_followersDaily follower counts, when supplied.
account.daily_reachDaily Instagram reach, when supplied. Never sum daily unique reach into period unique reach.
account.demographicsFull-tier audience counts by age, gender, country and city; null until eligible/available. Instagram normally requires at least 100 followers.
posts[].tracked_post_idTokPortal tracked-post ID, matching the normal drilldown.
posts[].captured_atActual metric observation timestamp.
posts[].metricsOptional lifetime per-post metrics: saves, followers gained, reach, profile/website/link clicks, average/total watch seconds, completion, replays or reposts.
posts[].traffic_sourcesAvailable traffic-source shares as percentages from 0 to 100.
posts[].audience_typesAvailable new/returning and follower/non-follower shares as percentages from 0 to 100. These are two independent distributions.

completion_rate is a fraction from 0 to 1 (0.42 means 42%). average_watch_seconds and total_watch_seconds use seconds. Unknown or unsupported metrics are omitted, never manufactured as zero. Real observed zeros remain valid counts. Generic zero-filled TikTok depth responses are withheld until there is evidence that detailed insights are available.

Creator includes core account growth, daily histories, post saves/followers gained and traffic/audience shares. Marketer/Farmer/full adds account reach and engagement, watch time, completion, profile/website/link clicks and demographics. Lower-tier responses do not carry hidden full-tier values.

Instagram account views include feed, Reels, Stories and other surfaces and deliberately differ from post-level totals. TikTok detailed post metrics depend on the connection lane and platform availability, often arrive 24–48 hours after publication, and may be limited to recently active posts. This endpoint does not promise account-level TikTok impressions/reach or a second-by-second retention curve.

New correctly connected accounts enter the existing background sync automatically. Account data and optional Instagram follower/reach history and demographics refresh through the daily account lane; optional audience failures do not discard the core observation. Analytics webhooks wake ingestion of already-available changes, not a real-time platform update. Use captured_at, the explicit period and GET /analytics/contract when interpreting results.