Skip to content

API referenceVideos

Get a video

GET/v1/videos/{platform}/{id}

TikTok, Instagram, YouTube and Facebook. An Instagram id is the post's shortcode (/p/{id}, /reel/{id}); its numeric media id, as search returns it, reads the same post. Instagram Reels, photos and carousels are all videos; Instagram reports no shares or saves, so those read 0. A YouTube id is the 11-character video id; a YouTube video carries its title then its description in description, no sound, and shares/saves of 0 since YouTube publishes neither. A Facebook id is the numeric video or reel id; its shares may be rounded above 1,000, as Facebook displays it (1.4K reads 1400). Metadata, stats, the latest Snapshot and Scores where the creator has a baseline. Scores are null rather than fetched when no baseline exists; POST /videos/{platform}/{id}/score forces that fetch. Any include turns the call into one video analysis Run (kind video_intelligence) charged a single price in place of the lookup price, never on top of it: include=intelligence (alone or with transcript) is the analysis price, include=transcript alone is the flat transcript price. include=download adds download_url, a link to the video file served from our own short-lived copy, valid until download_expires_at (7 days after the copy was made, at least a day away); ask again for a fresh link. Anyone holding the link can download the file. Alone it is the download price; with intelligence or transcript it is added to theirs, and it costs the same whether or not a copy already exists. include works on every platform, YouTube long-form included. A post that is gone, private or restricted fails the analysis Run with media_unavailable, uncharged. POST /videos/{platform}/{id}/score is TikTok only: on other platforms it is refused with 400 unsupported_platform before any Run. A video longer than 10 minutes is refused for analysis and download with 422 video_too_long: before any Run when its length is known (a YouTube video's length is asked of YouTube first, free), else the Run fails uncharged. With download beside intelligence or transcript, a file that cannot be copied does not fail the analysis: the result carries download_error and the download is not charged. The link is frozen into the Run, so reading the Run after its download_expires_at returns a dead link; look the video up again for a fresh one. A download-only lookup is a video_intelligence Run too.

Open in Playground(no API Key needed)

Price

3 credits. With include=intelligence: 23 credits instead of the lookup price. With include=transcript only: a flat 10 credits. With include=download: 3 credits alone, else 3 credits on top of the analysis or transcript price. A stored answer (or an existing copy) costs the same as a fresh one.

Supports estimate=true: the reply is the price in Credits and whether it would be cached, with no Run created and nothing charged.

A cached result costs the same as a fresh one; cached says which you got.

Parameters

NameInTypeRequiredDefaultDescription
platformpathstringone of tiktok, instagram, youtube, facebookRequired—Platform the subject lives on.
idpathstringRequired—Platform video id.
includequerystring[]one of intelligence, transcript, downloadOptional—intelligence adds the full analysis object (a TikTok photo carousel is analysed from its images, slide by slide); transcript adds the transcript; download adds download_url and download_expires_at (refused for a photo carousel: media_unavailable, reason not_a_video). Repeat the param or comma-separate (include=intelligence,download). Any value changes the price; see above.
refreshquerybooleanOptional—Only with include. true recomputes the analysis even when a current one is stored, and is charged as fresh. Ignored without include.
waitqueryintegermin 0; max 120Optional30Seconds to hold the response open before handing back a running Run to poll. Default 30, max 120. A POST may send it in the body instead; given in both places with different values it is a 400.
estimatequerybooleanOptional—true returns { estimate_credits, cached } computed from what is already stored, and creates no Run. Nothing is charged. When a source the call would read is unavailable and the answer is not served from cache, the estimate adds unavailable, the { platform, capability } parts it cannot reach, or answers 503 source_unavailable when it can reach none. Only true or false; anything else is a 400. A POST may send it in the body instead; given in both places with different values it is a 400.
Idempotency-KeyheaderstringOptional—1-255 printable ASCII characters. Replaying this key with the same payload returns the original Run (202 while it is still running); a different payload, or a different operation, is a 409 idempotency_conflict.

Responses

200 OK

The Run finished inside the wait budget: { run, result }. With estimate=true, { estimate_credits, cached } instead.

any of 2 shapes

  • Option 1object
    • runobjectrequiredEvery charged operation creates a Run. The envelope is identical whether the result came back inline or was polled.
      • idstringrequired
      • kindstringrequiredWhat was run, e.g. video_lookup.
      • statusstringrequiredone of running, completed, partial, failedpartial keeps the completed items and charges for them, up to what the Balance covers; failed charges nothing.
      • created_atstringrequiredRFC 3339.
      • completed_atstringrequirednullableNull while the Run is still running.
      • charge_creditsintegerrequiredWhat this Run actually cost, in Credits. A stored answer costs the same as a fresh one.
      • list_price_creditsintegerrequiredThe full price of the work asked for, in Credits. Equal to charge_credits unless the Run charged for only part of it.
      • cachedbooleanrequiredTrue when a stored result answered without paying the vendor.
      • progressobjectWhat a running Research has done so far, refreshed as it works: stage (keywords, searching, filtering, analysing, writing_digest) and the counts known by then — keywords, found, kept, dropped_off_topic, and analysed of shortlisted. A count is absent until its stage produced it. Absent once the Run settles, and on every other kind.
        • stagestringrequiredone of keywords, searching, filtering, analysing, writing_digest
        • keywordsintegervalue at least 0
        • searchesintegervalue at least 0
        • foundintegervalue at least 0
        • judgedintegervalue at least 0
        • keptintegervalue at least 0
        • dropped_off_topicintegervalue at least 0
        • dropped_unsureintegervalue at least 0
        • shortlistedintegervalue at least 0
        • analysedintegervalue at least 0
        • updated_atstringrequired
    • resultobjectrequiredOne normalised video, the same shape on every platform.
      • platformstringrequired
      • idstringrequired
      • urlstringrequirednullable
      • descriptionstringrequired
      • posted_atstringrequirednullable
      • duration_secondsnumberrequirednullable
      • thumbnail_urlstringrequirednullable
      • hashtagsarray of stringrequired
      • soundobjectrequirednullable
        • idstringrequired
        • titlestringrequirednullable
      • creatorobjectrequirednullable
        • idstringrequired
        • handlestringrequirednullable
        • namestringrequired
        • follower_countnumberrequirednullable
      • snapshotobjectrequirednullable
        • atstringrequired
        • viewsnumberrequired
        • likesnumberrequired
        • commentsnumberrequired
        • sharesnumberrequired
        • savesnumberrequired
      • scoresobjectrequirednullableNull when the creator has no baseline yet.
        • outliernumberrequirednullableViews over the creator's median views.
        • outlier_p90numberrequirednullable
        • percentilenumberrequirednullableWhere this video sits in the creator's own sample, 0-100.
        • velocityobjectrequirednullable
          • views_per_hournumberrequired
          • windowstringrequired
        • reasonstringrequirednullablealways aged_outaged_out when the video is over 30 days old and Scores are null.
        • baselineobjectrequiredThe yardstick every Score on this creator is measured against.
          • median_viewsnumberrequired
          • p90_viewsnumberrequired
          • engagement_ratenumberrequirednullable
          • posts_per_weeknumberrequirednullable
          • sampleintegerrequired
          • confidencestringrequiredone of high, low
          • sourcestringrequiredone of videos, follower_tier
          • computed_atstringrequirednullable
          • stalebooleanrequiredTrue past 7 days; the next lookup refreshes it.
      • coveragestringalways partialPresent only where the platform's index is best-effort.
      • download_urlstringWith include=download: a link to the video file, served from our own copy. Works until download_expires_at; anyone holding it can download the file.
      • download_expires_atstringWith include=download: when download_url stops working, 7 days after our copy was made (at least a day away).
      • download_errorobjectWith include=download beside analysis: why the file could not be copied (e.g. media_unavailable, too large). The analysis is still served and the download is not charged.
        • typestringrequired
        • titlestringrequired
        • statusintegerrequired
        • codestringrequiredone of missing_authorization, invalid_key, key_revoked, invalid_token, organization_not_selected, organization_access_revoked, connection_revoked, forbidden, invalid_request, unsupported_platform, unsupported_kind, kind_deprecated, not_found, video_too_long, idempotency_conflict, run_not_ready, advertiser_ambiguous, insufficient_balance, rate_limit_exceeded, concurrency_limit_exceeded, tracker_limit_exceeded, webhook_limit_exceeded, webhook_rotation_in_progress, collection_limit_exceeded, collection_name_taken, collection_kind_mismatch, media_unavailable, vendor_error, source_unavailable, service_unavailable, internal_errorStable machine-readable code.
        • detailstring
  • Option 2objectReturned instead of a Run when estimate=true. Nothing is charged. Operations with more to say about a quote add members; the two above are always present.
    • estimate_creditsintegerrequiredWhat the call would be charged, in Credits.
    • cachedbooleanrequiredWhether a stored result can already answer. It does not change the price.

200 · The Run finished inside the wait budget: `{ run, result }`. With `estimate=true`, `{ estimate_credits, cached }` instead.

{
  "run": {
    "id": "run_8f2c1ad04b",
    "kind": "video_lookup",
    "status": "completed",
    "created_at": "2026-09-08T09:14:02.418Z",
    "completed_at": "2026-09-08T09:14:04.930Z",
    "charge_credits": 3,
    "list_price_credits": 3,
    "cached": false
  },
  "result": {
    "platform": "tiktok",
    "id": "7300000000000000000",
    "url": "https://www.tiktok.com/@someone/video/7300000000000000000",
    "description": "3 minutes at 4°C every morning for 30 days. Here is what actually changed. #coldplunge #recovery",
    "posted_at": "2026-09-05T06:32:11.000Z",
    "duration_seconds": 41,
    "thumbnail_url": "https://p16-sign.tiktokcdn.com/obj/tos-cover-7300000000000000000.jpeg",
    "hashtags": [
      "coldplunge",
      "recovery",
      "morningroutine"
    ],
    "sound": {
      "id": "7180000000000000000",
      "title": "original sound - someone"
    },
    "creator": {
      "id": "cre_9d4b2a7f1c",
      "handle": "someone",
      "name": "Someone",
      "follower_count": 184300
    },
    "snapshot": {
      "at": "2026-09-08T09:14:04.219Z",
      "views": 1284000,
      "likes": 143200,
      "comments": 2810,
      "shares": 9040,
      "saves": 21600
    },
    "scores": {
      "outlier": 4.21,
      "outlier_p90": 1.6,
      "percentile": 95,
      "velocity": {
        "views_per_hour": 6698.6,
        "window": "snapshot"
      },
      "reason": null,
      "baseline": {
        "median_views": 305000,
        "p90_views": 802000,
        "engagement_rate": 0.1342,
        "posts_per_week": 4.1,
        "sample": 20,
        "confidence": "high",
        "source": "videos",
        "computed_at": "2026-09-08T09:14:03.902Z",
        "stale": false
      }
    }
  }
}

200 · With `estimate=true`: the price, no Run, no charge.

{
  "estimate_credits": 3,
  "cached": false
}

202 Accepted

Still running: { run }. Poll GET /v1/runs/{id}.

object

Still running. Poll GET /v1/runs/{id}.

  • runobjectrequiredEvery charged operation creates a Run. The envelope is identical whether the result came back inline or was polled.
    • idstringrequired
    • kindstringrequiredWhat was run, e.g. video_lookup.
    • statusstringrequiredone of running, completed, partial, failedpartial keeps the completed items and charges for them, up to what the Balance covers; failed charges nothing.
    • created_atstringrequiredRFC 3339.
    • completed_atstringrequirednullableNull while the Run is still running.
    • charge_creditsintegerrequiredWhat this Run actually cost, in Credits. A stored answer costs the same as a fresh one.
    • list_price_creditsintegerrequiredThe full price of the work asked for, in Credits. Equal to charge_credits unless the Run charged for only part of it.
    • cachedbooleanrequiredTrue when a stored result answered without paying the vendor.
    • progressobjectWhat a running Research has done so far, refreshed as it works: stage (keywords, searching, filtering, analysing, writing_digest) and the counts known by then — keywords, found, kept, dropped_off_topic, and analysed of shortlisted. A count is absent until its stage produced it. Absent once the Run settles, and on every other kind.
      • stagestringrequiredone of keywords, searching, filtering, analysing, writing_digest
      • keywordsintegervalue at least 0
      • searchesintegervalue at least 0
      • foundintegervalue at least 0
      • judgedintegervalue at least 0
      • keptintegervalue at least 0
      • dropped_off_topicintegervalue at least 0
      • dropped_unsureintegervalue at least 0
      • shortlistedintegervalue at least 0
      • analysedintegervalue at least 0
      • updated_atstringrequired

202 · Example

{
  "run": {
    "id": "run_8f2c1ad04b",
    "kind": "video_lookup",
    "status": "running",
    "created_at": "2026-09-08T09:14:02.418Z",
    "completed_at": null,
    "charge_credits": 0,
    "list_price_credits": 0,
    "cached": false
  }
}

400 Bad Request

invalid_request (a malformed id, wait, estimate or Idempotency-Key) or unsupported_platform, refused before any Run opens. Nothing is charged.

object

RFC 9457 problem document, served as application/problem+json. Branch on code, never on the prose.

  • typestringrequired
  • titlestringrequired
  • statusintegerrequired
  • codestringrequiredone of missing_authorization, invalid_key, key_revoked, invalid_token, organization_not_selected, organization_access_revoked, connection_revoked, forbidden, invalid_request, unsupported_platform, unsupported_kind, kind_deprecated, not_found, video_too_long, idempotency_conflict, run_not_ready, advertiser_ambiguous, insufficient_balance, rate_limit_exceeded, concurrency_limit_exceeded, tracker_limit_exceeded, webhook_limit_exceeded, webhook_rotation_in_progress, collection_limit_exceeded, collection_name_taken, collection_kind_mismatch, media_unavailable, vendor_error, source_unavailable, service_unavailable, internal_errorStable machine-readable code.
  • detailstring

400 · Example

{
  "type": "https://www.shortsintel.com/docs/reference/errors/invalid_request",
  "title": "Invalid request",
  "status": 400,
  "code": "invalid_request",
  "detail": "`estimate` must be `true` or `false`."
}

401 Unauthorized

missing_authorization, invalid_key, key_revoked or invalid_token.

object

RFC 9457 problem document, served as application/problem+json. Branch on code, never on the prose.

  • typestringrequired
  • titlestringrequired
  • statusintegerrequired
  • codestringrequiredone of missing_authorization, invalid_key, key_revoked, invalid_token, organization_not_selected, organization_access_revoked, connection_revoked, forbidden, invalid_request, unsupported_platform, unsupported_kind, kind_deprecated, not_found, video_too_long, idempotency_conflict, run_not_ready, advertiser_ambiguous, insufficient_balance, rate_limit_exceeded, concurrency_limit_exceeded, tracker_limit_exceeded, webhook_limit_exceeded, webhook_rotation_in_progress, collection_limit_exceeded, collection_name_taken, collection_kind_mismatch, media_unavailable, vendor_error, source_unavailable, service_unavailable, internal_errorStable machine-readable code.
  • detailstring

401 · Example

{
  "type": "https://www.shortsintel.com/docs/reference/errors/invalid_key",
  "title": "Invalid key",
  "status": 401,
  "code": "invalid_key",
  "detail": "That API key does not exist. Keys look like `sk_live_...` and are created in the console."
}

402 Payment Required

insufficient_balance — the Balance cannot cover the quoted price (the worst case for Research and enriched search). No Run is created and nothing is charged. A multi-item Run whose Balance runs short later charges its completed items up to the Balance and ends partial.

object

RFC 9457 problem document, served as application/problem+json. Branch on code, never on the prose.

  • typestringrequired
  • titlestringrequired
  • statusintegerrequired
  • codestringrequiredone of missing_authorization, invalid_key, key_revoked, invalid_token, organization_not_selected, organization_access_revoked, connection_revoked, forbidden, invalid_request, unsupported_platform, unsupported_kind, kind_deprecated, not_found, video_too_long, idempotency_conflict, run_not_ready, advertiser_ambiguous, insufficient_balance, rate_limit_exceeded, concurrency_limit_exceeded, tracker_limit_exceeded, webhook_limit_exceeded, webhook_rotation_in_progress, collection_limit_exceeded, collection_name_taken, collection_kind_mismatch, media_unavailable, vendor_error, source_unavailable, service_unavailable, internal_errorStable machine-readable code.
  • detailstring

402 · Example

{
  "type": "https://www.shortsintel.com/docs/reference/errors/insufficient_balance",
  "title": "Insufficient balance",
  "status": 402,
  "code": "insufficient_balance",
  "detail": "Your balance does not cover this operation. Top up in the console.",
  "balance_credits": 0,
  "required_credits": 3
}

409 Conflict

idempotency_conflict — the same Idempotency-Key with a different payload.

object

RFC 9457 problem document, served as application/problem+json. Branch on code, never on the prose.

  • typestringrequired
  • titlestringrequired
  • statusintegerrequired
  • codestringrequiredone of missing_authorization, invalid_key, key_revoked, invalid_token, organization_not_selected, organization_access_revoked, connection_revoked, forbidden, invalid_request, unsupported_platform, unsupported_kind, kind_deprecated, not_found, video_too_long, idempotency_conflict, run_not_ready, advertiser_ambiguous, insufficient_balance, rate_limit_exceeded, concurrency_limit_exceeded, tracker_limit_exceeded, webhook_limit_exceeded, webhook_rotation_in_progress, collection_limit_exceeded, collection_name_taken, collection_kind_mismatch, media_unavailable, vendor_error, source_unavailable, service_unavailable, internal_errorStable machine-readable code.
  • detailstring

409 · Example

{
  "type": "https://www.shortsintel.com/docs/reference/errors/idempotency_conflict",
  "title": "Idempotency conflict",
  "status": 409,
  "code": "idempotency_conflict",
  "detail": "This Idempotency-Key was used with a different payload.",
  "run_id": "run_8f2c1ad04b"
}

429 Too Many Requests

rate_limit_exceeded or concurrency_limit_exceeded, with Retry-After.

object

RFC 9457 problem document, served as application/problem+json. Branch on code, never on the prose.

  • typestringrequired
  • titlestringrequired
  • statusintegerrequired
  • codestringrequiredone of missing_authorization, invalid_key, key_revoked, invalid_token, organization_not_selected, organization_access_revoked, connection_revoked, forbidden, invalid_request, unsupported_platform, unsupported_kind, kind_deprecated, not_found, video_too_long, idempotency_conflict, run_not_ready, advertiser_ambiguous, insufficient_balance, rate_limit_exceeded, concurrency_limit_exceeded, tracker_limit_exceeded, webhook_limit_exceeded, webhook_rotation_in_progress, collection_limit_exceeded, collection_name_taken, collection_kind_mismatch, media_unavailable, vendor_error, source_unavailable, service_unavailable, internal_errorStable machine-readable code.
  • detailstring

429 · Example

{
  "type": "https://www.shortsintel.com/docs/reference/errors/rate_limit_exceeded",
  "title": "Rate limit exceeded",
  "status": 429,
  "code": "rate_limit_exceeded",
  "detail": "60 requests per minute per key. Retry after the number of seconds in `Retry-After`.",
  "limit": 60
}

502 Bad Gateway

A Run that opened and then failed is answered as application/problem+json with its problem's status — vendor_error here, not_found as 404 — and the Run envelope alongside, so the Run id is kept. A failed Run is never charged.

object

RFC 9457 problem document, served as application/problem+json. Branch on code, never on the prose.

  • typestringrequired
  • titlestringrequired
  • statusintegerrequired
  • codestringrequiredone of missing_authorization, invalid_key, key_revoked, invalid_token, organization_not_selected, organization_access_revoked, connection_revoked, forbidden, invalid_request, unsupported_platform, unsupported_kind, kind_deprecated, not_found, video_too_long, idempotency_conflict, run_not_ready, advertiser_ambiguous, insufficient_balance, rate_limit_exceeded, concurrency_limit_exceeded, tracker_limit_exceeded, webhook_limit_exceeded, webhook_rotation_in_progress, collection_limit_exceeded, collection_name_taken, collection_kind_mismatch, media_unavailable, vendor_error, source_unavailable, service_unavailable, internal_errorStable machine-readable code.
  • detailstring

502 · Example

{
  "type": "https://www.shortsintel.com/docs/reference/errors/vendor_error",
  "title": "Vendor error",
  "status": 502,
  "code": "vendor_error",
  "detail": "The upstream data provider could not be reached.",
  "run": {
    "id": "run_8f2c1ad04b",
    "kind": "video_lookup",
    "status": "failed",
    "created_at": "2026-09-08T09:14:02.418Z",
    "completed_at": "2026-09-08T09:14:04.930Z",
    "charge_credits": 0,
    "list_price_credits": 0,
    "cached": false
  }
}

503 Service Unavailable

source_unavailable: every source this Run reads failed or is switched off. The Run fails uncharged, with its envelope alongside; endpoints names the source. Retry later rather than treating it as empty.

object

RFC 9457 problem document, served as application/problem+json. Branch on code, never on the prose.

  • typestringrequired
  • titlestringrequired
  • statusintegerrequired
  • codestringrequiredone of missing_authorization, invalid_key, key_revoked, invalid_token, organization_not_selected, organization_access_revoked, connection_revoked, forbidden, invalid_request, unsupported_platform, unsupported_kind, kind_deprecated, not_found, video_too_long, idempotency_conflict, run_not_ready, advertiser_ambiguous, insufficient_balance, rate_limit_exceeded, concurrency_limit_exceeded, tracker_limit_exceeded, webhook_limit_exceeded, webhook_rotation_in_progress, collection_limit_exceeded, collection_name_taken, collection_kind_mismatch, media_unavailable, vendor_error, source_unavailable, service_unavailable, internal_errorStable machine-readable code.
  • detailstring

503 · Example

{
  "type": "https://www.shortsintel.com/docs/reference/errors/source_unavailable",
  "title": "Source unavailable",
  "status": 503,
  "code": "source_unavailable",
  "detail": "The tiktok/video source is unavailable right now.",
  "endpoints": [
    "tiktok/video"
  ],
  "run": {
    "id": "run_8f2c1ad04b",
    "kind": "video_lookup",
    "status": "failed",
    "created_at": "2026-09-08T09:14:02.418Z",
    "completed_at": "2026-09-08T09:14:04.930Z",
    "charge_credits": 0,
    "list_price_credits": 0,
    "cached": false
  }
}

Request samples

curl "https://www.shortsintel.com/v1/videos/tiktok/7300000000000000000?wait=30" \
  -H "Authorization: Bearer sk_live_YOUR_KEY"

This call creates a Run. With wait it holds the response open until the Run finishes or the wait runs out, then answers with the finished result or 202 and the running Run. Poll GET /v1/runs/{id} until it settles; re-reading a Run is free.

  • Runs: How a call becomes a Run, waiting inline, polling and free re-reads.
  • Pricing and estimates: Credits, estimates and why a cached result costs the same as a fresh one.
  • Addressing videos and creators: Naming a video, creator, hashtag or sound.
  • Analysis: What we derive from a video, why it is computed once and reused, and when asking for a refresh is charged as new work.
  • Scores: Outlier and velocity, how a creator baseline is built, what confidence means, and when a score stops being reported.