Skip to content

API referenceResearch

List a research's videos

GET/v1/research/{id}/videos

The videos a research collected, filtered and ordered however you like. Ask a narrower question of a research you already paid for — TikTok only, over 100k views, best outlier first — instead of running another one. Filters compose. Paging is limit/offset with a total, not the next_cursor the rest of this API uses: a research is a frozen set of at most 200 rows and you choose the ordering, so a count is cheap and more useful than a cursor. An offset past the end is an empty page with the true total, not an error. Under as_of=run (the default) the numbers are frozen as the research saw them, so this list always agrees with the digest; under as_of=latest they are the newest this API holds and may not. as_of on the response says which you got. A research that has not finished answers 409 run_not_ready.

Open in Playground(no API Key needed)

Price

Free, forever, filtered or not. Collection is the only thing charged.

Parameters

NameInTypeRequiredDefaultDescription
idpathstringRequired—Research id — the id of the Run that collected it.
min_viewsqueryintegermin 0Optional—Keep videos with at least this many views. A video with no view count is excluded rather than assumed to be at zero.
max_viewsqueryintegermin 0Optional—Keep videos with at most this many views.
platformsquerystringOptional—Comma-separated, or repeated, each one of tiktok, instagram, youtube, facebook; any other value is a 400. Omit for every platform the research covered. There is no per-video region: a research collects under one region.
start_datequerystringOptional—Keep videos posted at or after this ISO 8601 date (2026-01-01, midnight UTC) or date-time with Z or an explicit offset (2026-01-01T00:00:00Z). A video with no posted date is excluded.
end_datequerystringOptional—Keep videos posted at or before this ISO 8601 date or date-time (a date-time needs Z or an explicit offset). A date alone (2026-01-31) includes the whole of that UTC day; a date-time is the exact instant. A video with no posted date is excluded.
creatorquerystringOptional—A creator id or handle, with or without the @, matched case-insensitively.
hashtagquerystringOptional—A single hashtag, with or without the leading #.
soundquerystringOptional—A sound id, as sound.id carries it.
order_byquerystringone of rank, outlier, views, posted_atOptionalrankrank is the research's own ordering — outlier, then views — which is what the digest was written from. It is that frozen order under either as_of: latest does not re-rank, so use order_by=outlier for the order over the numbers actually served. Ties break on platform:id, so paging never drops or repeats a video.
sortquerystringone of asc, descOptionaldescDirection. desc is best-first for every order_by. Videos missing the value being sorted on come last in both directions: unknown is not the same as smallest.
limitqueryintegermin 1; max 100Optional25Videos per page. Over the maximum, or negative, is a 400 rather than a silent clamp.
offsetqueryintegermin 0Optional0Videos to skip. Past the end returns an empty page with the true total.
as_ofquerystringone of run, latestOptionalrunWhich snapshots to read. run (the default) is what the research itself saw, frozen, so the list always agrees with the digest. latest serves the newest snapshot this API holds for each video instead, and the filters and ordering then run over those numbers — a min_ filter under latest means "over that many now". Under latest, outlier and outlier_p90 are recomputed against the research's baseline; velocity and percentile stay as the research measured them. Be aware that latest is uneven until you refresh: a video is only as fresh as the last time anything looked at it, and a video nothing has looked at since keeps the numbers the research recorded rather than coming back empty. POST /research/{id}/refresh brings its refreshable videos (TikTok today) up to date; the rest keep what the research recorded. Every response states the basis it served.

Responses

200 OK

{ items, total, limit, offset, as_of }.

object

  • itemsarray of objectrequired
    • 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.
    • rankintegerrequiredPosition in the research's own ranking, best first from 0 — outlier, then views.
    • item_idstringrequirednullableThe run item this video was delivered by, if still present.
    • cachedbooleanrequiredWhether the analysis was already stored rather than paid for.
  • totalintegerrequiredRows matching the filters, before limit and offset.
  • limitintegerrequired
  • offsetintegerrequired
  • as_ofstringrequiredone of run, latestWhich snapshots the numbers come from: run is what the research itself saw, frozen, so a read always agrees with the digest; latest is the newest snapshot this API holds for each video.

200 · `{ items, total, limit, offset, as_of }`.

{
  "items": [
    {
      "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
        }
      },
      "rank": 0,
      "item_id": "ri_5c30f18ab4",
      "cached": true
    },
    {
      "platform": "tiktok",
      "id": "7300000000000000042",
      "url": "https://www.tiktok.com/@anotherone/video/7300000000000000042",
      "description": "I tried cold plunging before every workout for a week. #coldplunge",
      "posted_at": "2026-09-06T13:04:52.000Z",
      "duration_seconds": 28,
      "thumbnail_url": "https://p16-sign.tiktokcdn.com/obj/tos-cover-7300000000000000042.jpeg",
      "hashtags": [
        "coldplunge",
        "gym"
      ],
      "sound": {
        "id": "7180000000000000099",
        "title": "Winter Air"
      },
      "creator": {
        "id": "cre_1a77c0b3de",
        "handle": "anotherone",
        "name": "Another One",
        "follower_count": 21400
      },
      "snapshot": {
        "at": "2026-09-08T09:14:04.219Z",
        "views": 96200,
        "likes": 8140,
        "comments": 212,
        "shares": 401,
        "saves": 1180
      },
      "scores": null,
      "rank": 1,
      "item_id": "ri_5c30f18ab5",
      "cached": false
    }
  ],
  "total": 48,
  "limit": 25,
  "offset": 0,
  "as_of": "run"
}

400 Bad Request

invalid_request — an unknown platform, a min_views or max_views that is not a non-negative integer, an unparseable date, an unknown order_by, sort or as_of, a limit outside 1 to 100, or a negative or non-integer offset.

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": "`limit` must be an integer between 1 and 100, and `offset` a non-negative integer."
}

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."
}

404 Not Found

not_found.

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

404 · Example

{
  "type": "https://www.shortsintel.com/docs/reference/errors/not_found",
  "title": "Not found",
  "status": 404,
  "code": "not_found",
  "detail": "No research with that id for this Customer."
}

409 Conflict

run_not_ready — the research is still running, or failed with nothing to serve. If it is still running, poll GET /v1/runs/{id} and read again once it settles; a failed research never becomes readable.

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/run_not_ready",
  "title": "Run not ready",
  "status": 409,
  "code": "run_not_ready",
  "detail": "Research `run_c07a41bb92` is still running. Poll GET /v1/runs/run_c07a41bb92 until it reaches a terminal status."
}

Request samples

curl "https://www.shortsintel.com/v1/research/id_example/videos?platforms=tiktok%2Cyoutube&start_date=2026-01-01&end_date=2026-01-31&order_by=rank&sort=desc&limit=25&offset=0&as_of=run" \
  -H "Authorization: Bearer sk_live_YOUR_KEY"
  • Research: How an intent becomes a digest, why a research id is a run id, and how to re-slice what you collected for free instead of paying for it twice.