Every API here is available over the APIs.io API and to AI agents over MCP.
openapi: 3.2.0
info:
title: Hook Detector Shorts API
description: 'Find short-form hooks that already work.
Sign up with a beta access code (ask at https://hookdetector.com/access): one POST to
`/v1/accounts` with `access_code` gives an API key and free credits. The same operations are available as MCP tools at `/mcp`.
Full guide, always current, at `/llms.txt`.'
contact:
name: Hook Detector
url: https://hookdetector.com/
email: contact@hookdetector.com
version: 3.0.0
tags:
- name: shorts
paths:
/v1/shorts/trending:
get:
tags:
- shorts
summary: 'Trending Shorts: the short videos rising now on a subject'
description: 'The short videos (TikToks, Instagram Reels, YouTube Shorts) rising now on `subject`,
fastest first, from the same searches the Short form engine buys. Each item: video_id,
platform, url, cover_url, creator, caption, plays, posted_at, age_hours, plays_per_hour
(plays over the hours since posting, at least one), multiple and multiple_basis (plays
over the creator''s usual: the median of their latest videos, or TikTok likes per video;
null when the usual could not be measured), creator_usual, duration_s, and spoken_hook
with spoken_hook_from (the opening line when one of your own Short form runs delivered
the video, or Scribe v3 heard it). At most 20 items, at most 2 per creator; a video
with no post date, or posted before the window, is never listed. `computed_from` says
what the numbers are computed from; `unavailable` names a platform whose search did not
answer. Free: credits_charged is 0. The same subject, platform and window within 30
minutes is served from the cache (`cached`); fresh fetches are limited per account per
hour (429). 503 search_unavailable when no platform answered. Needs read. MCP:
trending_shorts.'
operationId: shorts_trending_v1_shorts_trending_get
parameters:
- name: subject
in: query
required: false
schema:
anyOf:
- type: string
- type: 'null'
description: what the videos are about, 2 to 120 characters
title: Subject
description: what the videos are about, 2 to 120 characters
- name: platform
in: query
required: false
schema:
anyOf:
- type: string
- type: 'null'
description: tiktok, instagram or youtube; leave out for all three
title: Platform
description: tiktok, instagram or youtube; leave out for all three
- name: window
in: query
required: false
schema:
anyOf:
- type: string
- type: 'null'
description: '24h, 7d (default) or 30d: how recently they were posted'
title: Window
description: '24h, 7d (default) or 30d: how recently they were posted'
- name: authorization
in: header
required: false
schema:
anyOf:
- type: string
- type: 'null'
description: Bearer hd_... from POST /v1/accounts or POST /v1/keys
title: Authorization
description: Bearer hd_... from POST /v1/accounts or POST /v1/keys
responses:
'200':
description: Successful Response
content:
application/json:
schema:
$ref: '#/components/schemas/TrendingShortsOut'
'401':
description: missing, invalid, revoked or expired API key (code key_expired)
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorOut'
'403':
description: the key lacks the scope this call needs (insufficient_scope), or its credit_limit is spent (key_credit_limit_reached)
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorOut'
'413':
description: request body over the size cap
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorOut'
'422':
description: invalid input
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorOut'
'429':
description: rate limited; see Retry-After
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorOut'
'500':
description: our fault; names the request id
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorOut'
'503':
description: 'search_unavailable: no platform''s search answered; nothing was charged'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorOut'
/v1/review/short:
post:
tags:
- shorts
summary: 'Rate my Reel or Short: one public short rated against the real finds on its…'
description: 'Body {"url": "",
"subject": ""}. Reads the clip the way Hook Detector
reads one (its first frames from its own video file, its words heard by ElevenLabs
Scribe v3 only) and rates it against the real finds on its subject: the Short form
search over the last 30 days (the same as GET /v1/shorts/trending), the finds whose
video can be heard, ranked by their multiple over the creator''s usual. A subject left
out is read from the clip. Returns `clip` (first_line, first_word_s, words_per_minute,
longest_pause_s, words, heard_by, not_heard_why, onscreen_text, plays, multiple), five
`reads` in order (stop: the first frame and line; gap: what it holds back; momentum:
the pace; feeling; reach: who it is for), each with verdict strong, moderate or weak,
says, `computed_from` (measured and counted against the finds, never a guessed number)
and `finds`, 1 to 3 ids of the real `finds` that show it, each with its url. A YouTube
Short is rated without its words (not_heard_why says so). Costs 3 credits, taken before
reading and given back on any failure: 422 invalid_link (nothing charged), 404
video_not_found, 422 not_a_short, 422 review_not_possible, 503 model_unavailable. 402
under 3 credits. Takes up to about 90 seconds; set a client timeout of 120 s and an
Idempotency-Key. Limited per account per hour (429). Needs research. MCP:
review_short.'
operationId: review_short_v1_review_short_post
parameters:
- name: Idempotency-Key
in: header
required: false
schema:
anyOf:
- type: string
- type: 'null'
description: 'retry safely: the same key and body within 24 hours answer the saved review (Idempotent-Replayed: true) and charge nothing; the same key with another body is 422 idempotency_key_reused'
title: Idempotency-Key
description: 'retry safely: the same key and body within 24 hours answer the saved review (Idempotent-Replayed: true) and charge nothing; the same key with another body is 422 idempotency_key_reused'
- name: authorization
in: header
required: false
schema:
anyOf:
- type: string
- type: 'null'
description: Bearer hd_... from POST /v1/accounts or POST /v1/keys
title: Authorization
description: Bearer hd_... from POST /v1/accounts or POST /v1/keys
requestBody:
content:
application/json:
schema:
anyOf:
- $ref: '#/components/schemas/ShortReviewIn'
- type: 'null'
title: Body
responses:
'200':
description: Successful Response
content:
application/json:
schema:
$ref: '#/components/schemas/ShortReviewOut'
'401':
description: missing, invalid, revoked or expired API key (code key_expired)
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorOut'
'402':
description: not enough credits
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorOut'
'403':
description: the key lacks the scope this call needs (insufficient_scope), or its credit_limit is spent (key_credit_limit_reached)
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorOut'
'404':
description: 'video_not_found: private, removed or missing; the credits came back'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorOut'
'409':
description: conflict with the account's state
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorOut'
'413':
description: request body over the size cap
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorOut'
'422':
description: invalid_request or invalid_link (nothing charged), not_a_short or review_not_possible (refunded), idempotency_key_reused
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorOut'
'429':
description: rate limited; see Retry-After
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorOut'
'500':
description: our fault; names the request id
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorOut'
'503':
description: model_unavailable, search_unavailable or unavailable; the credits came back
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorOut'
components:
schemas:
ErrorOut:
properties:
detail:
type: string
title: Detail
description: one sentence a person can read
code:
type: string
title: Code
description: stable machine code; see the Errors section of /llms.txt
errors:
anyOf:
- items:
additionalProperties: true
type: object
type: array
- type: 'null'
title: Errors
description: 'on a 422 only: the field level list'
additionalProperties: true
type: object
required:
- detail
- code
title: ErrorOut
examples:
- code: run_not_found
detail: no such run for this account
ShortFind:
properties:
find:
type: string
title: Find
url:
type: string
title: Url
platform:
type: string
enum:
- tiktok
- instagram
- youtube
title: Platform
creator:
type: string
title: Creator
plays:
type: integer
title: Plays
multiple:
anyOf:
- type: number
- type: 'null'
title: Multiple
multiple_basis:
anyOf:
- type: string
- type: 'null'
title: Multiple Basis
cover_url:
anyOf:
- type: string
- type: 'null'
title: Cover Url
first_line:
anyOf:
- type: string
- type: 'null'
title: First Line
description: its first line as Scribe v3 heard it
first_word_s:
anyOf:
- type: number
- type: 'null'
title: First Word S
words_per_minute:
anyOf:
- type: integer
- type: 'null'
title: Words Per Minute
feeling:
anyOf:
- type: string
- type: 'null'
title: Feeling
holds_back:
anyOf:
- type: boolean
- type: 'null'
title: Holds Back
for_whom:
anyOf:
- type: string
- type: 'null'
title: For Whom
additionalProperties: true
type: object
required:
- find
- url
- platform
- creator
- plays
- multiple
- multiple_basis
- cover_url
- first_line
- first_word_s
- words_per_minute
- feeling
- holds_back
- for_whom
title: ShortFind
ShortReviewOut:
properties:
review_id:
type: string
title: Review Id
url:
type: string
title: Url
platform:
type: string
enum:
- tiktok
- instagram
- youtube
title: Platform
video_id:
type: string
title: Video Id
subject:
type: string
title: Subject
subject_from:
type: string
title: Subject From
description: you, or the clip when the subject was read from it
clip:
additionalProperties: true
type: object
title: Clip
description: 'the clip as read: creator, plays, posted_at, duration_s, cover_url, first_line, first_word_s, words_per_minute, longest_pause_s, words (Scribe v3), heard_by, not_heard_why, onscreen_text, multiple, multiple_basis'
reads:
items:
$ref: '#/components/schemas/ShortRead'
type: array
title: Reads
description: 'the five reads in order: stop, gap, momentum, feeling, reach'
finds:
items:
$ref: '#/components/schemas/ShortFind'
type: array
title: Finds
description: the real finds the clip was rated against
finds_from:
type: string
title: Finds From
label:
type: string
title: Label
credits_charged:
type: integer
title: Credits Charged
description: 3 on delivery; a failure refunds them all
credits:
anyOf:
- type: integer
- type: 'null'
title: Credits
description: your balance after this call
created_at:
type: string
title: Created At
additionalProperties: true
type: object
required:
- review_id
- url
- platform
- video_id
- subject
- subject_from
- clip
- reads
- finds
- finds_from
- label
- credits_charged
- created_at
title: ShortReviewOut
examples:
- clip:
creator: zed
duration_s: 35
first_line: Stop scrolling if you sleep badly.
first_word_s: 0.4
heard_by: scribe_v3
longest_pause_s: 0.6
onscreen_text: STOP SCROLLING
plays: 467744
posted_at: '2026-09-23T10:00:00.000000+00:00'
words: Stop scrolling if you sleep badly. ...
words_per_minute: 168
created_at: '2026-09-23T10:00:00.000000+00:00'
credits: 437
credits_charged: 3
finds:
- creator: ana
feeling: curiosity
find: f1
first_line: Why do you wake up at 3 a.m.?
first_word_s: 0.3
for_whom: people who wake at night
holds_back: true
multiple: 10.0
multiple_basis: likes_per_video
platform: tiktok
plays: 2000000
url: https://www.tiktok.com/@ana/video/7430000000000000001
words_per_minute: 182
finds_from: 'the Short form search on "sleeping better": ...'
label: 'A read of your clip against real finds on its subject: it counts what they did and never guesses your views'
platform: instagram
reads:
- cited_by: model
computed_from: first word at 0.4 s; 4 of 5 finds speak within 1 s (their median 0.5 s)
finds:
- f1
- f3
label: The stop
read: stop
says: It speaks at once and names the viewer.
verdict: strong
review_id: 1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d
subject: sleeping better
subject_from: the clip
url: https://www.instagram.com/reel/Dd1zdF1yXBH/
video_id: Dd1zdF1yXBH
TrendingItem:
properties:
video_id:
type: string
title: Video Id
platform:
type: string
enum:
- tiktok
- instagram
- youtube
title: Platform
url:
type: string
title: Url
description: the video's public https link
cover_url:
anyOf:
- type: string
- type: 'null'
title: Cover Url
description: the platform's own still; it can expire
creator:
type: string
title: Creator
caption:
anyOf:
- type: string
- type: 'null'
title: Caption
description: the first 200 characters of the caption
plays:
type: integer
title: Plays
posted_at:
type: string
title: Posted At
description: when it was posted, ISO 8601 UTC
posted_from:
type: string
title: Posted From
description: post, or media_id when an Instagram reel's search row stated no time and its media id dated it
age_hours:
type: number
title: Age Hours
description: hours since posting
plays_per_hour:
type: integer
title: Plays Per Hour
description: plays over the hours since posting, at least one
multiple:
anyOf:
- type: number
- type: 'null'
title: Multiple
description: plays over the creator's usual; null when the usual could not be measured
multiple_basis:
anyOf:
- type: string
- type: 'null'
title: Multiple Basis
description: 'median_views or likes_per_video: what the multiple is computed from'
creator_usual:
anyOf:
- type: integer
- type: 'null'
title: Creator Usual
description: the creator's usual plays the multiple divides by
duration_s:
anyOf:
- type: integer
- type: 'null'
title: Duration S
spoken_hook:
anyOf:
- type: string
- type: 'null'
title: Spoken Hook
description: the opening line, when one of your own Short form runs delivered this video, or Scribe v3 heard it
spoken_hook_from:
anyOf:
- type: string
- type: 'null'
title: Spoken Hook From
description: your_run or heard
additionalProperties: true
type: object
required:
- video_id
- platform
- url
- cover_url
- creator
- caption
- plays
- posted_at
- posted_from
- age_hours
- plays_per_hour
- multiple
- multiple_basis
- creator_usual
- duration_s
- spoken_hook
- spoken_hook_from
title: TrendingItem
ShortRead:
properties:
read:
type: string
title: Read
description: stop, gap, momentum, feeling or reach
label:
type: string
title: Label
verdict:
type: string
enum:
- strong
- moderate
- weak
title: Verdict
says:
type: string
title: Says
computed_from:
type: string
title: Computed From
description: what was measured and counted against the finds, never a guessed number
finds:
items:
type: string
type: array
title: Finds
description: 1 to 3 ids from `finds` that show it
cited_by:
type: string
title: Cited By
description: model, or measure when the model named no real find and the read's own measure chose them
additionalProperties: true
type: object
required:
- read
- label
- verdict
- says
- computed_from
- finds
- cited_by
title: ShortRead
ShortReviewIn:
properties:
url:
anyOf:
- type: string
- type: 'null'
title: Url
description: a public Instagram Reel, TikTok video or YouTube Short link, starting with https://
examples:
- https://www.instagram.com/reel/Dd1zdF1yXBH/
subject:
anyOf:
- type: string
- type: 'null'
title: Subject
description: what the clip is about, 2 to 120 characters; left out, it is read from the clip
examples:
- sleeping better
type: object
title: ShortReviewIn
TrendingShortsOut:
properties:
subject:
type: string
title: Subject
platform:
anyOf:
- type: string
enum:
- tiktok
- instagram
- youtube
- type: 'null'
title: Platform
description: the platform asked for; null for all three
window:
type: string
title: Window
description: 24h, 7d or 30d
items:
items:
$ref: '#/components/schemas/TrendingItem'
type: array
title: Items
description: fastest first, by plays per hour; at most 20, at most 2 per creator
count:
type: integer
title: Count
computed_from:
type: string
title: Computed From
description: what plays per hour and the multiple are computed from
fetched_at:
type: string
title: Fetched At
description: when the providers were asked
cached:
type: boolean
title: Cached
description: true when this feed came from the last 30 minutes' fetch of the same subject, platform and window
unavailable:
items:
type: string
type: array
title: Unavailable
description: platforms whose search did not answer
credits_charged:
type: integer
title: Credits Charged
description: 'always 0: the feed is free'
additionalProperties: true
type: object
required:
- subject
- platform
- window
- items
- count
- computed_from
- fetched_at
- cached
- unavailable
- credits_charged
title: TrendingShortsOut
examples:
- cached: false
computed_from: 'plays per hour: plays over the hours since posting ...'
count: 1
credits_charged: 0
fetched_at: '2026-09-23T10:00:00.000000+00:00'
items:
- age_hours: 20.0
caption: the 3 a.m. wake up, explained
cover_url: https://p16-sign.tiktokcdn.com/obj/cover.jpeg
creator: ana
creator_usual: 200000
duration_s: 31
multiple: 10.0
multiple_basis: likes_per_video
platform: tiktok
plays: 2000000
plays_per_hour: 100000
posted_at: '2026-09-23T10:00:00.000000+00:00'
posted_from: post
url: https://www.tiktok.com/@ana/video/7430000000000000001
video_id: '7430000000000000001'
subject: sleep
unavailable: []
window: 7d