Skip to content

API referenceResearch

Read a research's digest

GET/v1/research/{id}/digest

The digest alone, with the intent, keywords and coverage it was computed in, and none of the evidence. GET /v1/research/{id} carries the digest too, alongside the counts; this is for feeding the synthesis into a prompt without paying tokens for the rest. There is nothing to page — a research has one digest — and a digest is always frozen: as_of is accepted like on every other read, but always reads run. A research that has not finished answers 409 run_not_ready here, where the overview instead hands back the run to poll.

Open in Playground(no API Key needed)

Price

Free, forever. Collection is the only thing charged.

Parameters

NameInTypeRequiredDefaultDescription
idpathstringRequired—Research id — the id of the Run that collected it.
as_ofquerystringone of run, latestOptionalrunAccepted for symmetry with the other research reads, so one basis can be passed to all of them, and validated like theirs. A digest is always served as run whichever you pass: it is what a model wrote about the shortlist the research collected, and there is no fresher one to serve.

Responses

200 OK

The digest, with the context it was computed in. as_of always reads run here: a digest is what a model wrote about the shortlist the research collected, not a number joined to a snapshot, so there is no fresher one to serve.

object

A research's digest with the intent, keywords and coverage it was computed in — and none of the evidence.

  • digestobjectrequired
    • summarystringrequiredTwo or three sentences answering the intent directly: what is working on this topic right now, across the videos analysed.
    • themesarray of objectrequireditems 1 to 8
      • namestringrequiredShort label, three or four words.
      • sharenumberrequiredvalue 0 to 1Fraction of the analysed videos that belong to this theme, 0-1.
      • descriptionstringrequiredWhat the videos in this theme have in common.
      • video_idsarray of stringrequiredvideo_id keys (platform:id) of the analysed videos that belong to this theme, exactly as given in the evidence.
    • hooksarray of objectrequireditems at most 12
      • textstringrequiredThe opening line, quoted VERBATIM from the evidence. Never paraphrase, never invent.
      • video_idstringrequiredvideo_id key (platform:id) of the video this hook is quoted from, exactly as given in the evidence.
      • hook_typestringrequiredThe kind of hook, e.g. question, claim, negation, stat.
      • why_it_worksstringrequiredOne sentence.
      • templatestringlength at least 1The verbatim hook with its specifics (product, number, mistake, outcome, audience) replaced by short bracketed slots, every other word kept as spoken, e.g. "Stop [mistake] if you want [outcome]".
      • liftobjectnullableThe lift_tables.hook_type row for the quoted video's analysed hook type, attached in code (never written by the model). Null when that video has no analysed hook type.
        • valuestringrequiredThe analysed hook type, or format + hook type, the row is keyed by.
        • video_countintegerrequired
        • median_viewsnumberrequirednullable
        • median_engagement_ratenumberrequirednullable
        • views_liftnumberrequirednullable
        • engagement_liftnumberrequirednullable
        • low_confidencebooleanrequired
    • sentimentobjectrequired
      • positivenumberrequiredvalue 0 to 1
      • neutralnumberrequiredvalue 0 to 1
      • negativenumberrequiredvalue 0 to 1
      • notestringrequiredOne sentence on what drives the distribution.
    • anglesarray of objectrequireditems 1 to 8
      • anglestringrequiredA content angle worth trying.
      • rationalestringrequiredWhy the evidence supports it, referencing what was seen.
    • formulasarray of objectitems at most 5
      • namestringrequiredShort label, three to five words.
      • recipestringrequiredOne line saying how to make it: the format, how it opens, and the order of its beats.
      • visual_formatstringrequiredThe visual format, copied exactly as it appears in lift_tables.visual_format.
      • hook_typestringrequiredThe hook type, copied exactly as it appears in lift_tables.hook_type.
      • structurestringrequiredThe beat structure the example videos share, as short beats joined by arrows, e.g. "problem → demo → result → CTA".
      • video_idsarray of stringrequiredUp to 3 video_id keys (platform:id) of analysed videos that follow this formula, best performing first, exactly as given in the evidence.
      • liftobjectrequirednullableThe lift_tables.format_hook row for this formula's visual format and hook type, attached in code (never written by the model): its video_count and lifts. The lift is for the format + hook type pair only; the structure is not bucketed. Null when no analysed video has that pair.
        • valuestringrequiredThe analysed hook type, or format + hook type, the row is keyed by.
        • video_countintegerrequired
        • median_viewsnumberrequirednullable
        • median_engagement_ratenumberrequirednullable
        • views_liftnumberrequirednullable
        • engagement_liftnumberrequirednullable
        • low_confidencebooleanrequired
    • gapsarray of objectitems at most 5Up to 5 things the audience asks for in the top 10 videos' comments that no analysed video addresses, each with verbatim quoted comments (and the video they came from) and a suggested angle. Empty when no comments were available. Absent on research recorded before gaps existed.
      • askstringrequiredWhat the audience asks for or complains about, in one sentence, in your own words.
      • commentsarray of objectrequireditems 1 to 3
        • textstringrequiredA comment quoted VERBATIM from <comments>, character for character. Never paraphrase, never invent.
        • video_idstringrequiredvideo_id key (platform:id) of the video this comment was left under, exactly as given in <comments>.
      • anglestringrequiredA video someone could make that answers this ask, in one sentence.
  • digest_versionintegerrequired
  • lift_tablesobjectLift tables computed in code over the analysed videos: per visual format, hook type and length bucket, the video count, median views and engagement rate, and lift versus the whole set's median. The Digest quotes lifts only from these. Absent on research recorded before they existed.
    • setobjectrequired
      • video_countintegerrequired
      • median_viewsnumberrequirednullable
      • median_engagement_ratenumberrequirednullable
    • visual_formatarray of objectrequired
      • valuestringrequiredThe visual format, hook type, length bucket, or <format> + <hook type>.
      • video_countintegerrequired
      • median_viewsnumberrequirednullable
      • median_engagement_ratenumberrequirednullable(likes + comments + shares + saves) / views.
      • views_liftnumberrequirednullableMedian views of this row over the whole set's median views. Null when the set median is 0 or unknown.
      • engagement_liftnumberrequirednullable
      • low_confidencebooleanrequiredTrue when the row rests on fewer than 3 videos.
    • hook_typearray of objectrequired
      • valuestringrequiredThe visual format, hook type, length bucket, or <format> + <hook type>.
      • video_countintegerrequired
      • median_viewsnumberrequirednullable
      • median_engagement_ratenumberrequirednullable(likes + comments + shares + saves) / views.
      • views_liftnumberrequirednullableMedian views of this row over the whole set's median views. Null when the set median is 0 or unknown.
      • engagement_liftnumberrequirednullable
      • low_confidencebooleanrequiredTrue when the row rests on fewer than 3 videos.
    • lengtharray of objectrequired
      • valuestringrequiredThe visual format, hook type, length bucket, or <format> + <hook type>.
      • video_countintegerrequired
      • median_viewsnumberrequirednullable
      • median_engagement_ratenumberrequirednullable(likes + comments + shares + saves) / views.
      • views_liftnumberrequirednullableMedian views of this row over the whole set's median views. Null when the set median is 0 or unknown.
      • engagement_liftnumberrequirednullable
      • low_confidencebooleanrequiredTrue when the row rests on fewer than 3 videos.
    • format_hookarray of objectVisual format and hook type together, value as <format> + <hook type>: the rows the Digest's formulas take their lift from. Absent on research recorded before it existed.
      • valuestringrequiredThe visual format, hook type, length bucket, or <format> + <hook type>.
      • video_countintegerrequired
      • median_viewsnumberrequirednullable
      • median_engagement_ratenumberrequirednullable(likes + comments + shares + saves) / views.
      • views_liftnumberrequirednullableMedian views of this row over the whole set's median views. Null when the set median is 0 or unknown.
      • engagement_liftnumberrequirednullable
      • low_confidencebooleanrequiredTrue when the row rests on fewer than 3 videos.
  • intentstringrequired
  • keywordsarray of stringrequired
  • keyword_rationalestringrequired
  • coveragemap of stringrequired
  • 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 · The digest, with the context it was computed in. `as_of` always reads `run` here: a digest is what a model wrote about the shortlist the research collected, not a number joined to a snapshot, so there is no fresher one to serve.

{
  "digest": {
    "summary": "Cold plunge videos are travelling on measured personal diaries rather than on science explainers. The videos that break out open on an unimpressive first number and pay it off with a later one, and keep the discomfort on camera instead of cutting around it. Sentiment is broadly positive, with the negative share concentrated on the cost of equipment.",
    "themes": [
      {
        "name": "30-day diaries",
        "share": 0.45,
        "description": "One creator, one tub, a counter on screen, and the change stated as a number rather than a feeling.",
        "video_ids": [
          "tiktok:7300000000000000000"
        ]
      },
      {
        "name": "Before-workout plunging",
        "share": 0.2,
        "description": "Shorter clips arguing about timing relative to training, usually with a gym setting.",
        "video_ids": [
          "tiktok:7300000000000000042"
        ]
      }
    ],
    "hooks": [
      {
        "text": "Day one I lasted eleven seconds.",
        "video_id": "tiktok:7300000000000000000",
        "hook_type": "stat",
        "why_it_works": "An unimpressive concrete number sets up a payoff the viewer stays for.",
        "template": "Day one I lasted [small number] [unit].",
        "lift": {
          "value": "STORY",
          "video_count": 9,
          "median_views": 1840000,
          "median_engagement_rate": 0.0912,
          "views_lift": 1.6,
          "engagement_lift": 1.21,
          "low_confidence": false
        }
      }
    ],
    "sentiment": {
      "positive": 0.62,
      "neutral": 0.23,
      "negative": 0.15,
      "note": "Enthusiasm for the routine itself; the negative share is almost entirely the price of a tub."
    },
    "angles": [
      {
        "angle": "Open on the day-30 number and work backwards to day one.",
        "rationale": "Every diary in the shortlist that led with the outcome outperformed the ones that led with the method."
      }
    ],
    "gaps": [
      {
        "ask": "Viewers want a cold plunge setup that does not cost hundreds.",
        "comments": [
          {
            "text": "ok but what do I do if I can't afford a 600 dollar tub",
            "video_id": "tiktok:7300000000000000000"
          }
        ],
        "angle": "A 30-day diary on a chest freezer or bathtub build, with the receipt on screen."
      }
    ]
  },
  "digest_version": 2,
  "intent": "why are cold plunge videos taking off",
  "keywords": [
    "cold plunge",
    "ice bath",
    "cold exposure",
    "cold plunge routine"
  ],
  "keyword_rationale": "The intent is about a practice rather than a product, so the keywords cover the practice and its two common synonyms.",
  "coverage": {
    "tiktok": "complete",
    "instagram": "partial"
  },
  "as_of": "run"
}

400 Bad Request

invalid_request — an unknown as_of.

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": "`as_of` must be one of run, latest."
}

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/digest?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.