long form

Find long-form hooks: how the YouTube outliers on a topic open.

Start a long-form run and return its run_id at once. It searches YouTube for long-form videos on the topic (never Shorts), keeps only the outliers (videos whose views are at least min_outlier times the median of their own channel's recent uploads, 2 by default) posted in 2023 or later, reads the first intro_seconds (60 by default) of each one's transcript and says why that opening holds a viewer. Then GET /v1/runs/{run_id}?wait=55 until status is done or failed: the results are the run's finds, each with hook and intro (verbatim, timed lines), hook_type, mechanism, promise, open_loop, beats, template and steal_it; report.patterns says which opening types several of them share, report.shape how the openings are built (how long the hook runs, which beats they use and when, counted from the finds) and report.openers holds up to openers (3 by default) openings written for your topic from those structures, always labelled as written, never counted as a find. While it runs, progress.items holds the finds written so far, as cards, and progress.scan the videos it is looking at. It reserves up to 90 credits for 20 videos or fewer and 5 more for each video above that (140 for the largest count, 30; less on a smaller balance, down to 25) and charges only the provider calls it made (the searches, one call per channel measured, one per transcript read); a failed run costs nothing, and so does one that finds nothing, inside the same allowance a hook run has. How long it takes has not been measured yet: poll, never assume a time. A second long or packaging run on the same topic within a week starts from the videos the first one bought (reuse), and a video you already had read is delivered from that read (analysis_from_earlier_read).

topic may be a YouTube address instead of a subject: a channel's (https://www.youtube.com/@name, or @name) returns that channel's own outliers among its recent long-form uploads (one provider call, no search), and a long-form video's returns that one video whatever its multiple (requested is 1). The run's options.link says which ("channel" or "video"); no openings are written for a link. channel_size (small, medium, large) keeps only channels of that size, by what their uploads usually get. report.landscape is the subject at a glance, counted. Starts are limited per account per hour (429). Needs research. MCP: find_long_hooks.

post/v1/longform

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/longform
  • Auth: none declared

Headers

Idempotency-Keystring nullable

retry safely: the same key within 24 hours returns the first run and reserves nothing

authorizationstring nullable

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

Request body

topicstring required

the subject to research, 3 to 500 characters, in any language. Or a YouTube address instead of a subject: a channel's (https://www.youtube.com/@name, or just @name) returns that channel's own outliers, its recent long-form uploads that beat its usual; a long-form video's (https://www.youtube.com/watch?v=...) returns that one video, whatever its multiple. A Short's address is refused (422)

countinteger

how many long-form hooks to deliver, 1 to 30

languagestring nullable

optional, up to 60 characters: the language the videos should be spoken in, in words or as a tag ("Spanish", "pt-BR"). Left out, it is read from the topic's own language

countrystring nullable

optional, an assigned ISO 3166-1 alpha-2 code (US, GB, MA): searches YouTube as seen from this country. It steers the search; it does not filter the channels

min_outliernumber nullable

optional, 1.5 to 100 (default 2): how many times its own channel's usual views a video must have to count as an outlier. Only outliers are ever delivered

posted_within_days30 | 90 | 365 | 730 | null nullable

optional, how recent the videos must be, as days: 30, 90, 365, 730. Null or left out means everything since 2023-01-01; nothing older is ever delivered

channel_size'small' | 'medium' | 'large' | 'null' nullable

optional: only videos from channels of this size, by what the channel's uploads usually get (its median views, the figure the outlier is measured against): small (under 20,000), medium (up to 200,000) or large. Null or left out: any size

intro_secondsinteger nullable

optional, 20 to 120 (default 60): how many seconds of each video's opening to read and return

openersinteger nullable

optional, 0 to 6 (default 3): how many openings to write for the subject from the structure of the real ones; 0 for none

reuseboolean

start from the videos your last long or packaging run on this topic already bought (within a week), so the second tab on a topic costs little

Example request

{
  "topic": "home espresso",
  "language": "Spanish",
  "country": "US",
  "min_outlier": 3,
  "posted_within_days": 365,
  "channel_size": "small",
  "intro_seconds": 60,
  "openers": 3
}

Response

a replay of an earlier start with the same Idempotency-Key; nothing was reserved

run_idstring required
statusstring 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

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 ("find more like these"), 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

credits_reservedinteger required
created_atstring required

Example response

{
  "created_at": "2026-09-23T10:00:00.000000+00:00",
  "credits_reserved": 200,
  "requested": 10,
  "run_id": "8f1c2d3e-4b5a-4c6d-8e7f-9a0b1c2d3e4f",
  "status": "queued",
  "topic": "cold plunge recovery"
}

Changes

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