Skip to content

API referenceVideos

Score a video

POST/v1/videos/{platform}/{id}/score

Outlier multiple against the creator's median views, outlier_p90, views per hour and percentile within the creator's sample. Forces the creator fetch and the baseline computation. Velocity is null with reason: aged_out past 30 days. Numbers only, no labels.

Open in Playground(no API Key needed)

Price

9 credits, always fresh — asking for a score is asking for the fetch.

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 tiktokRequired—Platform the subject lives on.
idpathstringRequired—Platform video id.
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
    • resultobjectrequiredScoring forces the creator fetch, so both come back.
      • videoobjectrequiredOne 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.
      • creatorobjectrequirednullableThe creator whose baseline the video was scored against.
        • platformstringrequired
        • idstringrequired
        • handlestringrequirednullable
        • namestringrequired
        • avatar_urlstringrequirednullable
        • follower_countnumberrequirednullable
        • biostringrequirednullable
        • verifiedbooleanThe verified badge, where the platform reports it (Instagram, YouTube).
        • video_countnumbernullableVideos the platform says the creator has (TikTok, YouTube).
        • total_likesnumbernullableLikes the creator's videos have received, lifetime (TikTok). TikTok reports no lifetime views.
        • snapshotobjectrequirednullable
          • atstringrequired
          • followersnumberrequirednullable
        • baselineobjectrequirednullableThe 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.
        • videosarray of objectWith include=videos.
          • 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.
        • next_cursorstringnullableWith include=videos on a platform that pages the feed (every one but TikTok): pass it back as cursor, with the same creator, for the next page; null on the last page.
        • like_countnumbernullablePage likes (Facebook).
        • categorystringnullableThe Page's category, e.g. Artist (Facebook).
        • advertiserobjectFacebook: the Meta Advertiser with this Page's id, when we already hold it (GET /advertisers/{page_id}). Absent otherwise.
          • page_idstringrequired
          • namestringrequirednullable
          • page_urlstringrequired
  • 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_score",
    "status": "completed",
    "created_at": "2026-09-08T09:14:02.418Z",
    "completed_at": "2026-09-08T09:14:04.930Z",
    "charge_credits": 9,
    "list_price_credits": 9,
    "cached": false
  },
  "result": {
    "video": {
      "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
        }
      }
    },
    "creator": {
      "platform": "tiktok",
      "id": "cre_9d4b2a7f1c",
      "handle": "someone",
      "name": "Someone",
      "avatar_url": "https://p16-sign.tiktokcdn.com/obj/tos-avatar-someone.jpeg",
      "follower_count": 184300,
      "bio": "Cold water, early mornings, and the numbers behind both.",
      "snapshot": {
        "at": "2026-09-08T09:14:04.219Z",
        "followers": 184300
      },
      "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": 9,
  "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_score",
    "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": 9
}

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_score",
    "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_score",
    "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 -X POST "https://www.shortsintel.com/v1/videos/tiktok/7300000000000000000/score?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.