---
title: "Find hooks on a topic. Returns immediately; wait on the run."
method: POST
path: "/v1/research"
tags: ["research"]
---

# Find hooks on a topic. Returns immediately; wait on the run.

`POST /v1/research`

## 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/research`
- Auth: none declared

Start a research run across TikTok and Instagram and return its run_id at once.
It reserves up to 200 credits (less on a smaller balance, down to 40) and charges only
what it used; a failed run costs nothing, and so does a run that finds no hooks, within
a per-account allowance (llms.txt has the numbers). A fresh run takes about 2 to 3 minutes (up to about 5 on a dialect request): then
GET /v1/runs/{run_id}?wait=55 until status is done or failed. Needs research. A key
with a credit_limit reserves at most what is left of it (403 key_credit_limit_reached
under the minimum). Any language, dialect or country: set language ("Moroccan
Darija", "ar-MA") and country ("MA"), or say it in the topic. Since 2026-10-01:
posted_within_days (7, 30, 90 or 365) keeps the search to recent clips, and
seed_hook_ids (up to 10 of your hooks) means "find more like these". Hooks carry
virality (the v2 score), outlier (views over the creator's usual), posted_at and
the other signals; a done run carries patterns, the structures winning in its deck.
Since 2026-10-10: platforms (any of "tiktok", "instagram", "youtube"; all three when
left out) searches and delivers only those platforms.
MCP: find_hooks.

## Headers

- `Idempotency-Key` string, nullable: retry safely: the same key within 24 hours returns the first run and reserves nothing
- `authorization` string, nullable: Bearer hd_... from POST /v1/accounts or POST /v1/keys

## Request body

- ResearchIn
  - `topic` string, required: what the clips are about, 3 to 500 characters. Instructions inside it are fine: a long topic, or one with a language or country, is searched by the subject read out of it, never verbatim.
  - `count` integer: how many hooks to deliver, 1 to 30
  - `conversation_id` string, nullable: attach the run to one of your conversations
  - `reuse` boolean: start from rows you already paid for
  - `language` string, nullable: optional, up to 60 characters: only clips spoken or written in this language or dialect, in words or as a tag: "Moroccan Darija", "ar-MA", "Egyptian Arabic", "Brazilian Portuguese". Any language works, including a regional variety ("Quebec French", "Flemish", "Catalan") and a mixed, code-switched one ("Hinglish", "Taglish", "Moroccan Darija with French"), or two joined by "or" ("Tagalog or Taglish"). The value is handed to the judges exactly as written; how well they honour a mixed or either-of value is not measured yet. Saying it inside topic works too; this field wins when both are given.
  - `country` string, nullable: optional: only clips from creators in this country, as an assigned ISO 3166-1 alpha-2 code: MA, EG, BR, US (UK is read as GB). For a region that is not a country, send its country's code and name the regional variety in language: Quebec is country CA with language "Quebec French", Flanders BE with "Flemish", Catalonia ES with "Catalan". The country places the TikTok search there; the language judge does the regional filtering, and Instagram reports no country. A country alone does not restrict the language.
  - `posted_within_days` 7 | 30 | 90 | 365 | null, nullable: since 2026-10-01: optional, how recent the clips must be, as days: 7, 30, 90, 365. The search asks the platforms for that window and the floor for a clip under 7 days old uses views per day (10,000 a day passes); null or left out means any time. A clip's `posted_at` and `age_days` say what the run found.
  - `seed_hook_ids` string[], nullable: since 2026-10-01: optional, up to 10 hook ids from your runs (a kept hook from GET /v1/keeps, or any hook you can read): "find more like these". The engine reads their lines, topics, creators and languages to steer the search phrases and the creators it expands; a seed is never delivered again. An id that is not one of your hooks is 422.
  - `platforms` string[], nullable: since 2026-10-10: optional, which platforms to search and deliver from, a non-empty list of "tiktok", "instagram" and "youtube" (YouTube Shorts); left out (or all three) searches all three. ["instagram"] delivers Instagram reels only. Names are read without regard to case and a repeat counts once; an empty list or any other name is 422 naming platforms. The run echoes it as `platforms`.

## Response `200`

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

- RunStartedOut
  - `run_id` string, required
  - `status` string, required
  - `kind` string: 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
  - `topic` string, required
  - `requested` integer, required
  - `language` string, nullable: the language or dialect asked for, as sent, or null for any language
  - `country` string, 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_days` integer, nullable: the recency window asked for (7, 30, 90 or 365 days), or null
  - `seed_hook_ids` string[], nullable: the seed hooks asked for ("find more like these"), or null
  - `platforms` string[], 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_reserved` integer, required
  - `created_at` string, required

## Other responses

- `202`: Successful Response
- `401`: missing, invalid, revoked or expired API key (code key_expired)
- `402`: not enough credits
- `403`: the key lacks the scope this call needs (insufficient_scope), or its credit_limit is spent (key_credit_limit_reached)
- `413`: request body over the size cap
- `422`: invalid input
- `429`: too_many_runs: the account already has the most runs in flight it may; see Retry-After; nothing was reserved
- `500`: our fault; names the request id
- `503`: nothing was reserved; see Retry-After. search_unavailable: the video search provider is refusing our requests right now. at_capacity: today's research capacity is used up. restarting: this server is restarting for an update. unavailable: every database connection stayed busy

---

[API](https://skmtc.dev/hookdetector/apis/hook-detector.md) · [All operations](https://skmtc.dev/hookdetector/apis/hook-detector/llms.txt) · [OpenAPI document](https://skmtc.dev/hookdetector/apis/hook-detector/revisions/b136a2804d2b?raw)
