research

A run and its hooks

One run: status, progress, reserved, charged, error (ending with the request id that started it, on a failed run) and every hook it produced. ?wait=N holds the request until the run is done or failed, or N seconds pass. With since set to the last progress.updated_at, it also returns on progress change. A done run with fewer hooks than asked for, or none, has outcome: why, in plain words; a full run has none, or one sentence on hooks under the view floor. hooks holds only clips where a person on camera is talking; bonus is always [] (removed 2026-10-08). Every hook has format and section, and since 2026-10-01 virality, virality_why, outlier, posted_at, age_days, views_per_day and the other virality v2 fields (null on older hooks). A done run also carries patterns: up to 3 hook structures winning in this deck with the hook_ids using each, computed after delivery (null until then, and on older runs). Free. MCP: get_run.

get/v1/runs/{run_id}

Request

  • Base URL: https://api.hookdetector.com (the document declares no server; this is the origin it was published from)
  • URL: https://api.hookdetector.com/v1/runs/{run_id}
  • Auth: none declared

Path parameters

run_idstring uuid required

Query parameters

waitinteger

seconds to wait for the run to finish, 0 to 55. Returns as soon as it is done or failed.

sincestring nullable

Last progress.updated_at; return early when it changes.

Headers

authorizationstring nullable

Bearer hd_... from POST /v1/accounts or POST /v1/keys

Response

Successful Response

run_idstring required
kindstring

what the run researches: "short" (hooks from TikTok, Instagram and YouTube Shorts, POST /v1/research), "long" (how long-form YouTube outliers open, POST /v1/longform) or "packaging" (their titles and thumbnails, POST /v1/packaging). Every run from before 2026-10-07 is short

topicstring required
requestedinteger required
languagestring nullable

the language or dialect asked for, as sent, or null for any language

countrystring nullable

the country asked for, as an ISO 3166-1 alpha-2 code (upper-cased, UK read as GB), or null for any country

errorstring nullable required

on a failed run: why, ending with the request id

reservedinteger required

credits held for this run when it started; what it does not use is refunded when it finishes

chargedinteger required

credits deducted from your balance for this run: what it used, never more than reserved; 0 while it runs, on a failed run, and on a run that found no hooks within the account's allowance of free empty runs (three a day, 500 credits in all; outcome says which)

credits_iginteger required

Instagram provider credits used so far, including on a refunded run; what you pay is charged

credits_ttinteger required

TikTok provider credits used so far, including on a refunded run; what you pay is charged

credits_ytinteger nullable

of credits_tt, the provider credits that went to YouTube Shorts (since 2026-10-01; null on older runs); already inside credits_tt, never added again; what you pay is charged

posted_within_daysinteger nullable

the recency window asked for (7, 30, 90 or 365 days), or null

seed_hook_idsstring[] nullable

the seed hooks asked for, or null

platformsstring[] nullable

the platforms this Short form run searches and delivers from ("tiktok", "instagram", "youtube"): the ones asked for, or all three; null on a long or packaging run

created_atstring required
finished_atstring nullable required
request_idstring nullable required

the X-Request-Id of the call that started it

conversation_idstring nullable required

the chat conversation this run belongs to, or null for a run started outside one

outcomestring nullable

on a finished run that delivered fewer hooks than asked for (or none): one plain paragraph on why, what to try next, and whether an empty run was free. On a full run whose hooks under the view floor are there because the country filter set most clips aside: that one sentence, with no advice. On a run that asked for a kind of opening, one more sentence: how many hooks open that way. null on any other run

optionsobject nullable

long and packaging runs only: the options the request set (min_outlier, intro_seconds, openers, ideas); {} when it set none

reportobject nullable

long and packaging runs only, once done; null until then. Long: patterns, the opening types at least two of the finds share (hook_type, count, video_ids, template, example); shape, how the openings are built, counted from the finds (videos, mapped, hook_seconds, how long the hook runs, and hook_words as medians, beats: each beat with the count of openings using it and the median second it starts_at, and sequence: the order of beats most of them share); and openers (hook, then, hook_type, based_on: openings WRITTEN for the subject from those structures, always labelled by openers_label, "Written by Hook Detector from the structure of these real openings", and never counted as a find). Packaging: title_patterns and thumbnail_patterns (each name, template or description, why, video_ids, count: what at least two of the packages share), ideas (title, thumbnail, thumbnail_text, pattern, based_on: packages WRITTEN for the subject from those patterns, always labelled by ideas_label, "Written by Hook Detector from the patterns in these real packages", and never counted as a find; an idea over 70 characters, with more than 5 words on its thumbnail, or one that repeats a real title is dropped), numbers (the packages counted: title_chars and title_words as medians, titles_with_number, titles_with_question, titles_cut_on_phone (with phone_title_chars, the approximate length a phone cuts a title at), thumbnails_with_text and thumbnail_words over the analysed ones, and the title_devices, thumbnail_devices and emotions that repeat, each name and count; and age_cohorts: the same counts for packages posted in the last split_days days (recent) and for older ones, with the undated counted apart) and playbook_note (one line when the patterns could not be written). Both kinds: landscape, the subject at a glance, counted from the videos the run held against their own channel (videos, channels, bar, outliers and their share, strong, breakouts, small_channel_outliers, recent_outliers, median_views, and beat_2x, beat_5x and beat_10x: how many beat their channel by that much whatever bar the run asked for); null when none was measured

Example response

{
  "charged": 63,
  "conversation_id": "5e6f7a8b-9c0d-4e1f-8a2b-3c4d5e6f7a8b",
  "created_at": "2026-09-23T10:00:00.000000+00:00",
  "credits_ig": 30,
  "credits_tt": 33,
  "finished_at": "2026-09-23T10:00:00.000000+00:00",
  "hooks": [
    {
      "above_floor": true,
      "age_days": 3.5,
      "aspect": "9:16",
      "audience": "people who already cold plunge",
      "banner": "STOP DOING 10 MINUTES",
      "caption": "cold plunge tips",
      "catchy_line": "Two minutes is the whole trick.",
      "creator": "someone",
      "creator_country": "US",
      "creator_median_views": 38000,
      "duration_s": 41,
      "format": "talking_head",
      "found_by": "search",
      "hashtags": [
        "coldplunge",
        "recovery"
      ],
      "hook": "Nobody tells you this about cold plunges.",
      "hook_id": "1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
      "hook_type": "contrarian_claim",
      "language": "English",
      "likes": 90000,
      "main_idea": "Short exposure beats long.",
      "mechanism": "\"Nobody tells you this\" promises a secret about a routine the viewer already does. The clip pays it off with the two-minute rule.",
      "onscreen_text": "STOP DOING 10 MINUTES",
      "outlier": 31.2,
      "outlier_basis": "median_views",
      "own_sound": true,
      "paid": false,
      "platform": "tiktok",
      "player_url": "https://www.tiktok.com/player/v1/7412345678901234567?autoplay=1",
      "posted_at": "2026-09-28T10:00:00+00:00",
      "rank": 1,
      "section": "main",
      "sound_reuse": 1200,
      "steal_it": "Name a routine your viewers already do and promise the thing nobody says about it.",
      "template": "Nobody tells you this about {routine}.",
      "topic": "cold plunge timing",
      "transcript": "Nobody tells you this...",
      "transcript_kind": "speech",
      "video_id": "7412345678901234567",
      "views": 1200000,
      "views_per_day": 342857.1,
      "virality": 0.91,
      "virality_why": "31x this creator's usual",
      "visual_hook": "The banner \"STOP DOING 10 MINUTES\" adds an order that contradicts the usual advice.",
      "watch_url": "https://www.tiktok.com/@someone/video/7412345678901234567",
      "why_it_travelled": "\"Nobody tells you this\" promises a secret about a routine the viewer already does. The clip pays it off with the two-minute rule."
    }
  ],
  "progress": {
    "counts": {
      "above_floor": 40,
      "found": 80,
      "held": 10,
      "on_topic": 20,
      "read": 16,
      "wanted": 10,
      "written": 10
    },
    "message": "10 hooks ready",
    "stage": "done",
    "updated_at": "2026-09-23T10:00:00.000000+00:00"
  },
  "request_id": "a1b2c3",
  "requested": 10,
  "reserved": 200,
  "run_id": "8f1c2d3e-4b5a-4c6d-8e7f-9a0b1c2d3e4f",
  "stats": {},
  "status": "done",
  "topic": "cold plunge recovery"
}

Changes

No recorded changes to this operation across all 1 revision of this API.