---
title: "Find packaging: the titles and thumbnails that earned the click on a topic."
method: POST
path: "/v1/packaging"
tags: ["packaging"]
---

# Find packaging: the titles and thumbnails that earned the click on a topic.

`POST /v1/packaging`

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

Start a packaging run and return its run_id at once. It finds the long-form YouTube
videos on the topic that beat their own channel (the same outlier search as POST
/v1/longform, 2023 or later), looks at each one's thumbnail beside its title, and
returns the pairs as the run's `finds`: title and thumbnail_url (YouTube's own), views
and outlier, and what the package does: title_device, title_template, thumbnail_text,
thumbnail_subject, thumbnail_composition, thumbnail_devices and gap (what the picture
shows that the title does not say). `report` then holds title_patterns and
thumbnail_patterns (what at least two of them share) and ideas: up to `ideas` (6 by
default) titles and thumbnail concepts written for your topic from those patterns,
always labelled as written, never counted as a find, and `numbers`: the packages
counted (title length, how many carry a number or a question, how many thumbnails print
words, which devices repeat). GET /v1/runs/{run_id}?wait=55 until done; while it runs,
`progress.items` holds the packages looked at so far, as cards, and `progress.scan` the
videos it is looking at. It reserves up to 70 credits for 24 packages or fewer and 3 more for each package above that (88 for the largest count, 30; down to 25) and charges only the provider
calls it made (the searches, one call per channel measured, and one for each video whose
date had to be read exactly): looking at a thumbnail costs none. A failed run costs
nothing, and so does one that finds nothing, inside the allowance. How long it takes
has not been measured yet: poll, never assume a time. `topic` may be a channel's or a
video's YouTube address, and `channel_size` narrows to channels of one size, exactly as
on POST /v1/longform; no ideas are written for a link. Starts are limited per account
per hour (429). Needs research. MCP: find_packaging.

## 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

- PackagingIn
  - `topic` string, 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)
  - `count` integer: how many packages to deliver, 1 to 30
  - `language` string, 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
  - `country` string, 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_outlier` number, 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_days` 30 | 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
  - `ideas` integer, nullable: optional, 0 to 10 (default 6): how many written packages (a title and a thumbnail concept) to add for the subject; 0 for none
  - `reuse` boolean: 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

## 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, at_capacity, restarting or unavailable, as on POST /v1/research

---

[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)
