Get a video
/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
| Name | In | Type | Required | Default | Description |
|---|---|---|---|---|---|
platform | path | stringone of tiktok, instagram, youtube, facebook | Required | — | Platform the subject lives on. |
id | path | string | Required | — | Platform video id. |
include | query | string[]one of intelligence, transcript, download | Optional | — | 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. |
refresh | query | boolean | Optional | — | Only with include. true recomputes the analysis even when a current one is stored, and is charged as fresh. Ignored without include. |
wait | query | integermin 0; max 120 | Optional | 30 | Seconds 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. |
estimate | query | boolean | Optional | — | 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-Key | header | string | Optional | — | 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.idstringrequiredkindstringrequiredWhat was run, e.g.video_lookup.statusstringrequiredone ofrunning,completed,partial,failedpartialkeeps the completed items and charges for them, up to what the Balance covers;failedcharges 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 tocharge_creditsunless 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, andanalysedofshortlisted. A count is absent until its stage produced it. Absent once the Run settles, and on every other kind.stagestringrequiredone ofkeywords,searching,filtering,analysing,writing_digestkeywordsintegervalue at least 0searchesintegervalue at least 0foundintegervalue at least 0judgedintegervalue at least 0keptintegervalue at least 0dropped_off_topicintegervalue at least 0dropped_unsureintegervalue at least 0shortlistedintegervalue at least 0analysedintegervalue at least 0updated_atstringrequired
resultobjectrequiredOne normalised video, the same shape on every platform.platformstringrequiredidstringrequiredurlstringrequirednullabledescriptionstringrequiredposted_atstringrequirednullableduration_secondsnumberrequirednullablethumbnail_urlstringrequirednullablesoundobjectrequirednullableidstringrequiredtitlestringrequirednullable
creatorobjectrequirednullableidstringrequiredhandlestringrequirednullablenamestringrequiredfollower_countnumberrequirednullable
snapshotobjectrequirednullableatstringrequiredviewsnumberrequiredlikesnumberrequiredcommentsnumberrequiredsavesnumberrequired
scoresobjectrequirednullableNull when the creator has no baseline yet.outliernumberrequirednullableViews over the creator's median views.outlier_p90numberrequirednullablepercentilenumberrequirednullableWhere this video sits in the creator's own sample, 0-100.velocityobjectrequirednullableviews_per_hournumberrequiredwindowstringrequired
reasonstringrequirednullablealwaysaged_outaged_outwhen the video is over 30 days old and Scores are null.baselineobjectrequiredThe yardstick every Score on this creator is measured against.median_viewsnumberrequiredp90_viewsnumberrequiredengagement_ratenumberrequirednullableposts_per_weeknumberrequirednullablesampleintegerrequiredconfidencestringrequiredone ofhigh,lowsourcestringrequiredone ofvideos,follower_tiercomputed_atstringrequirednullablestalebooleanrequiredTrue past 7 days; the next lookup refreshes it.
coveragestringalwayspartialPresent only where the platform's index is best-effort.download_urlstringWithinclude=download: a link to the video file, served from our own copy. Works untildownload_expires_at; anyone holding it can download the file.download_expires_atstringWithinclude=download: whendownload_urlstops working, 7 days after our copy was made (at least a day away).download_errorobjectWithinclude=downloadbeside 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.typestringrequiredtitlestringrequiredstatusintegerrequiredcodestringrequiredone ofmissing_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.idstringrequiredkindstringrequiredWhat was run, e.g.video_lookup.statusstringrequiredone ofrunning,completed,partial,failedpartialkeeps the completed items and charges for them, up to what the Balance covers;failedcharges 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 tocharge_creditsunless 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, andanalysedofshortlisted. A count is absent until its stage produced it. Absent once the Run settles, and on every other kind.stagestringrequiredone ofkeywords,searching,filtering,analysing,writing_digestkeywordsintegervalue at least 0searchesintegervalue at least 0foundintegervalue at least 0judgedintegervalue at least 0keptintegervalue at least 0dropped_off_topicintegervalue at least 0dropped_unsureintegervalue at least 0shortlistedintegervalue at least 0analysedintegervalue at least 0updated_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.
typestringrequiredtitlestringrequiredstatusintegerrequiredcodestringrequiredone ofmissing_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.
typestringrequiredtitlestringrequiredstatusintegerrequiredcodestringrequiredone ofmissing_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.
typestringrequiredtitlestringrequiredstatusintegerrequiredcodestringrequiredone ofmissing_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.
typestringrequiredtitlestringrequiredstatusintegerrequiredcodestringrequiredone ofmissing_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.
typestringrequiredtitlestringrequiredstatusintegerrequiredcodestringrequiredone ofmissing_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.
typestringrequiredtitlestringrequiredstatusintegerrequiredcodestringrequiredone ofmissing_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.
typestringrequiredtitlestringrequiredstatusintegerrequiredcodestringrequiredone ofmissing_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"const response = await fetch(
"https://www.shortsintel.com/v1/videos/tiktok/7300000000000000000?wait=30",
{
method: "GET",
headers: {
"Authorization": "Bearer sk_live_YOUR_KEY",
},
},
);
if (!response.ok) {
throw new Error(`ShortsIntel ${response.status}`);
}
const data = await response.json();import requests
response = requests.get(
"https://www.shortsintel.com/v1/videos/tiktok/7300000000000000000?wait=30",
headers={
"Authorization": "Bearer sk_live_YOUR_KEY",
},
)
response.raise_for_status()
data = response.json()Related guides
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.