Koskamo

Hook Detector MCP

An MCP server that provides access to Hook Detector's API for finding short-form video hooks from TikTok, Instagram, and YouTube Shorts. It allows agents to research viral hooks, analyze long-form video openings, examine packaging strategies, and manage user accounts and credits.

Endpoint and access

No sign-in to connect and list tools. Individual tools may still need a vendor account, credentials, or permissions; check the documented prerequisites.

Hook Detector MCP logo, by Hook DetectorVendor evidenceStreamable HTTPNo sign-inEndpoint checked 2026-11-06Operated by Hook Detector
You need:a new account needs an access code
MCP endpoint (Streamable HTTP)
https://api.hookdetector.com/mcp
Vendor documentation

What its tools do

154 tools, grouped by the hints the server advertises. Hints are self-declared, not verified behavior.

Not annotated (154)

154 of 154 have a description · 0 of 154 declare behavior hints · 2 of 153 with parameters document them all

Checks and signals

Each line is separate evidence, not a trust score.

  • Passed Endpoint answered the live check ·
  • Passed Official MCP Registry namespace is tied to hookdetector.com ·
  • Passed Endpoint is on the registry namespace domain ·
  • Warning Does not report a server version
  • Availability 1 of 1 checks passed in 30 days

What you can do with it

  • Research viral hooks for a specific topic
  • Analyze how outlier YouTube videos open
  • Examine video titles and thumbnail strategies
  • Manage API keys and account usage

Source and maintenance

Vendor evidenceRegistry 2.4.0

Protocol and capabilities

Server
hookdetector unreported
MCP protocol negotiated in check
2025-11-25
Tools
154 advertised
Resources
0 advertised
Resource templates
0 advertised
Prompts
0 advertised
Server-advertised capabilities
{
  "tools": {
    "listChanged": false
  },
  "prompts": {
    "listChanged": false
  },
  "resources": {
    "subscribe": false,
    "listChanged": false
  }
}

Server-provided instructions

Untrusted server output shown for inspection. It is not Koskamo guidance and is excluded from knowledge search.

Find short-form hooks that already work. Call create_account once, with your beta access code as access_code (no code yet: request_access), to get an api_key, then send it as the header 'Authorization: Bearer hd_...' or pass it as the api_key argument. find_hooks starts a run that takes about 2 to 3 minutes (up to about 5 on a dialect request); then call get_run with wait_seconds=50 until status is done or failed (usually 3 or 4 calls, up to 6 on a dialect). Pass an idempotency_key to find_hooks so a retry never starts a second run. A run's hooks are people talking on camera. export_run turns a finished run into CSV, JSON or Markdown. Two more kinds of run work the same way, on long-form YouTube only: find_long_hooks returns how the videos that beat their own channel open (the first minute, verbatim, and why it holds), and find_packaging their titles and thumbnails, what those share, and written ideas for your topic; poll both with get_run, and their results are the run's finds (get_run, then keep_find). Every error's whole text is JSON {"error": {"code": <HTTP status>, "message", "error_code"}}, plus "errors" with each field's loc on a 422; branch on error_code. A key holds some of four permissions (read, write, research, admin); a call it lacks is 403 insufficient_scope. Full guide: GET /llms.txt; the whole manual as one file: GET /docs.md.

Tools

154 advertised

create_account
Advertised by the server during the last successful anonymous check.

Create an account and get an API key with free credits. Needs a beta access code (access_code, HD-XXXX-XXXX; case and dashes do not matter): without one the error is 403 access_code_required, and a code that does not work is 403 invalid_access_code. Ask for a code with request_access, or at hookdetector.com/access. No key needed for this call. label is an optional note up to 120 characters. Keep the api_key it returns: it is the only copy. Send it as the header 'Authorization: Bearer hd_...' or pass it as api_key on every other tool. Signups, and wrong codes, are limited per address per hour. Next step: find_hooks.

create_account input schema
{
  "type": "object",
  "title": "create_accountArguments",
  "properties": {
    "label": {
      "anyOf": [
        {
          "type": "string",
          "maxLength": 120
        },
        {
          "type": "null"
        }
      ],
      "title": "Label",
      "default": null
    },
    "access_code": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Access Code",
      "default": null
    }
  }
}
create_account output schema
{
  "type": "object",
  "title": "create_accountOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
showcase
Advertised by the server during the last successful anonymous check.

Real hooks this service has delivered, for a wait screen: talking heads only, each hook_id, still_url (a JPEG, no key needed), platform, creator, views and line (the banner on the video, else its opening line). The most viewed first, one per creator, only what is already public on TikTok or Instagram: never a topic, an account or a run. No key needed. Cached 10 minutes. Free. REST: GET /v1/showcase?n=12.

showcase input schema
{
  "type": "object",
  "title": "showcaseArguments",
  "properties": {
    "n": {
      "type": "integer",
      "title": "N",
      "default": 12,
      "description": "how many; clamped to 1 to 24"
    }
  }
}
showcase output schema
{
  "type": "object",
  "title": "showcaseOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
find_hooks
Advertised by the server during the last successful anonymous check.

Start hook research on a topic across TikTok, Instagram and YouTube Shorts (Shorts only, never long-form YouTube; platform "youtube"). platforms (since 2026-10-10, any of "tiktok", "instagram", "youtube"; all three when left out) searches and delivers only those: ["instagram"] is Instagram reels only. What it does: searches both platforms, reads what each clip says and shows, and returns ranked hooks. Each hook has its verbatim opening line, most quotable line, main idea, why it travelled, topic, on-screen text, full transcript, a watch link and a vertical player_url (9:16 iframe). Talking heads only (since 2026-09-29): `hooks` holds only clips where a person on camera is talking (talking head, podcast, interview). A clip where nobody talks is dropped; Bonus was removed on 2026-10-08 and `bonus` is always []. Every hook carries format (talking_head, podcast, interview, stage; null on older hooks) and section (always "main"). A run that finds fewer talking heads than count says so in outcome; it never fills hooks with clips that do not talk. Where it comes from (since 2026-09-30): every hook has creator_country (ISO alpha-2 of the creator's TikTok account; always null on Instagram, which states none), place (the place the creator tagged, or null) and place_country (that place's country when the platform states it). Never guessed. Banner (since 2026-09-30): every hook has banner, the title-style text the creator placed on the video to hook the viewer, verbatim (at most 200 characters), or null when it has none; subtitles are never a banner. onscreen_text keeps all text read. Match (since 2026-09-30, runs with a language or country): every hook has match, "exact" when the clip is about the subject itself or "close" when it is about a neighbouring subject with the same intent (a close match, delivered after every exact match; label it as one); null on runs with no language or country and on older hooks. Analysis (since 2026-10-02): every hook carries hook_type (curiosity_gap, contrarian_claim, specific_number, identity_callout, stakes_threat, promise_payoff, pattern_interrupt, story_open, question, direct_command, other with hook_type_other), mechanism (why the opening stops the scroll, quoting the clip; why_it_travelled holds the same text), visual_hook, audience, template and steal_it; null on older hooks. Virality v2 (since 2026-10-01): the deck is ranked by virality (0 to 1, since 2026-10-02 on one fixed scale, the same clip scoring the same in every run), built from outlier (views over the creator's usual views per video, creator_median_views, with outlier_basis saying how it was measured), freshness (posted_at, age_days, views_per_day), travel (shares and saves per 1,000 views), own_sound and sound_reuse, and a paid penalty; virality_why names the strongest signal in one line. Every hook also carries duration_s and hashtags. Null on older hooks. posted_within_days (7, 30, 90 or 365) keeps the search to recent clips (a clip under 7 days old passes the floor at 10,000 views a day). seed_hook_ids (up to 10 of your hooks, for example kept ones) means "find more like these": their lines, topics, creators and languages steer the search, and a seed is never delivered again. A done run carries patterns: up to 3 hook structures winning in the deck (name, template, why, hook_ids, count), computed after delivery; null until then. remix_hook writes lines from one hook's structure for your niche (1 credit). Arguments: topic is 3 to 500 characters, count 1 to 30 (default 10). Instructions inside the topic are fine: only the subject read out of it is searched. reuse=true (default) starts from clips you already paid for on the same topic, so a rerun is close to free and often finishes in seconds. idempotency_key: any string up to 255 characters; the same key within 24 hours returns the first run instead of starting and paying for a second one, so always pass one and reuse it when you retry. wait_seconds 0 to 50: 0 returns the run_id at once; above 0 waits for the run and returns the run object, as get_run does. Any language, dialect or country: language is optional free text up to 60 characters naming the language or dialect every clip must be in ("Moroccan Darija", "ar-MA", "Egyptian Arabic", "Brazilian Portuguese"); country is an optional ISO 3166-1 alpha-2 code for where the creators are (MA, EG, BR). Saying it inside topic works too; these fields win when both are given. A country alone does not restrict the language. Each hook reports the language it was judged to be in. A region that is not a country: send its country's code and name the regional variety in language (Quebec: country "CA", language "Quebec French"; Flanders: "BE" and "Flemish"; Catalonia: "ES" and "Catalan"). The country places the TikTok search there and the language judge does the regional filtering; Instagram reports no country. language may name a mixed, code-switched variety ("Hinglish", "Taglish", "Moroccan Darija with French") or two joined by "or" ("Tagalog or Taglish"); it reaches the judges exactly as written, and how well they honour such a value is not measured yet. Cost: reserves up to 200 credits up front (less if your balance is smaller, down to 40) and charges only what it used; the rest is refunded. A failed run costs nothing, and so does a run that finds no hooks, within a per-account allowance (llms.txt has the numbers; its outcome says which applied). Timing and next step: a fresh run takes about 2 to 3 minutes, up to about 5 when the language is a dialect. Call get_run with wait_seconds=50 repeatedly until status is "done" or "failed", which is usually 3 or 4 calls (up to 6 on a dialect request). Watch progress.stage and progress.message meanwhile. Needs a key with the research permission (403 insufficient_scope otherwise). A key with a credit_limit reserves at most what is left of it; under the minimum it is 403 key_credit_limit_reached and nothing is reserved.

find_hooks input schema
{
  "type": "object",
  "title": "find_hooksArguments",
  "required": [
    "topic"
  ],
  "properties": {
    "count": {
      "type": "integer",
      "title": "Count",
      "default": 10,
      "maximum": 30,
      "minimum": 1,
      "description": "how many hooks to deliver, 1 to 30"
    },
    "reuse": {
      "type": "boolean",
      "title": "Reuse",
      "default": true,
      "description": "start from clips you already paid for"
    },
    "topic": {
      "type": "string",
      "title": "Topic",
      "examples": [
        "short term rental tax strategy"
      ],
      "maxLength": 500,
      "minLength": 3,
      "description": "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."
    },
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "country": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Country",
      "default": null,
      "pattern": "^[A-Za-z]{2}$",
      "examples": [
        "MA"
      ],
      "description": "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."
    },
    "language": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Language",
      "default": null,
      "examples": [
        "Moroccan Darija"
      ],
      "maxLength": 60,
      "description": "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."
    },
    "platforms": {
      "type": [
        "array",
        "null"
      ],
      "items": {
        "enum": [
          "tiktok",
          "instagram",
          "youtube"
        ],
        "type": "string"
      },
      "title": "Platforms",
      "default": null,
      "examples": [
        [
          "instagram"
        ]
      ],
      "maxItems": 3,
      "minItems": 1,
      "description": "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`.",
      "uniqueItems": true
    },
    "wait_seconds": {
      "type": "integer",
      "title": "Wait Seconds",
      "default": 0,
      "maximum": 50,
      "minimum": 0,
      "description": "0 to 50: seconds to wait for the run to finish; 0 returns at once"
    },
    "seed_hook_ids": {
      "type": [
        "array",
        "null"
      ],
      "items": {
        "type": "string",
        "format": "uuid"
      },
      "title": "Seed Hook Ids",
      "default": null,
      "maxItems": 10,
      "description": "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.",
      "uniqueItems": true
    },
    "conversation_id": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Conversation Id",
      "default": null,
      "description": "attach the run to one of your conversations"
    },
    "idempotency_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Idempotency Key",
      "default": null,
      "description": "1 to 255 visible ASCII characters; the same key within 24 hours returns the first run instead of paying for a second"
    },
    "posted_within_days": {
      "enum": [
        7,
        30,
        90,
        365,
        null
      ],
      "type": [
        "integer",
        "null"
      ],
      "title": "Posted Within Days",
      "default": null,
      "examples": [
        30
      ],
      "description": "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."
    }
  }
}
find_hooks output schema
{
  "type": "object",
  "title": "find_hooksOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
get_run
Advertised by the server during the last successful anonymous check.

A research run: its status, progress, charge and every hook it produced. wait_seconds 0 to 50: wait up to that long for the run to finish, returning the moment it is done or failed. Use wait_seconds=50 and call again while status is "queued" or "running"; a fresh run needs about 3 or 4 such calls. progress has stage, message and updated_at, plus counts once the research reports them, preview (hooks written so far) while a running run holds one, and feed (the live research wall: up to 24 clips the run is working on, newest first, each key, platform, creator, views, state found|reading|talking|passed, thumb_url, a JPEG any <img> can load with no key, and line, the banner or opening words once the judges passed the clip) while it shows one, and on a Long form or Packaging run scan (the videos it is looking at: up to 60 tiles {v: YouTube video id, s: found|on_topic|off_topic|measured|beat| reading|ready|passed, x: multiple of the channel's usual or null}, oldest first, and lanes with found, on_topic, measured, beat, reading, ready; draw a tile from https://i.ytimg.com/vi/{v}/mqdefault.jpg; counts.measured and counts.beat are the numbers report.landscape ends on). Pass since with the last progress.updated_at to return on change or completion. It must be an ISO timestamp with a timezone (422 invalid_request otherwise). `hooks` is the deck (a person talking); `bonus` is always [] (removed 2026-10-08). Every hook has format and section (always "main"), and creator_country (TikTok only), place and place_country (null when unknown), banner (the title placed on the video, null when none) and match ("exact" or "close", a close match being a neighbouring subject delivered after the exact ones; null on runs with no language or country and on older hooks). Since 2026-10-04 the run carries understood (what the request was read to ask: subject, language, country, core, audience, neighbours, styles, style_other, wants; null until read) and, on a run that asked for a kind of opening ("controversial hooks", "storytime"), every hook carries style_match (true when it opens that way; null otherwise). Since 2026-10-01 every hook carries the virality v2 fields: virality (0 to 1 on one fixed scale since 2026-10-02), virality_why, outlier, outlier_basis, creator_median_views, posted_at, age_days, views_per_day, duration_s, paid, sound_reuse, own_sound, hashtags (null on older hooks), and since 2026-10-02 the analysis: hook_type, hook_type_other, mechanism, visual_hook, audience, template, steal_it; the run echoes posted_within_days and seed_hook_ids, and once done carries patterns: up to 3 hook structures winning in this deck, each name, template, why, hook_ids and count, grouped by hook_type and computed before the run is done, in the deck's language (null on older runs and when nothing is shared). Hooks include transcript_kind: speech, music, none, other, or null when unknown; and heard: false when our speech service could not hear the clip right now (no spoken words shown, transcript null; the done message then counts them), true otherwise. conversation_id names the chat conversation the run belongs to (null outside one). A done run with fewer hooks than asked for, or none, has outcome: why in one plain paragraph, what to try next, and whether it was free (stats.rejections holds the counts); a full run's outcome is null, or one sentence when some hooks are under the view floor because the country filter set clips aside. Free: reading a run costs no credits.

get_run input schema
{
  "type": "object",
  "title": "get_runArguments",
  "required": [
    "run_id"
  ],
  "properties": {
    "since": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Since",
      "default": null
    },
    "run_id": {
      "type": "string",
      "title": "Run Id"
    },
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "wait_seconds": {
      "type": "integer",
      "title": "Wait Seconds",
      "default": 0,
      "maximum": 50,
      "minimum": 0,
      "description": "0 to 50: seconds to wait for the run to finish; 0 returns at once"
    }
  }
}
get_run output schema
{
  "type": "object",
  "title": "get_runOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
list_runs
Advertised by the server during the last successful anonymous check.

Your research runs, newest first, without their results, limit 1 to 100 (default 20) per page. Each run says its kind: "short" (find_hooks), "long" (find_long_hooks) or "packaging" (find_packaging); pass kind to list only one. Returns {"runs": [...], "next_cursor"}: pass next_cursor back as cursor for the next page; it is null on the last. Same shape as GET /v1/runs. Next step: open one with get_run, or download it with export_run. Free. Errors: 422 invalid_cursor for a cursor this API did not issue, 422 for a kind that is not one of the three.

list_runs input schema
{
  "type": "object",
  "title": "list_runsArguments",
  "properties": {
    "kind": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Kind",
      "default": null,
      "description": "only runs of this kind: short, long or packaging; leave out for every kind"
    },
    "limit": {
      "type": "integer",
      "title": "Limit",
      "default": 20,
      "maximum": 100,
      "minimum": 1,
      "description": "page size, 1 to 100"
    },
    "cursor": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Cursor",
      "default": null
    },
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    }
  }
}
list_runs output schema
{
  "type": "object",
  "title": "list_runsOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
find_long_hooks
Advertised by the server during the last successful anonymous check.

Find long-form hooks: how the YouTube videos that beat their own channel open, on a topic. Long-form YouTube only, never Shorts, TikTok or Instagram (for those: find_hooks). What it does: searches YouTube for long-form videos on the topic, keeps only the outliers (a video whose views are at least min_outlier times the median views of its own channel's recent uploads; 2 by default) posted in 2023 or later (nothing older is ever delivered), reads the first intro_seconds (60 by default) of each one's transcript, and says why that opening holds a viewer. What comes back: the run's `finds`, ranked, strongest outlier first. Each has title, channel, watch_url, player_url (an iframe src), thumbnail_url, views, channel_median_views, outlier (28.2 means 28 times the channel's usual), outlier_score (0 to 100 on one fixed scale: nothing else a run found moves it, so finds from different runs rank together), outlier_tier, channel_size, likes_per_1k, posted_at, duration_s, and the opening: intro (the first minute as it was said: lines of n, t in seconds, text) and intro_text, always whole; and inside it the hook (verbatim: the lines the analysis marks as the hook, ending on a sentence end, at most 25 seconds and 60 words, with hook_words, hook_start_s and hook_end_s, a sponsor read or greeting at the front left out; hook_seconds is hook_end_s). auto_captions says the opening has almost no punctuation. about_subject is yes or partly: a video the model judges not to be about the subject is dropped. The analysis: hook_type (the same list as find_hooks), mechanism (why it holds, quoting the opening word for word), promise, open_loop, title_link (how the first lines pay off the title), beats (the opening in parts: from, to, beat, does), opens_with (cold_open, greeting, teaser, question, sponsor), format (the kind of video), angle, template (the hook as a fill-in structure), steal_it and audience. When the topic asks for a kind of opening ("question hooks", "storytime"), style_match says whether each find is that kind and the matches come first. analysed is false when the model could not analyse an opening: the opening is still real, its analysis fields are null. The run's report.patterns lists the opening types at least two finds share; report.shape says how the openings are built, counted from the finds (hook_seconds, how long the hook runs, and hook_words as medians, beats with how many openings use each and the second it usually starts_at, and the sequence of beats most of them share); and report.openers holds up to `openers` (3 by default, 0 for none) openings WRITTEN for your topic from those structures (hook, then, hook_type, based_on). report.openers_label is always "Written by Hook Detector from the structure of these real openings": show it with them, never present one as a real video's opening. report.landscape is the subject at a glance, counted from the videos the run held against their own channel (videos, channels, outliers and their share, breakouts, small_channel_outliers, recent_outliers). The run's hooks and bonus are always empty. A link instead of a subject: when topic is a channel's YouTube address (https://www.youtube.com/@name, or just @name) the run returns that channel's OWN outliers, its recent long-form uploads that beat its usual (one provider call, no search); when it is a long-form video's address the run returns that one video, whatever its multiple (requested is 1). options.link says "channel" or "video". No openings are written for a link (use remix_find on a find to write for your niche). A Short's address is refused with 422. Arguments: topic 3 to 500 characters; count 1 to 30 (default 8); language and country optional; posted_within_days 30, 90, 365 or 730 to narrow the 2023 floor further; channel_size "small", "medium" or "large" to keep only channels of that size (by what their uploads usually get), so a small creator sees breakouts from channels their own size; reuse=true (default) starts from the videos your last long or packaging run on this topic bought within a week, and a video you already had read is delivered from that read (analysis_from_earlier_read) at no model or provider cost. seen_before says an earlier run of yours already found the video. idempotency_key: always pass one and reuse it on a retry. wait_seconds 0 to 50: 0 returns the run_id at once; above 0 waits and returns the run. Starts are limited per account per hour (429 rate_limited). Cost: reserves up to 90 credits for 20 videos or fewer and 5 more for each video above that (140 at 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 a run that finds nothing, inside the same allowance a hook run has (the outcome says so). Timing and next step: how long a run takes has not been measured yet, so never assume a time. Call get_run with wait_seconds=50 until status is done or failed; progress.items holds the finds written so far as cards and progress.scan the videos it is looking at while it runs. Then keep_find, export_run. Same as POST /v1/longform. Needs the research permission (403 insufficient_scope otherwise).

find_long_hooks input schema
{
  "type": "object",
  "title": "find_long_hooksArguments",
  "required": [
    "topic"
  ],
  "properties": {
    "count": {
      "type": "integer",
      "title": "Count",
      "default": 8,
      "maximum": 30,
      "minimum": 1,
      "description": "how many long-form hooks to deliver, 1 to 30"
    },
    "reuse": {
      "type": "boolean",
      "title": "Reuse",
      "default": true,
      "description": "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"
    },
    "topic": {
      "type": "string",
      "title": "Topic",
      "examples": [
        "home espresso",
        "https://www.youtube.com/@name"
      ],
      "maxLength": 500,
      "minLength": 3,
      "description": "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)"
    },
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "country": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Country",
      "default": null,
      "pattern": "^[A-Za-z]{2}$",
      "examples": [
        "US"
      ],
      "description": "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"
    },
    "filters": {
      "type": [
        "object",
        "null"
      ],
      "title": "Filters",
      "default": null,
      "examples": [
        {
          "max_views": 2000000,
          "min_multiple": 5
        }
      ],
      "description": "optional bounds, every one optional: min_multiple and max_multiple (views over the channel's median, 1 to 1000), min_views, max_views, min_channel_median, max_channel_median, min_duration_s, max_duration_s; on the Library (and a library preset) also min_comments, max_comments, posted_within_days (7, 30, 90, 365, 730) and language. No delivered find falls outside one; a min_multiple above the bar raises it"
    },
    "openers": {
      "type": [
        "integer",
        "null"
      ],
      "title": "Openers",
      "default": null,
      "maximum": 6,
      "minimum": 0,
      "examples": [
        3
      ],
      "description": "optional, 0 to 6 (default 3): how many openings to write for the subject from the structure of the real ones; 0 for none"
    },
    "language": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Language",
      "default": null,
      "examples": [
        "Spanish"
      ],
      "maxLength": 60,
      "description": "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"
    },
    "preset_id": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Preset Id",
      "default": null,
      "description": "optional: start from a saved preset of this tab (save_preset); filters sent with it win over the preset's"
    },
    "min_outlier": {
      "type": [
        "number",
        "null"
      ],
      "title": "Min Outlier",
      "default": null,
      "maximum": 100,
      "minimum": 1.5,
      "examples": [
        3
      ],
      "description": "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"
    },
    "channel_size": {
      "enum": [
        "small",
        "medium",
        "large",
        null
      ],
      "type": [
        "string",
        "null"
      ],
      "title": "Channel Size",
      "default": null,
      "examples": [
        "small"
      ],
      "description": "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"
    },
    "wait_seconds": {
      "type": "integer",
      "title": "Wait Seconds",
      "default": 0,
      "maximum": 50,
      "minimum": 0,
      "description": "0 to 50: seconds to wait for the run to finish; 0 returns at once"
    },
    "intro_seconds": {
      "type": [
        "integer",
        "null"
      ],
      "title": "Intro Seconds",
      "default": null,
      "maximum": 120,
      "minimum": 20,
      "examples": [
        60
      ],
      "description": "optional, 20 to 120 (default 60): how many seconds of each video's opening to read and return"
    },
    "idempotency_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Idempotency Key",
      "default": null,
      "description": "1 to 255 visible ASCII characters; the same key within 24 hours returns the first run instead of paying for a second"
    },
    "posted_within_days": {
      "enum": [
        30,
        90,
        365,
        730,
        null
      ],
      "type": [
        "integer",
        "null"
      ],
      "title": "Posted Within Days",
      "default": null,
      "examples": [
        365
      ],
      "description": "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"
    }
  }
}
find_long_hooks output schema
{
  "type": "object",
  "title": "find_long_hooksOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
find_packaging
Advertised by the server during the last successful anonymous check.

Find packaging: the titles and thumbnails that earned the click on a topic, from long-form YouTube videos that beat their own channel. What it does: runs the same outlier search as find_long_hooks (long-form YouTube, 2023 or later, views at least min_outlier times the channel's usual), then looks at each video's thumbnail beside its title and says what the pair is doing. What comes back: the run's `finds`, ranked. Each is a real package, as YouTube shows it: title (verbatim) and thumbnail_url (the size YouTube really served, the picture the analysis looked at; thumbnail_checked says so), channel, watch_url, views, channel_median_views, outlier, outlier_score (0 to 100 on one fixed scale, so finds from different runs rank together), outlier_tier, channel_size and posted_at, plus the analysis: title_device (curiosity_gap, contrarian_claim, specific_number, how_to, listicle, challenge, story, question, warning, comparison, authority, timeliness, identity_callout, other), title_template (the title as a fill-in structure), thumbnail_text (the words printed on the thumbnail, read by a vision model; null when none), thumbnail_text_role (repeats_title, adds_to_title, contrasts_title, no_text; counted from title_overlap, the share of the thumbnail's words that are also in the title, where that can be), thumbnail_subject, thumbnail_faces, thumbnail_emotion, thumbnail_colors, thumbnail_composition, thumbnail_devices, angle, gap (what the picture shows that the title does not say, and the question that leaves), promise, steal_it and audience, visible_action (the single thing happening in the picture), title_carries and picture_adds (what only the title says, what only the picture adds), text_box, and thumb_metrics (measured in code from the pixels: palette, brightness, contrast, saturation, busyness, the biggest words' height at 210 px wide and whether the length corner is clear), plus cut_on_phone (the title is longer than 60 characters, where a phone roughly cuts it; approximate) and on_subject (yes or partly: whether the look judged the package to be about your topic; a package judged not to be is dropped and the next one takes its slot; null on a link run). analysed is false when a package could not be looked at (its look is asked twice, and the next candidate takes the slot if it fails twice); it is still a real package. The run's report then holds title_patterns and thumbnail_patterns (what at least two of the packages share, each naming their video_ids) and ideas: up to `ideas` (6 by default, 0 for none) packages WRITTEN for your topic from those patterns, each a title a thumbnail concept and thumbnail_text (the words to print on it), naming the real packages it is based_on. report.ideas_label is always "Written by Hook Detector from the patterns in these real packages": show it with them, never present an idea as a real video's title; an idea over 70 characters, with more than 5 words for its thumbnail or repeating a real title is dropped. report.numbers counts the packages: title_chars and title_words (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 (name, count); age_cohorts repeats the counts for packages posted in the last split_days days (recent) and for older ones, with the undated apart. report.landscape is the subject at a glance, as on find_long_hooks. Arguments: topic 3 to 500 characters, or a channel's or a long-form video's YouTube address exactly as on find_long_hooks (a channel's own outliers, or that one video; no ideas are written for a link, use remix_find); count 1 to 30 (default 12); language, country, min_outlier, posted_within_days, channel_size and reuse as on find_long_hooks; idempotency_key and wait_seconds as on find_hooks; content_type "short" finds the packages of YouTube Shorts instead of long-form videos ("long", the default), each Short measured against its own channel's Shorts. Every find carries multiple (views over the channel's median, the number 1of10 and vidIQ show), multiple_band (10x, 5x, 2x, under 2x) and content_type. Cost: reserves up to 70 credits for 24 packages or fewer and 3 more for each package above that (88 at 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 no credit. After a find_long_hooks run on the same topic it is close to free. A failed run costs nothing, and so does a run that finds nothing, inside the allowance. Timing and next step: how long a run takes has not been measured yet, so never assume a time. Call get_run with wait_seconds=50 until status is done or failed; progress.items holds the packages looked at so far as cards and progress.scan the videos it is looking at while it runs. Same as POST /v1/packaging. Needs the research permission.

find_packaging input schema
{
  "type": "object",
  "title": "find_packagingArguments",
  "required": [
    "topic"
  ],
  "properties": {
    "count": {
      "type": "integer",
      "title": "Count",
      "default": 12,
      "maximum": 30,
      "minimum": 1,
      "description": "how many packages to deliver, 1 to 30"
    },
    "ideas": {
      "type": [
        "integer",
        "null"
      ],
      "title": "Ideas",
      "default": null,
      "maximum": 10,
      "minimum": 0,
      "examples": [
        6
      ],
      "description": "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": {
      "type": "boolean",
      "title": "Reuse",
      "default": true,
      "description": "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"
    },
    "topic": {
      "type": "string",
      "title": "Topic",
      "examples": [
        "home espresso",
        "https://www.youtube.com/@name"
      ],
      "maxLength": 500,
      "minLength": 3,
      "description": "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)"
    },
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "country": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Country",
      "default": null,
      "pattern": "^[A-Za-z]{2}$",
      "examples": [
        "US"
      ],
      "description": "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"
    },
    "filters": {
      "type": [
        "object",
        "null"
      ],
      "title": "Filters",
      "default": null,
      "examples": [
        {
          "max_views": 2000000,
          "min_multiple": 5
        }
      ],
      "description": "optional bounds, every one optional: min_multiple and max_multiple (views over the channel's median, 1 to 1000), min_views, max_views, min_channel_median, max_channel_median, min_duration_s, max_duration_s; on the Library (and a library preset) also min_comments, max_comments, posted_within_days (7, 30, 90, 365, 730) and language. No delivered find falls outside one; a min_multiple above the bar raises it"
    },
    "language": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Language",
      "default": null,
      "examples": [
        "Spanish"
      ],
      "maxLength": 60,
      "description": "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"
    },
    "preset_id": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Preset Id",
      "default": null,
      "description": "optional: start from a saved preset of this tab (save_preset); filters sent with it win over the preset's"
    },
    "min_outlier": {
      "type": [
        "number",
        "null"
      ],
      "title": "Min Outlier",
      "default": null,
      "maximum": 100,
      "minimum": 1.5,
      "examples": [
        3
      ],
      "description": "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"
    },
    "channel_size": {
      "enum": [
        "small",
        "medium",
        "large",
        null
      ],
      "type": [
        "string",
        "null"
      ],
      "title": "Channel Size",
      "default": null,
      "examples": [
        "small"
      ],
      "description": "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"
    },
    "content_type": {
      "enum": [
        "long",
        "short",
        null
      ],
      "type": [
        "string",
        "null"
      ],
      "title": "Content Type",
      "default": null,
      "examples": [
        "short"
      ],
      "description": "optional: \"long\" (long-form videos, the default) or \"short\" (YouTube Shorts): the kind of video whose packages to find. A Short is measured against its own channel's Shorts"
    },
    "wait_seconds": {
      "type": "integer",
      "title": "Wait Seconds",
      "default": 0,
      "maximum": 50,
      "minimum": 0,
      "description": "0 to 50: seconds to wait for the run to finish; 0 returns at once"
    },
    "idempotency_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Idempotency Key",
      "default": null,
      "description": "1 to 255 visible ASCII characters; the same key within 24 hours returns the first run instead of paying for a second"
    },
    "posted_within_days": {
      "enum": [
        30,
        90,
        365,
        730,
        null
      ],
      "type": [
        "integer",
        "null"
      ],
      "title": "Posted Within Days",
      "default": null,
      "examples": [
        365
      ],
      "description": "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"
    }
  }
}
find_packaging output schema
{
  "type": "object",
  "title": "find_packagingOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
save_preset
Advertised by the server during the last successful anonymous check.

Save filters under a name for one tab: "long", "packaging" or "library" (the bounds are those of find_packaging's filters; a library preset may also hold min_comments, max_comments, posted_within_days and language). The same name on the same tab replaces it. Start a run from it with preset_id on find_long_hooks or find_packaging. At most 50 an account. Same as POST /v1/presets. Free.

save_preset input schema
{
  "type": "object",
  "title": "save_presetArguments",
  "required": [
    "name",
    "tab"
  ],
  "properties": {
    "tab": {
      "type": "string",
      "title": "Tab"
    },
    "name": {
      "type": "string",
      "title": "Name"
    },
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "filters": {
      "type": [
        "object",
        "null"
      ],
      "title": "Filters",
      "default": null,
      "examples": [
        {
          "max_views": 2000000,
          "min_multiple": 5
        }
      ],
      "description": "optional bounds, every one optional: min_multiple and max_multiple (views over the channel's median, 1 to 1000), min_views, max_views, min_channel_median, max_channel_median, min_duration_s, max_duration_s; on the Library (and a library preset) also min_comments, max_comments, posted_within_days (7, 30, 90, 365, 730) and language. No delivered find falls outside one; a min_multiple above the bar raises it"
    }
  }
}
save_preset output schema
{
  "type": "object",
  "title": "save_presetOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
list_presets
Advertised by the server during the last successful anonymous check.

Your saved filter presets, the newest change first; tab ("long", "packaging", "library") narrows. Same as GET /v1/presets. Free.

list_presets input schema
{
  "type": "object",
  "title": "list_presetsArguments",
  "properties": {
    "tab": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Tab",
      "default": null
    },
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    }
  }
}
list_presets output schema
{
  "type": "object",
  "title": "list_presetsOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
delete_preset
Advertised by the server during the last successful anonymous check.

Delete one of your presets. Same as DELETE /v1/presets/{preset_id}. Free. Errors: 404 preset_not_found.

delete_preset input schema
{
  "type": "object",
  "title": "delete_presetArguments",
  "required": [
    "preset_id"
  ],
  "properties": {
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "preset_id": {
      "type": "string",
      "title": "Preset Id"
    }
  }
}
delete_preset output schema
{
  "type": "object",
  "title": "delete_presetOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
write_titles
Advertised by the server during the last successful anonymous check.

Write 20 to 40 titles for your video, each borrowing the device and structure of one real outlier (a video that beat its own channel): the finds of one of your runs, or the shared index's videos nearest your topic by meaning. Each title: title, chars, first_40 (what a phone feed surely shows), device, borrows, and from (the real outlier: video_id, title, channel, multiple, find_id). best: the five whose outlier beat its channel the most among the devices the outliers share (best_basis says so; never a predicted click-through rate). label: written by Hook Detector, never a real video's title. Costs 1 credit, refunded when the writing fails; fewer than 5 outliers known is 422 too_few_outliers and costs nothing (run find_packaging on the topic first). Same as POST /v1/titles. Read it back with get_call.

write_titles input schema
{
  "type": "object",
  "title": "write_titlesArguments",
  "properties": {
    "count": {
      "anyOf": [
        {
          "type": "integer"
        },
        {
          "type": "null"
        }
      ],
      "title": "Count",
      "default": null,
      "description": "20 to 40 titles (default 20)"
    },
    "topic": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Topic",
      "default": null,
      "description": "the subject of your video, 3 to 300 characters (the outliers come from the shared index); send this or run_id"
    },
    "run_id": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Run Id",
      "default": null,
      "description": "a finished Packaging or Long form run of yours: its finds are the sources"
    },
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "channel": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Channel",
      "default": null,
      "description": "optional @handle whose style to write in"
    },
    "content_type": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Content Type",
      "default": null,
      "description": "for a topic: long (default) or short"
    },
    "idempotency_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Idempotency Key",
      "default": null,
      "description": "1 to 255 visible ASCII characters; the same key and request within 24 hours answer the saved remix and charge nothing; the same key with another request is 422 idempotency_key_reused"
    }
  }
}
write_titles output schema
{
  "type": "object",
  "title": "write_titlesOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
write_ideas
Advertised by the server during the last successful anonymous check.

Write ideas for new videos: each a title, a thumbnail concept (one visible action) and the words to print, built on one to three real outliers (based_on, each with its video_id, title, multiple, find_id and bend niche when it came from another niche). constraints are rules every idea keeps (said in fits). band (10x, 5x, 2x) is the median multiple of the finds behind the idea, never a predicted view count. label: written by Hook Detector. 1 credit, refunded when the writing fails; fewer than 5 outliers known is 422 too_few_outliers and costs nothing. Same as POST /v1/ideas; remix_find makes one find yours.

write_ideas input schema
{
  "type": "object",
  "title": "write_ideasArguments",
  "properties": {
    "count": {
      "anyOf": [
        {
          "type": "integer"
        },
        {
          "type": "null"
        }
      ],
      "title": "Count",
      "default": null,
      "description": "3 to 12 ideas (default 6)"
    },
    "topic": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Topic",
      "default": null,
      "description": "the subject, 3 to 300 characters (the outliers come from the shared index); send this or run_id"
    },
    "run_id": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Run Id",
      "default": null,
      "description": "a finished Packaging or Long form run of yours: its finds, direct and bent, are the sources"
    },
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "constraints": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Constraints",
      "default": null,
      "description": "rules every idea keeps, up to 200 characters (\"low budget\")"
    },
    "content_type": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Content Type",
      "default": null,
      "description": "for a topic: long (default) or short"
    },
    "idempotency_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Idempotency Key",
      "default": null,
      "description": "1 to 255 visible ASCII characters; the same key and request within 24 hours answer the saved remix and charge nothing; the same key with another request is 422 idempotency_key_reused"
    }
  }
}
write_ideas output schema
{
  "type": "object",
  "title": "write_ideasOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
get_call
Advertised by the server during the last successful anonymous check.

One call of a priced tool (titles and the tools after it): tool, credits, charged, status (pending, done, failed), request and result. Same as GET /v1/calls/{call_id}. Free. Errors: 404 call_not_found.

get_call input schema
{
  "type": "object",
  "title": "get_callArguments",
  "required": [
    "call_id"
  ],
  "properties": {
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "call_id": {
      "type": "string",
      "title": "Call Id"
    }
  }
}
get_call output schema
{
  "type": "object",
  "title": "get_callOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
list_calls
Advertised by the server during the last successful anonymous check.

Your calls of priced tools, newest first; tool ("titles", ...) narrows. Same as GET /v1/calls. Free.

list_calls input schema
{
  "type": "object",
  "title": "list_callsArguments",
  "properties": {
    "tool": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Tool",
      "default": null
    },
    "limit": {
      "type": "integer",
      "title": "Limit",
      "default": 50
    },
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    }
  }
}
list_calls output schema
{
  "type": "object",
  "title": "list_callsOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
find_similar
Advertised by the server during the last successful anonymous check.

The videos in the shared index nearest one of your finds (find_id from a Packaging or Long form run), nearest first, never the find's own video. Each has distance (0 is the same; measures says how it is computed), its numbers and its multiple over its channel's median. A find not indexed yet is matched by its own words (matched_from). Free. Same as GET /v1/finds/{find_id}/similar. Errors: 404 find_not_found.

find_similar input schema
{
  "type": "object",
  "title": "find_similarArguments",
  "required": [
    "find_id"
  ],
  "properties": {
    "type": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Type",
      "default": null,
      "description": "video (default): by the title's meaning on any channel; title: on other channels; thumbnail: by our read of the picture on other channels"
    },
    "limit": {
      "anyOf": [
        {
          "type": "integer"
        },
        {
          "type": "null"
        }
      ],
      "title": "Limit",
      "default": null,
      "description": "1 to 50 (default 12)"
    },
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "find_id": {
      "type": "string",
      "title": "Find Id"
    },
    "content_type": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Content Type",
      "default": null,
      "description": "long or short"
    },
    "min_multiple": {
      "anyOf": [
        {
          "type": "number"
        },
        {
          "type": "null"
        }
      ],
      "title": "Min Multiple",
      "default": null
    },
    "posted_within_days": {
      "anyOf": [
        {
          "type": "integer"
        },
        {
          "type": "null"
        }
      ],
      "title": "Posted Within Days",
      "default": null,
      "description": "30, 90, 365 or 730"
    }
  }
}
find_similar output schema
{
  "type": "object",
  "title": "find_similarOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
search_thumbnails
Advertised by the server during the last successful anonymous check.

Thumbnails in the shared index by what the picture shows (q), or like a picture (image_url: read once by the model as subject, action, words and setting, then searched). Each result has matched_by (text or meaning). Free; image reads at most 60 an hour. Same as GET /v1/index/thumbnails and POST /v1/index/thumbnails/by-image.

search_thumbnails input schema
{
  "type": "object",
  "title": "search_thumbnailsArguments",
  "properties": {
    "q": {
      "anyOf": [
        {
          "type": "string",
          "maxLength": 200
        },
        {
          "type": "null"
        }
      ],
      "title": "Q",
      "default": null,
      "description": "what the picture shows, e.g. person pointing at a chart"
    },
    "limit": {
      "anyOf": [
        {
          "type": "integer"
        },
        {
          "type": "null"
        }
      ],
      "title": "Limit",
      "default": null,
      "description": "1 to 50 (default 12)"
    },
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "image_url": {
      "anyOf": [
        {
          "type": "string",
          "maxLength": 2000
        },
        {
          "type": "null"
        }
      ],
      "title": "Image Url",
      "default": null,
      "description": "instead of q: a public https picture or a YouTube video link; it is read once by the model, then searched"
    },
    "content_type": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Content Type",
      "default": null,
      "description": "long or short"
    },
    "min_multiple": {
      "anyOf": [
        {
          "type": "number"
        },
        {
          "type": "null"
        }
      ],
      "title": "Min Multiple",
      "default": null
    }
  }
}
search_thumbnails output schema
{
  "type": "object",
  "title": "search_thumbnailsOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
score_package
Advertised by the server during the last successful anonymous check.

Score a package against the real outliers on its subject, part by part (title device, length, words on the picture, faces, the words' role, contrast, brightness): each part says how many outliers it matches; pattern_match (0 to 100) is the mean share: how much the package resembles the subject's outliers, never a click-through rate or a prediction (in our test it scored outliers above their channels' usual videos on about 3 of 4 pairs). Also nearest (5 outliers) and fixes. 1 credit, refunded when the read fails. Same as POST /v1/packaging/score.

score_package input schema
{
  "type": "object",
  "title": "score_packageArguments",
  "properties": {
    "title": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Title",
      "default": null,
      "description": "your title, 1 to 150 characters (or video_url)"
    },
    "topic": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Topic",
      "default": null,
      "description": "the subject (the shared index's outliers)"
    },
    "run_id": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Run Id",
      "default": null,
      "description": "a finished run of yours whose outliers to compare with; or topic"
    },
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "thumbnail": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Thumbnail",
      "default": null,
      "description": "optional: your thumbnail as base64 (JPEG, PNG, WebP, at most 4 MB) or an i.ytimg.com URL"
    },
    "video_url": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Video Url",
      "default": null,
      "description": "a published YouTube video to score instead"
    },
    "content_type": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Content Type",
      "default": null,
      "description": "for a topic: long or short"
    },
    "idempotency_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Idempotency Key",
      "default": null,
      "description": "1 to 255 visible ASCII characters; the same key and request within 24 hours answer the saved remix and charge nothing; the same key with another request is 422 idempotency_key_reused"
    }
  }
}
score_package output schema
{
  "type": "object",
  "title": "score_packageOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
preview_package
Advertised by the server during the last successful anonymous check.

Your package in a mock YouTube feed among a run's winners: view home, search, sidebar, phone (marks where a phone cuts the title) or tv; theme dark or light; look normal, grey or blur. Returns png_base64 and mine_at. Free. Same as POST /v1/packaging/preview.

preview_package input schema
{
  "type": "object",
  "title": "preview_packageArguments",
  "required": [
    "title"
  ],
  "properties": {
    "look": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Look",
      "default": null
    },
    "seed": {
      "anyOf": [
        {
          "type": "integer"
        },
        {
          "type": "null"
        }
      ],
      "title": "Seed",
      "default": null
    },
    "view": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "View",
      "default": null
    },
    "theme": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Theme",
      "default": null
    },
    "title": {
      "type": "string",
      "title": "Title"
    },
    "topic": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Topic",
      "default": null
    },
    "run_id": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Run Id",
      "default": null
    },
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "thumbnail": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Thumbnail",
      "default": null
    }
  }
}
preview_package output schema
{
  "type": "object",
  "title": "preview_packageOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
make_thumbnails
Advertised by the server during the last successful anonymous check.

Make 2 to 12 thumbnails (default 4): 3 different concepts first, then images (Nano Banana 2.1, 1K or 4K), every printed word read back and checked letter by letter, a face only from your photos or your channel's own thumbnails. Answers at once with call_id and status pending; read it with get_thumbnails until done. 4 credits a delivered 1K image, 12 a 4K one, charged only for what is delivered. Same as POST /v1/thumbnails.

make_thumbnails input schema
{
  "type": "object",
  "title": "make_thumbnailsArguments",
  "required": [
    "title"
  ],
  "properties": {
    "mode": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Mode",
      "default": null
    },
    "size": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Size",
      "default": null
    },
    "count": {
      "anyOf": [
        {
          "type": "integer"
        },
        {
          "type": "null"
        }
      ],
      "title": "Count",
      "default": null
    },
    "faces": {
      "anyOf": [
        {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        {
          "type": "null"
        }
      ],
      "title": "Faces",
      "default": null
    },
    "title": {
      "type": "string",
      "title": "Title"
    },
    "topic": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Topic",
      "default": null
    },
    "words": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Words",
      "default": null
    },
    "run_id": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Run Id",
      "default": null
    },
    "sketch": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Sketch",
      "default": null
    },
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "channel": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Channel",
      "default": null
    },
    "concept": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Concept",
      "default": null
    },
    "reference": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Reference",
      "default": null
    },
    "background": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Background",
      "default": null
    },
    "idempotency_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Idempotency Key",
      "default": null,
      "description": "1 to 255 visible ASCII characters; the same key and request within 24 hours answer the saved remix and charge nothing; the same key with another request is 422 idempotency_key_reused"
    }
  }
}
make_thumbnails output schema
{
  "type": "object",
  "title": "make_thumbnailsOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
get_thumbnails
Advertised by the server during the last successful anonymous check.

A thumbnails call: status, concepts and every image so far (url signed for 7 days, width, height, words, words_checked, cost_usd). Same as GET /v1/thumbnails/{call_id}. Free.

get_thumbnails input schema
{
  "type": "object",
  "title": "get_thumbnailsArguments",
  "required": [
    "call_id"
  ],
  "properties": {
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "call_id": {
      "type": "string",
      "title": "Call Id"
    }
  }
}
get_thumbnails output schema
{
  "type": "object",
  "title": "get_thumbnailsOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
edit_thumbnail
Advertised by the server during the last successful anonymous check.

Edit one thumbnail (an image of your thumbnails call, or a picture you send): op erase, edit, face_swap, text, text_variations, upscale, fix, crop or color. crop and color are free; upscale 12; the rest 4 (a version each for text_variations), charged only for what is delivered. Answers at once; read it with get_thumbnails. Same as POST /v1/thumbnails/edit.

edit_thumbnail input schema
{
  "type": "object",
  "title": "edit_thumbnailArguments",
  "required": [
    "op"
  ],
  "properties": {
    "op": {
      "type": "string",
      "title": "Op"
    },
    "faces": {
      "anyOf": [
        {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        {
          "type": "null"
        }
      ],
      "title": "Faces",
      "default": null
    },
    "fixes": {
      "anyOf": [
        {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        {
          "type": "null"
        }
      ],
      "title": "Fixes",
      "default": null
    },
    "words": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Words",
      "default": null
    },
    "prompt": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Prompt",
      "default": null
    },
    "region": {
      "anyOf": [
        {
          "type": "array",
          "items": {
            "type": "integer"
          }
        },
        {
          "type": "null"
        }
      ],
      "title": "Region",
      "default": null
    },
    "source": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Source",
      "default": null
    },
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "contrast": {
      "anyOf": [
        {
          "type": "number"
        },
        {
          "type": "null"
        }
      ],
      "title": "Contrast",
      "default": null
    },
    "source_n": {
      "anyOf": [
        {
          "type": "integer"
        },
        {
          "type": "null"
        }
      ],
      "title": "Source N",
      "default": null
    },
    "brightness": {
      "anyOf": [
        {
          "type": "number"
        },
        {
          "type": "null"
        }
      ],
      "title": "Brightness",
      "default": null
    },
    "saturation": {
      "anyOf": [
        {
          "type": "number"
        },
        {
          "type": "null"
        }
      ],
      "title": "Saturation",
      "default": null
    },
    "variations": {
      "anyOf": [
        {
          "type": "integer"
        },
        {
          "type": "null"
        }
      ],
      "title": "Variations",
      "default": null
    },
    "score_call_id": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Score Call Id",
      "default": null
    },
    "source_call_id": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Source Call Id",
      "default": null
    },
    "idempotency_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Idempotency Key",
      "default": null,
      "description": "1 to 255 visible ASCII characters; the same key and request within 24 hours answer the saved remix and charge nothing; the same key with another request is 422 idempotency_key_reused"
    }
  }
}
edit_thumbnail output schema
{
  "type": "object",
  "title": "edit_thumbnailOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
recreate_thumbnail
Advertised by the server during the last successful anonymous check.

New thumbnails for your video with a YouTube video's own thumbnail as the layout reference (2 to 4 images, every printed word checked); priced as make_thumbnails. Same as POST /v1/thumbnails/recreate; read it with get_thumbnails.

recreate_thumbnail input schema
{
  "type": "object",
  "title": "recreate_thumbnailArguments",
  "required": [
    "video_url"
  ],
  "properties": {
    "mode": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Mode",
      "default": null
    },
    "size": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Size",
      "default": null
    },
    "count": {
      "anyOf": [
        {
          "type": "integer"
        },
        {
          "type": "null"
        }
      ],
      "title": "Count",
      "default": null
    },
    "title": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Title",
      "default": null
    },
    "words": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Words",
      "default": null
    },
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "video_url": {
      "type": "string",
      "title": "Video Url"
    },
    "idempotency_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Idempotency Key",
      "default": null,
      "description": "1 to 255 visible ASCII characters; the same key and request within 24 hours answer the saved remix and charge nothing; the same key with another request is 422 idempotency_key_reused"
    }
  }
}
recreate_thumbnail output schema
{
  "type": "object",
  "title": "recreate_thumbnailOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
optimize_video
Advertised by the server during the last successful anonymous check.

New packages (4, 8 or 12) for your published video: the original scored first against the real outliers on its subject, then each new package (title, thumbnail with every word checked, what it tests) scored the same way, best first, with vs_original. 10 credits a delivered package. Answers at once; read it with get_optimize. Same as POST /v1/optimize.

optimize_video input schema
{
  "type": "object",
  "title": "optimize_videoArguments",
  "required": [
    "video_url"
  ],
  "properties": {
    "size": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Size",
      "default": null
    },
    "count": {
      "anyOf": [
        {
          "type": "integer"
        },
        {
          "type": "null"
        }
      ],
      "title": "Count",
      "default": null
    },
    "run_id": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Run Id",
      "default": null
    },
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "video_url": {
      "type": "string",
      "title": "Video Url"
    },
    "idempotency_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Idempotency Key",
      "default": null,
      "description": "1 to 255 visible ASCII characters; the same key and request within 24 hours answer the saved remix and charge nothing; the same key with another request is 422 idempotency_key_reused"
    }
  }
}
optimize_video output schema
{
  "type": "object",
  "title": "optimize_videoOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
get_optimize
Advertised by the server during the last successful anonymous check.

An Optimize call: the original's score and every new package so far. Same as GET /v1/optimize/{call_id}. Free.

get_optimize input schema
{
  "type": "object",
  "title": "get_optimizeArguments",
  "required": [
    "call_id"
  ],
  "properties": {
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "call_id": {
      "type": "string",
      "title": "Call Id"
    }
  }
}
get_optimize output schema
{
  "type": "object",
  "title": "get_optimizeOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
ab_set
Advertised by the server during the last successful anonymous check.

Pack 2 or 3 images of one of your thumbnails, edit or Optimize calls for YouTube Studio's Test and Compare: a zip of A, B, C (1280x720, under 2 MB) and a note of what each tests. Free. Same as POST /v1/ab.

ab_set input schema
{
  "type": "object",
  "title": "ab_setArguments",
  "required": [
    "source_call_id",
    "picks"
  ],
  "properties": {
    "picks": {
      "type": "array",
      "items": {
        "type": "integer"
      },
      "title": "Picks"
    },
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "source_call_id": {
      "type": "string",
      "title": "Source Call Id"
    }
  }
}
ab_set output schema
{
  "type": "object",
  "title": "ab_setOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
get_find
Advertised by the server during the last successful anonymous check.

One result of a long or packaging run with every field: the video, its numbers and the opening or the package analysis. find_id comes from a run's finds (get_run) or from list_kept_finds. Same shape as GET /v1/finds/{find_id}. Free. Errors: 404 find_not_found, 422 for an id that is not a UUID.

get_find input schema
{
  "type": "object",
  "title": "get_findArguments",
  "required": [
    "find_id"
  ],
  "properties": {
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "find_id": {
      "type": "string",
      "title": "Find Id"
    }
  }
}
get_find output schema
{
  "type": "object",
  "title": "get_findOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
keep_find
Advertised by the server during the last successful anonymous check.

Keep or reject a find (a long-form hook or a package): verdict is "keep" (default) or "reject", and the last verdict wins. The verdict is the video's on that tab: every find of the same video you have there carries it, so a later run that finds the video again shows it already kept. Kept videos come back from list_kept_finds, across all runs. Returns {"find_id", "verdict"}. Same as POST /v1/finds/{find_id}/decision. Free. Errors: 404 find_not_found.

keep_find input schema
{
  "type": "object",
  "title": "keep_findArguments",
  "required": [
    "find_id"
  ],
  "properties": {
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "find_id": {
      "type": "string",
      "title": "Find Id"
    },
    "verdict": {
      "type": "string",
      "title": "Verdict",
      "default": "keep"
    }
  }
}
keep_find output schema
{
  "type": "object",
  "title": "keep_findOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
clear_find_decision
Advertised by the server during the last successful anonymous check.

Undo keep_find: the video goes back to having no verdict, on every find of it, and leaves list_kept_finds. Returns {"find_id", "verdict": null}. Clearing a find with no decision is the same success, so a retry is safe. Same as DELETE /v1/finds/{find_id}/decision. Free.

clear_find_decision input schema
{
  "type": "object",
  "title": "clear_find_decisionArguments",
  "required": [
    "find_id"
  ],
  "properties": {
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "find_id": {
      "type": "string",
      "title": "Find Id"
    }
  }
}
clear_find_decision output schema
{
  "type": "object",
  "title": "clear_find_decisionOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
list_finds
Advertised by the server during the last successful anonymous check.

The index: every video your long and packaging runs found, across all runs, one entry a video on each tab (its newest read, with the keep or reject made on the video). Each is a full find object: the title and thumbnail_url as YouTube shows them, views, channel_median_views, outlier (views over the channel's usual) and outlier_score (0 to 100 on one fixed scale, so entries from different runs rank together), plus the analysis its run wrote. q (at most 120 characters) looks in the title, the channel, the hook as it was said, and what the thumbnail prints and shows, so a package can be found by its thumbnail ("red arrow", "before and after"). Returns {"finds", "count", "total" (every match, on any page), "next_cursor"}; pass next_cursor as cursor for the next page. Use it to search what you already paid for before starting a new run. Same as GET /v1/finds. Free. Errors: 422 for a kind, sort, tier, min_score, device or q that is not allowed. The bounds min_multiple to language (T3-FL) narrow the index on the server, as on GET /v1/finds; an entry that does not carry a bounded field is left out.

list_finds input schema
{
  "type": "object",
  "title": "list_findsArguments",
  "properties": {
    "q": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Q",
      "default": null,
      "description": "words a title, channel, hook or thumbnail must hold"
    },
    "kind": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Kind",
      "default": null,
      "description": "only \"long\" or only \"packaging\"; leave out for both"
    },
    "sort": {
      "type": "string",
      "title": "Sort",
      "default": "score",
      "description": "\"score\" (default: the highest outlier score first), \"outlier\" (views over the channel's usual), \"views\" or \"newest\""
    },
    "tier": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Tier",
      "default": null,
      "description": "only \"breakout\" (10 times the channel's usual or more), \"strong\" (5 to 10) or \"outlier\" (under 5)"
    },
    "limit": {
      "type": "integer",
      "title": "Limit",
      "default": 100,
      "maximum": 100,
      "minimum": 1,
      "description": "page size, 1 to 100"
    },
    "cursor": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Cursor",
      "default": null
    },
    "device": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Device",
      "default": null,
      "description": "only entries filed under this device: a hook_type, a title_device or one of the thumbnail_devices"
    },
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "language": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Language",
      "default": null
    },
    "max_views": {
      "anyOf": [
        {
          "type": "integer"
        },
        {
          "type": "null"
        }
      ],
      "title": "Max Views",
      "default": null
    },
    "min_score": {
      "anyOf": [
        {
          "type": "integer"
        },
        {
          "type": "null"
        }
      ],
      "title": "Min Score",
      "default": null,
      "description": "only entries with an outlier_score of at least this, 0 to 100"
    },
    "min_views": {
      "anyOf": [
        {
          "type": "integer"
        },
        {
          "type": "null"
        }
      ],
      "title": "Min Views",
      "default": null
    },
    "max_comments": {
      "anyOf": [
        {
          "type": "integer"
        },
        {
          "type": "null"
        }
      ],
      "title": "Max Comments",
      "default": null
    },
    "max_multiple": {
      "anyOf": [
        {
          "type": "number"
        },
        {
          "type": "null"
        }
      ],
      "title": "Max Multiple",
      "default": null
    },
    "min_comments": {
      "anyOf": [
        {
          "type": "integer"
        },
        {
          "type": "null"
        }
      ],
      "title": "Min Comments",
      "default": null
    },
    "min_multiple": {
      "anyOf": [
        {
          "type": "number"
        },
        {
          "type": "null"
        }
      ],
      "title": "Min Multiple",
      "default": null
    },
    "max_duration_s": {
      "anyOf": [
        {
          "type": "integer"
        },
        {
          "type": "null"
        }
      ],
      "title": "Max Duration S",
      "default": null
    },
    "min_duration_s": {
      "anyOf": [
        {
          "type": "integer"
        },
        {
          "type": "null"
        }
      ],
      "title": "Min Duration S",
      "default": null
    },
    "max_channel_median": {
      "anyOf": [
        {
          "type": "integer"
        },
        {
          "type": "null"
        }
      ],
      "title": "Max Channel Median",
      "default": null
    },
    "min_channel_median": {
      "anyOf": [
        {
          "type": "integer"
        },
        {
          "type": "null"
        }
      ],
      "title": "Min Channel Median",
      "default": null
    },
    "posted_within_days": {
      "anyOf": [
        {
          "type": "integer"
        },
        {
          "type": "null"
        }
      ],
      "title": "Posted Within Days",
      "default": null
    }
  }
}
list_finds output schema
{
  "type": "object",
  "title": "list_findsOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
remix_find
Advertised by the server during the last successful anonymous check.

Make it mine, for a long-form opening or a package: write up to n (1 to 5, default 3) new ones for your niche that keep one real find's structure. From a long find, `written` holds openings: hook (what the creator says first), then (what the next half minute has to deliver) and kept_structure, built on the find's template and its order of beats. From a packaging find it holds packages: title, thumbnail (what to show and how to frame it), thumbnail_text (the words to print, or null) and kept_structure, built on its title template and its thumbnail's composition. Write my intro: with mode "intro" and video_brief (your own video in a few sentences, 10 to 600 characters; a long find only, a packaging find is 422; niche and n are not used) it writes YOUR intro in the real opening's structure. `written` then holds one object with a beat for each beat of the real opening, in order: beat, does, from_s, to_s, seconds and words_budget (the real opening's own timing, never the model's), say (the words to say, with {placeholders} for what only you know), words, placeholders, suggested_on_screen and suggested_broll (suggestions, never part of what is said); and seconds, timing (measured or estimated), kept_structure, placeholders and numbers_to_placeholders. The answer carries mode "intro" and video_brief, and niche is null. A number you did not write in video_brief becomes {number}. One that copies the real opening or runs over a word budget is 422 remix_not_possible. Returns {"find_id", "kind", "niche", "n", "written", "label", "credits_charged": 1, "credits"}. label says they were written by Hook Detector from a real opening's (or package's) structure: show it with them, never present one as a real video's. One that is the real find's own words is dropped. They are never added to a run, a keep or an export. Costs 1 credit per call, taken before the model is asked and refunded when it does not answer (503 model_unavailable) or when nothing it wrote keeps the rules or it declines the content (422 remix_not_possible, the reason in the message; another try or another niche may work). 402 insufficient_credits at a balance of 0; 404 find_not_found; limited per account per hour (429 rate_limited). Needs the research permission. The answer carries remix_id: list_remixes and get_remix read it back, free. Same as POST /v1/finds/{find_id}/remix.

remix_find input schema
{
  "type": "object",
  "title": "remix_findArguments",
  "properties": {
    "n": {
      "type": "integer",
      "title": "N",
      "default": null,
      "maximum": 5,
      "minimum": 1,
      "description": "how many to write, 1 to 5 (default 3)"
    },
    "mode": {
      "enum": [
        "openings",
        "intro",
        null
      ],
      "type": [
        "string",
        "null"
      ],
      "title": "Mode",
      "default": null,
      "examples": [
        "intro"
      ],
      "description": "optional: \"openings\" (the default) writes openings or packages for a niche; \"intro\" is Write my intro: your own timed intro for the video you describe in video_brief, in this long-form opening's structure. Only a long find has an intro (a packaging find is 422); niche and n are not used in intro mode"
    },
    "niche": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Niche",
      "default": null,
      "description": "your niche, product or audience, as you would say it (1 to 120 characters)"
    },
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "find_id": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Find Id",
      "default": null,
      "description": "one of your finds (from get_run, get_find, list_finds or list_kept_finds)"
    },
    "video_brief": {
      "type": [
        "string",
        "null"
      ],
      "title": "Video Brief",
      "default": null,
      "examples": [
        "A beginner video on why home espresso tastes sour, built around my 18 grams in, 36 grams out recipe."
      ],
      "maxLength": 600,
      "minLength": 10,
      "description": "required for mode \"intro\", 10 to 600 characters: what your video is about, in a few sentences, in your own words and language. The intro uses only the facts and numbers you write here; anything else becomes a {placeholder}"
    },
    "idempotency_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Idempotency Key",
      "default": null,
      "description": "1 to 255 visible ASCII characters; the same key and request within 24 hours answer the saved remix and charge nothing; the same key with another request is 422 idempotency_key_reused"
    }
  }
}
remix_find output schema
{
  "type": "object",
  "title": "remix_findOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
list_remixes
Advertised by the server during the last successful anonymous check.

The remixes you paid for from one hook (hook_id) or one find (find_id), newest first, each as its answer held it, with remix_id and created_at. Returns {"remixes": [...]}. Free. 404 hook_not_found or find_not_found for one that is not yours. Same as GET /v1/hooks/{hook_id}/remixes and GET /v1/finds/{find_id}/remixes.

list_remixes input schema
{
  "type": "object",
  "title": "list_remixesArguments",
  "properties": {
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "find_id": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Find Id",
      "default": null,
      "description": "one of your finds; or give hook_id"
    },
    "hook_id": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Hook Id",
      "default": null,
      "description": "one of your hooks; or give find_id"
    }
  }
}
list_remixes output schema
{
  "type": "object",
  "title": "list_remixesOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
get_remix
Advertised by the server during the last successful anonymous check.

One remix you paid for (from remix_hook, remix_find or a chat turn), as its answer held it. Free. 404 remix_not_found for one that is not yours. Same as GET /v1/remixes/{remix_id}.

get_remix input schema
{
  "type": "object",
  "title": "get_remixArguments",
  "required": [
    "remix_id"
  ],
  "properties": {
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "remix_id": {
      "type": "string",
      "title": "Remix Id"
    }
  }
}
get_remix output schema
{
  "type": "object",
  "title": "get_remixOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
list_kept_finds
Advertised by the server during the last successful anonymous check.

The videos you kept with keep_find (long-form hooks and packages), newest decision first: one entry a video on each tab across all runs, or the kept finds of one run_id, limit 1 to 100 (default 100) per page. Returns {"kept": [...], "count": n on this page, "next_cursor"} with full find objects. Same shape as GET /v1/finds/keeps. Free. Errors: 422 for a bad kind, run_id or cursor.

list_kept_finds input schema
{
  "type": "object",
  "title": "list_kept_findsArguments",
  "properties": {
    "kind": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Kind",
      "default": null,
      "description": "only \"long\" or only \"packaging\"; leave out for both"
    },
    "limit": {
      "type": "integer",
      "title": "Limit",
      "default": 100,
      "maximum": 100,
      "minimum": 1,
      "description": "page size, 1 to 100"
    },
    "cursor": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Cursor",
      "default": null
    },
    "run_id": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Run Id",
      "default": null
    },
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    }
  }
}
list_kept_finds output schema
{
  "type": "object",
  "title": "list_kept_findsOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
export_run
Advertised by the server during the last successful anonymous check.

A finished run's hooks as a file a creator can open: format "csv" (default; one row per hook, opens in any spreadsheet, formula-like cells defused with a leading quote), "json" (the full hook objects) or "md" (Markdown notes, one section per hook). Returns {"filename", "content_type", "format", "content"}, where content is the whole file as text: write it to filename. The same bytes as GET /v1/runs/{run_id}/export?format=... . Free. Errors: 404 run_not_found, 409 run_not_finished while the run is queued or running (call get_run with wait_seconds=50 first), 422 invalid_request for another format.

export_run input schema
{
  "type": "object",
  "title": "export_runArguments",
  "required": [
    "run_id"
  ],
  "properties": {
    "format": {
      "type": "string",
      "title": "Format",
      "default": "csv"
    },
    "run_id": {
      "type": "string",
      "title": "Run Id"
    },
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    }
  }
}
export_run output schema
{
  "type": "object",
  "title": "export_runOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
get_hook
Advertised by the server during the last successful anonymous check.

One hook with every field: its opening line, transcript, watch_url and the vertical player_url (null only for an unusable id or a photo post found before 2026-09-26; runs deliver single videos only). hook_id comes from get_run. Free.

get_hook input schema
{
  "type": "object",
  "title": "get_hookArguments",
  "required": [
    "hook_id"
  ],
  "properties": {
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "hook_id": {
      "type": "string",
      "title": "Hook Id"
    }
  }
}
get_hook output schema
{
  "type": "object",
  "title": "get_hookOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
embed_hook
Advertised by the server during the last successful anonymous check.

Official TikTok or Instagram embed HTML for a hook, so the clip plays inside your own page. kind is "embed", or "fallback" when the creator disabled embedding or the clip is a photo post found before 2026-09-26 (then show thumbnail, which falls back to the hook's still_url, and the transcript). For a plain vertical iframe use the hook's player_url instead. Free.

embed_hook input schema
{
  "type": "object",
  "title": "embed_hookArguments",
  "required": [
    "hook_id"
  ],
  "properties": {
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "hook_id": {
      "type": "string",
      "title": "Hook Id"
    }
  }
}
embed_hook output schema
{
  "type": "object",
  "title": "embed_hookOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
keep_hook
Advertised by the server during the last successful anonymous check.

Keep or reject a hook: verdict is "keep" (default) or "reject", and the last verdict wins. A reject may say why: reason is off_subject, weak_opening, wrong_language, low_quality, already_have or other, and note (up to 280 characters) your own words. Kept hooks come back from list_keeps, across all runs. Free.

keep_hook input schema
{
  "type": "object",
  "title": "keep_hookArguments",
  "required": [
    "hook_id"
  ],
  "properties": {
    "note": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Note",
      "default": null
    },
    "reason": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Reason",
      "default": null
    },
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "hook_id": {
      "type": "string",
      "title": "Hook Id"
    },
    "verdict": {
      "type": "string",
      "title": "Verdict",
      "default": "keep"
    }
  }
}
keep_hook output schema
{
  "type": "object",
  "title": "keep_hookOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
clear_decision
Advertised by the server during the last successful anonymous check.

Undo keep_hook: the hook goes back to having no verdict, so it leaves list_keeps and its export row carries none. Returns {"hook_id", "verdict": null}. Clearing a hook with no decision is the same success, so a retry is safe. Same as DELETE /v1/hooks/{hook_id}/decision. Free. Errors: 404 hook_not_found, 422 for an id that is not a UUID.

clear_decision input schema
{
  "type": "object",
  "title": "clear_decisionArguments",
  "required": [
    "hook_id"
  ],
  "properties": {
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "hook_id": {
      "type": "string",
      "title": "Hook Id"
    }
  }
}
clear_decision output schema
{
  "type": "object",
  "title": "clear_decisionOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
remix_hook
Advertised by the server during the last successful anonymous check.

Make it mine: write up to n new opening lines (1 to 5, default 3) for your niche that keep a real hook's structure, the device that made it travel, with the subject swapped for yours. hook_id is one of your hooks (from get_run, get_hook or list_keeps); 404 hook_not_found otherwise. Leave mode out: "intro" (Write my intro) is remix_find on a long-form find, and is 422 here, before any charge. Returns {"hook_id", "niche", "n", "written": [{"line", "kept_structure"}], "label", "credits_charged": 1, "credits"}. label is always "Written by Hook Detector from a real hook's structure": these are written lines, never found hooks; show the label with them, never present one as a real clip's hook. They are never added to a deck, a keep or an export. Costs 1 credit per call, taken before the model is asked and refunded when it does not answer (503 model_unavailable) or when nothing it wrote keeps the rules or it declines the content (422 remix_not_possible, the reason in the message; another try or another niche may work). 402 insufficient_credits at a balance of 0; limited per account per hour (429 rate_limited). Needs the research permission. The answer carries remix_id: list_remixes and get_remix read it back, free. Same as POST /v1/hooks/{hook_id}/remix.

remix_hook input schema
{
  "type": "object",
  "title": "remix_hookArguments",
  "properties": {
    "n": {
      "type": "integer",
      "title": "N",
      "default": null,
      "maximum": 5,
      "minimum": 1,
      "description": "how many lines to write, 1 to 5 (default 3)"
    },
    "mode": {
      "enum": [
        "openings",
        null
      ],
      "type": [
        "string",
        "null"
      ],
      "title": "Mode",
      "default": null,
      "description": "leave out (or \"openings\"). Write my intro works from a long-form find (POST /v1/finds/{find_id}/remix), so \"intro\" is refused here with 422"
    },
    "niche": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Niche",
      "default": null,
      "description": "your niche, product or audience, as you would say it (1 to 120 characters)"
    },
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "hook_id": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Hook Id",
      "default": null,
      "description": "one of your hooks (from get_run, get_hook or list_keeps)"
    },
    "idempotency_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Idempotency Key",
      "default": null,
      "description": "1 to 255 visible ASCII characters; the same key and request within 24 hours answer the saved remix and charge nothing; the same key with another request is 422 idempotency_key_reused"
    }
  }
}
remix_hook output schema
{
  "type": "object",
  "title": "remix_hookOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
list_keeps
Advertised by the server during the last successful anonymous check.

The hooks you kept with keep_hook, newest decision first, across all runs or within one run_id, limit 1 to 100 (default 100) per page. Returns {"kept": [...], "count": n on this page, "next_cursor"} with full hook objects; pass next_cursor back as cursor for more. Same shape as GET /v1/keeps. Free. Errors: 422 for a bad run_id or cursor.

list_keeps input schema
{
  "type": "object",
  "title": "list_keepsArguments",
  "properties": {
    "limit": {
      "type": "integer",
      "title": "Limit",
      "default": 100,
      "maximum": 100,
      "minimum": 1,
      "description": "page size, 1 to 100"
    },
    "cursor": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Cursor",
      "default": null
    },
    "run_id": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Run Id",
      "default": null
    },
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    }
  }
}
list_keeps output schema
{
  "type": "object",
  "title": "list_keepsOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
balance
Advertised by the server during the last successful anonymous check.

Your account and remaining credits right now: account_id, credits, key_prefix and key_id of the key making this call, label, created_at, and that key's scopes, credit_limit and expires_at, and user (the person signed in with Google, or null). Same shape as GET /v1/me. A run needs at least 40 credits and reserves up to 200. For spend, credits held by in-flight runs and daily history, call usage. Free.

balance input schema
{
  "type": "object",
  "title": "balanceArguments",
  "properties": {
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    }
  }
}
balance output schema
{
  "type": "object",
  "title": "balanceOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
delete_account
Advertised by the server during the last successful anonymous check.

Erase your account and everything in it, for good: every API key, run, hook, keep or reject decision, conversation and message. Unspent credits go with it. It cannot be undone. confirm must be exactly "delete my account", or nothing happens (422 confirmation_required). Refused with 409 run_in_flight while a run is queued or running: wait for it with get_run first. Returns {"deleted": true, "account_id", "erased": {counts per kind}, "message"}. Every key of the account stops working at once; create_account starts a new one. Same as DELETE /v1/account. Free.

delete_account input schema
{
  "type": "object",
  "title": "delete_accountArguments",
  "properties": {
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "confirm": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Confirm",
      "default": null
    }
  }
}
delete_account output schema
{
  "type": "object",
  "title": "delete_accountOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
chat
Advertised by the server during the last successful anonymous check.

One conversational turn, for an agent relaying a person's words. It either asks ONE clarifying question (action "clarify"), answers about delivered hooks or the tool (action "answer", run null, no credits), or starts a research run (action "research", with the started run under "run"), exactly like POST /v1/chat. Every response has suggestions: 0 to 4 follow-ups, [] for research. A deck size typed into the message ("..., 15 hooks") and a recency phrase ("this month", "last week") set the run's count and posted_within_days; the count argument sets the deck size when the message states none. Asked to make one of the conversation's hooks yours for a niche ("make hook 2 mine for dentists"), the turn writes lines from that hook's structure (1 credit, like remix_hook) and answers them labelled, the full result under "remix". language and country select the search locale: omitted or null keeps the current conversation setting; a value sets it for this and later turns; an empty string clears it to any language / any country. The picker wins over the message's locale; replies stay in the language the person writes in. Invalid locale: 422 invalid_request. message is 1 to 4000 characters. Pass conversation_id from an earlier turn to keep the context; leave it out to start a conversation. Cost: a turn itself is free but is a model call, so turns are limited per account per hour (429 rate_limited); a turn that starts a run reserves credits like find_hooks. Next step: on "research", call get_run with run.run_id and wait_seconds=50. Errors: 503 model_unavailable when the model is down (nothing charged, try again), 402 insufficient_credits. For direct research without the question step, call find_hooks instead. Needs the write permission, and research for a turn that starts a run (403 insufficient_scope, nothing reserved).

chat input schema
{
  "type": "object",
  "title": "chatArguments",
  "required": [
    "message"
  ],
  "properties": {
    "count": {
      "anyOf": [
        {
          "type": "integer",
          "maximum": 30,
          "minimum": 1
        },
        {
          "type": "null"
        }
      ],
      "title": "Count",
      "default": null,
      "description": "how many hooks a run this turn starts should deliver, 1 to 30; a size typed into the message (\"15 hooks\") wins over it, and it wins over the turn's own guess; null or omitted lets the turn decide. Fewer hooks is a faster, cheaper run"
    },
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "country": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Country",
      "default": null,
      "pattern": "^[A-Za-z]{2}$",
      "examples": [
        "MA"
      ],
      "description": "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. For chat, omitted or null keeps the conversation's current setting; a value sets it for this and later turns; the empty string clears it back to any language / any country."
    },
    "message": {
      "type": "string",
      "title": "Message",
      "maxLength": 4000,
      "minLength": 1
    },
    "language": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Language",
      "default": null,
      "examples": [
        "Moroccan Darija"
      ],
      "maxLength": 60,
      "description": "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. For chat, omitted or null keeps the conversation's current setting; a value sets it for this and later turns; the empty string clears it back to any language / any country."
    },
    "platforms": {
      "type": [
        "array",
        "null"
      ],
      "items": {
        "enum": [
          "tiktok",
          "instagram",
          "youtube"
        ],
        "type": "string"
      },
      "title": "Platforms",
      "default": null,
      "examples": [
        [
          "instagram"
        ]
      ],
      "maxItems": 3,
      "minItems": 1,
      "description": "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`.",
      "uniqueItems": true
    },
    "seed_hook_ids": {
      "type": [
        "array",
        "null"
      ],
      "items": {
        "type": "string",
        "format": "uuid"
      },
      "title": "Seed Hook Ids",
      "default": null,
      "maxItems": 10,
      "description": "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.",
      "uniqueItems": true
    },
    "conversation_id": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Conversation Id",
      "default": null
    },
    "idempotency_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Idempotency Key",
      "default": null,
      "description": "retry safely: a turn that started a run with this key within 24 hours answers that run again, no model call, nothing reserved"
    },
    "posted_within_days": {
      "enum": [
        7,
        30,
        90,
        365,
        0,
        null
      ],
      "type": [
        "integer",
        "null"
      ],
      "title": "Posted Within Days",
      "default": null,
      "description": "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. For chat (the recency chip): omitted keeps the conversation's current setting; 7, 30, 90 or 365 sets it for this and later turns and wins over a recency phrase in the message; an explicit null on REST, or 0 on either door, clears it back to any time."
    }
  }
}
chat output schema
{
  "type": "object",
  "title": "chatOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
list_conversations
Advertised by the server during the last successful anonymous check.

Your chat conversations, most recently active first, limit 1 to 100 (default 30) per page: conversation_id, title, created_at, updated_at, language, country and messages (the count). Nullable language/country are the saved search picker settings. Returns {"conversations": [...], "next_cursor"}; pass next_cursor back as cursor for more. Same shape as GET /v1/conversations. Next step: get_conversation. Free.

list_conversations input schema
{
  "type": "object",
  "title": "list_conversationsArguments",
  "properties": {
    "limit": {
      "type": "integer",
      "title": "Limit",
      "default": 30,
      "maximum": 100,
      "minimum": 1,
      "description": "page size, 1 to 100"
    },
    "cursor": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Cursor",
      "default": null
    },
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    }
  }
}
list_conversations output schema
{
  "type": "object",
  "title": "list_conversationsOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
get_conversation
Advertised by the server during the last successful anonymous check.

One chat conversation: its newest messages in order (role, content, run_id, created_at), a page of `limit`, with earlier_cursor for the older ones (null when the page reaches the first message), and the runs those messages hold, with their hooks, plus nullable language and country search picker settings. Same shape as GET /v1/conversations/{conversation_id}. Free. Errors: 404 conversation_not_found, 422 for an id that is not a UUID or a cursor this API did not issue.

get_conversation input schema
{
  "type": "object",
  "title": "get_conversationArguments",
  "required": [
    "conversation_id"
  ],
  "properties": {
    "limit": {
      "type": "integer",
      "title": "Limit",
      "default": 100,
      "maximum": 500,
      "minimum": 1,
      "description": "messages per page, newest page first"
    },
    "before": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Before",
      "default": null,
      "description": "earlier_cursor from the previous page; leave out for the newest messages"
    },
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "conversation_id": {
      "type": "string",
      "title": "Conversation Id"
    }
  }
}
get_conversation output schema
{
  "type": "object",
  "title": "get_conversationOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
usage
Advertised by the server during the last successful anonymous check.

Your credits and what you have spent: credits (balance now), spent_total, reserved_now (credits held by runs still in flight, refunded in part when they finish), runs_total, last_30_days (one entry per UTC day: date, runs, charged) and recent (your 20 newest runs). Same shape as GET /v1/usage. Free.

usage input schema
{
  "type": "object",
  "title": "usageArguments",
  "properties": {
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    }
  }
}
usage output schema
{
  "type": "object",
  "title": "usageOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
list_keys
Advertised by the server during the last successful anonymous check.

Your API keys and what each may do. keys: key_id, name, prefix, created_at, last_used_at and current (true for the key making this call). access, per key_id: scopes, credit_limit, credits_used, expires_at and expired. scopes: the calling key's. The original signup key has key_id "original". The keys themselves are never shown again. Needs admin. Same shape as GET /v1/keys. Free.

list_keys input schema
{
  "type": "object",
  "title": "list_keysArguments",
  "properties": {
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    }
  }
}
list_keys output schema
{
  "type": "object",
  "title": "list_keysOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
create_key
Advertised by the server during the last successful anonymous check.

Create another API key on your account, for example one per agent or machine, so you can revoke one without touching the others. name is up to 60 characters. Give the key only what it needs. scopes: any of "read" (read runs, hooks, keeps, usage), "write" (keep, reject, undo, chat), "research" (start runs, which spend credits), "admin" (manage keys, delete the account); all four when left out. An agent that finds hooks needs ["read", "write", "research"]. credit_limit: the most credits runs started with this key may spend over its life, 1 to 100000 (a run then reserves at most what is left). expires_in_days: 1 to 365; the key then stops working (401 key_expired). The new key is in this result once and never again: store it now. Needs admin. An account holds at most 10 active keys (409 beyond) and may create 20 per hour by default (429 beyond). Free. Next step: use the returned api_key; revoke old ones with revoke_key.

create_key input schema
{
  "type": "object",
  "title": "create_keyArguments",
  "properties": {
    "name": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Name",
      "default": null
    },
    "scopes": {
      "type": "array",
      "items": {
        "enum": [
          "read",
          "write",
          "research",
          "admin"
        ],
        "type": "string"
      },
      "title": "Scopes",
      "default": null,
      "minItems": 1
    },
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "credit_limit": {
      "type": "integer",
      "title": "Credit Limit",
      "default": null,
      "minimum": 1
    },
    "expires_in_days": {
      "type": "integer",
      "title": "Expires In Days",
      "default": null,
      "minimum": 1
    }
  }
}
create_key output schema
{
  "type": "object",
  "title": "create_keyOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
revoke_key
Advertised by the server during the last successful anonymous check.

Revoke one of your API keys at once: key_id from list_keys, or "original" for the signup key. Any key may revoke itself (the result says so, and that key stops working immediately); revoking another needs admin. Never the last active key on the account (409): create another with create_key first. If a key leaked, pass key_id "others": every key of the account except the one you are calling with is revoked at once, and the result says how many (revoked_count) and which key is kept. Needs admin. Free.

revoke_key input schema
{
  "type": "object",
  "title": "revoke_keyArguments",
  "required": [
    "key_id"
  ],
  "properties": {
    "key_id": {
      "type": "string",
      "title": "Key Id"
    },
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    }
  }
}
revoke_key output schema
{
  "type": "object",
  "title": "revoke_keyOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
request_access
Advertised by the server during the last successful anonymous check.

Ask for the beta access code a new account needs (create_account's access_code). email is where the code is sent; note is optional (who you are, up to 500 characters). The owner approves the request and the code is emailed at once; it works once, in any agent or on the web. Returns status pending (waiting for approval) or emailed (sent now). No key needed. Limited to 5 per address per hour. Free.

request_access input schema
{
  "type": "object",
  "title": "request_accessArguments",
  "required": [
    "email"
  ],
  "properties": {
    "note": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Note",
      "default": null
    },
    "email": {
      "type": "string",
      "title": "Email"
    }
  }
}
request_access output schema
{
  "type": "object",
  "title": "request_accessOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
list_access_requests
Advertised by the server during the last successful anonymous check.

Owner only: every access request, newest first, optionally only one status (pending, approved, rejected). Each has request_id, email, note, status, code_id, code_prefix, created_at and decided_at. Never a code. Needs the admin scope and an admin account (403 admin_required otherwise). Free.

list_access_requests input schema
{
  "type": "object",
  "title": "list_access_requestsArguments",
  "properties": {
    "status": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Status",
      "default": null
    },
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    }
  }
}
list_access_requests output schema
{
  "type": "object",
  "title": "list_access_requestsOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
approve_access_request
Advertised by the server during the last successful anonymous check.

Owner only: approve an access request. Makes a one-use code, marks the request approved and emails the code. The code is in this result once, with emailed true or false; when false, send it yourself or approve again (the unused code is revoked and a new one sent). 409 request_already_decided once its code was used. Free.

approve_access_request input schema
{
  "type": "object",
  "title": "approve_access_requestArguments",
  "properties": {
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "request_id": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Request Id",
      "default": null
    }
  }
}
approve_access_request output schema
{
  "type": "object",
  "title": "approve_access_requestOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
reject_access_request
Advertised by the server during the last successful anonymous check.

Owner only: reject a pending access request. Nothing is emailed. An approved or rejected request is 409 request_already_decided. Free.

reject_access_request input schema
{
  "type": "object",
  "title": "reject_access_requestArguments",
  "properties": {
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "request_id": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Request Id",
      "default": null
    }
  }
}
reject_access_request output schema
{
  "type": "object",
  "title": "reject_access_requestOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
list_access_codes
Advertised by the server during the last successful anonymous check.

Owner only: every access code, newest first, with code_id, code_prefix, email, note, source, max_uses, uses, expires_at, revoked_at, created_at and usable. Never the code itself. Free.

list_access_codes input schema
{
  "type": "object",
  "title": "list_access_codesArguments",
  "properties": {
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    }
  }
}
list_access_codes output schema
{
  "type": "object",
  "title": "list_access_codesOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
create_access_code
Advertised by the server during the last successful anonymous check.

Owner only: make an access code to hand out. max_uses 1 to 100000 (default 1), expires_in_days 1 to 365 (optional), note your own. email binds it to one person: then only Google sign-in with that verified email redeems it, never the API or MCP; leave it out for a code that works on every door. The code is in this result only. Free.

create_access_code input schema
{
  "type": "object",
  "title": "create_access_codeArguments",
  "properties": {
    "note": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Note",
      "default": null
    },
    "email": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Email",
      "default": null
    },
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "max_uses": {
      "anyOf": [
        {
          "type": "integer"
        },
        {
          "type": "null"
        }
      ],
      "title": "Max Uses",
      "default": null
    },
    "expires_in_days": {
      "anyOf": [
        {
          "type": "integer"
        },
        {
          "type": "null"
        }
      ],
      "title": "Expires In Days",
      "default": null
    }
  }
}
create_access_code output schema
{
  "type": "object",
  "title": "create_access_codeOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
revoke_access_code
Advertised by the server during the last successful anonymous check.

Owner only: revoke an access code (code_id from list_access_codes). It admits nobody from now on; accounts it already made keep working. 404 access_code_not_found for an unknown id. Free.

revoke_access_code input schema
{
  "type": "object",
  "title": "revoke_access_codeArguments",
  "properties": {
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "code_id": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Code Id",
      "default": null
    }
  }
}
revoke_access_code output schema
{
  "type": "object",
  "title": "revoke_access_codeOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
admin_overview
Advertised by the server during the last successful anonymous check.

Owner only: the whole service at a glance, as GET /v1/admin/overview. Counts of accounts and people; runs (total, last_24h, last_7d, failed_24h, in_flight); hooks; credits (charged_24h, charged_7d, charged_total, balance_total); access (pending_requests, active_codes, redemptions); search (credits_left, low, accepting_runs; null when not read yet); and the running commit. Needs the admin scope and an admin account (403 admin_required otherwise). Free.

admin_overview input schema
{
  "type": "object",
  "title": "admin_overviewArguments",
  "properties": {
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    }
  }
}
admin_overview output schema
{
  "type": "object",
  "title": "admin_overviewOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
admin_list_accounts
Advertised by the server during the last successful anonymous check.

Owner only: every account, newest first, as GET /v1/admin/accounts: {total, accounts: [{id, email, name, label, credits, created_at, last_seen_at, runs, hooks, charged_total, is_admin, admitted_by, keys_active}]}. Free.

admin_list_accounts input schema
{
  "type": "object",
  "title": "admin_list_accountsArguments",
  "properties": {
    "q": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Q",
      "default": null,
      "description": "up to 200 characters: part of an email or label (any case), or the start of an id"
    },
    "limit": {
      "anyOf": [
        {
          "type": "integer",
          "maximum": 200,
          "minimum": 1
        },
        {
          "type": "null"
        }
      ],
      "title": "Limit",
      "default": null,
      "description": "page size, 1 to 200 (default 50)"
    },
    "offset": {
      "anyOf": [
        {
          "type": "integer",
          "maximum": 1000000,
          "minimum": 0
        },
        {
          "type": "null"
        }
      ],
      "title": "Offset",
      "default": null,
      "description": "how many to skip"
    },
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    }
  }
}
admin_list_accounts output schema
{
  "type": "object",
  "title": "admin_list_accountsOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
admin_adjust_credits
Advertised by the server during the last successful anonymous check.

Owner only: add or remove an account's credits, as POST /v1/admin/accounts/{account_id}/credits. A change that would take the balance below zero is 422 credits_below_zero and nothing changes; an unknown account is 404 account_not_found. Every change is audited (admin_list_actions). Returns {account_id, credits, delta}.

admin_adjust_credits input schema
{
  "type": "object",
  "title": "admin_adjust_creditsArguments",
  "properties": {
    "note": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Note",
      "default": null,
      "description": "why, for the audit: 1 to 200 characters, required"
    },
    "delta": {
      "anyOf": [
        {
          "type": "integer",
          "maximum": 1000000,
          "minimum": -1000000
        },
        {
          "type": "null"
        }
      ],
      "title": "Delta",
      "default": null,
      "description": "credits to add (positive) or remove (negative), -1000000 to 1000000, not zero"
    },
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "account_id": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Account Id",
      "default": null,
      "description": "the id from admin_list_accounts"
    }
  }
}
admin_adjust_credits output schema
{
  "type": "object",
  "title": "admin_adjust_creditsOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
admin_list_runs
Advertised by the server during the last successful anonymous check.

Owner only: every account's research runs, newest first, as GET /v1/admin/runs: {runs: [{run_id, account_id, email, topic, language, country, status, requested, hooks, reserved, charged, failure_kind, error, created_at, finished_at}]}. Free.

admin_list_runs input schema
{
  "type": "object",
  "title": "admin_list_runsArguments",
  "properties": {
    "limit": {
      "anyOf": [
        {
          "type": "integer",
          "maximum": 200,
          "minimum": 1
        },
        {
          "type": "null"
        }
      ],
      "title": "Limit",
      "default": null,
      "description": "page size, 1 to 200 (default 50)"
    },
    "status": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Status",
      "default": null,
      "description": "only runs with this status: queued, running, done, failed"
    },
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "account_id": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Account Id",
      "default": null,
      "description": "only this account's"
    }
  }
}
admin_list_runs output schema
{
  "type": "object",
  "title": "admin_list_runsOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
admin_list_actions
Advertised by the server during the last successful anonymous check.

Owner only: the audit of password sign-ins and credit changes, newest first, as GET /v1/admin/actions: {actions: [{id, action, actor_email, target_account_id, detail, created_at}]}. Free.

admin_list_actions input schema
{
  "type": "object",
  "title": "admin_list_actionsArguments",
  "properties": {
    "limit": {
      "anyOf": [
        {
          "type": "integer",
          "maximum": 200,
          "minimum": 1
        },
        {
          "type": "null"
        }
      ],
      "title": "Limit",
      "default": null,
      "description": "page size, 1 to 200 (default 50)"
    },
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    }
  }
}
admin_list_actions output schema
{
  "type": "object",
  "title": "admin_list_actionsOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
list_strategies
Advertised by the server during the last successful anonymous check.

The hook strategy library: every kind of long-form hook the shared index holds at least 3 openings of, with its plain name, count, the median multiple of those videos over their channels' usual views, the median hook length in seconds, the template most share, and up to 3 real examples (title, hook as said, video_id, channel, multiple). Counted from the index, never by a model; public data only. Free. Same as GET /v1/strategies.

list_strategies input schema
{
  "type": "object",
  "title": "list_strategiesArguments",
  "properties": {
    "q": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Q",
      "default": null,
      "description": "a subject: only openings of videos whose title or channel match its words"
    },
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    }
  }
}
list_strategies output schema
{
  "type": "object",
  "title": "list_strategiesOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
create_folder
Advertised by the server during the last successful anonymous check.

Make a named folder on one tab to file hooks or finds in. The same name again on that tab (case and spaces aside) returns the folder already there with created false, so a retry never makes two. At most 200 folders an account (409 folder_limit_reached). Returns the folder: folder_id, kind, name, item_count, cover, shared, share_url, created. Same as POST /v1/folders. Free. Next: save_to_folder.

create_folder input schema
{
  "type": "object",
  "title": "create_folderArguments",
  "properties": {
    "kind": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Kind",
      "default": null,
      "description": "the tab: \"short\" (Short form hooks), \"long\" (Long form finds) or \"packaging\" (Packaging finds)"
    },
    "name": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Name",
      "default": null,
      "description": "1 to 80 characters; the same name again on the tab is the folder already there"
    },
    "team": {
      "type": "boolean",
      "title": "Team",
      "default": false,
      "description": "true: your team's folder, which every member sees and edits (404 not_in_team without a team)"
    },
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "description": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Description",
      "default": null,
      "description": "optional, up to 280 characters"
    }
  }
}
create_folder output schema
{
  "type": "object",
  "title": "create_folderOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
list_folders
Advertised by the server during the last successful anonymous check.

Your folders, the most recently changed first, each with item_count, up to 4 cover thumbnails and its share link when shared. With hook_id or find_id, each folder also has contains: the item id when it holds that video (any run's copy), else null. Returns {"folders": [...], "count"}. Same as GET /v1/folders. Free.

list_folders input schema
{
  "type": "object",
  "title": "list_foldersArguments",
  "properties": {
    "kind": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Kind",
      "default": null,
      "description": "only \"short\", \"long\" or \"packaging\"; leave out for every tab"
    },
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "find_id": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Find Id",
      "default": null,
      "description": "say which folders already hold this find's video"
    },
    "hook_id": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Hook Id",
      "default": null,
      "description": "say which folders already hold this hook"
    }
  }
}
list_folders output schema
{
  "type": "object",
  "title": "list_foldersOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
get_folder
Advertised by the server during the last successful anonymous check.

One of your folders and its items, newest filed first, limit 1 to 100 a page (page with next_cursor). Each item has item_id, note, thumbnail_url, title and the full hook or find object. Same as GET /v1/folders/{folder_id}. Free. Errors: 404 folder_not_found.

get_folder input schema
{
  "type": "object",
  "title": "get_folderArguments",
  "properties": {
    "limit": {
      "type": "integer",
      "title": "Limit",
      "default": 100,
      "maximum": 100,
      "minimum": 1,
      "description": "page size, 1 to 100"
    },
    "cursor": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Cursor",
      "default": null
    },
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "folder_id": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Folder Id",
      "default": null,
      "description": "one of your folders (from list_folders or create_folder)"
    }
  }
}
get_folder output schema
{
  "type": "object",
  "title": "get_folderOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
rename_folder
Advertised by the server during the last successful anonymous check.

Rename a folder, change its description, or both. A name another folder on the tab already has is 409 folder_exists. Returns the folder. Same as PATCH /v1/folders/{folder_id}. Free.

rename_folder input schema
{
  "type": "object",
  "title": "rename_folderArguments",
  "properties": {
    "name": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Name",
      "default": null,
      "description": "a new name, 1 to 80 characters"
    },
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "folder_id": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Folder Id",
      "default": null,
      "description": "one of your folders (from list_folders or create_folder)"
    },
    "description": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Description",
      "default": null,
      "description": "a new description; \"\" clears it, left out it stays"
    }
  }
}
rename_folder output schema
{
  "type": "object",
  "title": "rename_folderOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
delete_folder
Advertised by the server during the last successful anonymous check.

Delete a folder, its items and its share link at once (the link stops working). The hooks and finds in it, and their keeps, stay. Returns {"folder_id", "deleted": true}. Same as DELETE /v1/folders/{folder_id}. Free. Errors: 404 folder_not_found.

delete_folder input schema
{
  "type": "object",
  "title": "delete_folderArguments",
  "properties": {
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "folder_id": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Folder Id",
      "default": null,
      "description": "one of your folders (from list_folders or create_folder)"
    }
  }
}
delete_folder output schema
{
  "type": "object",
  "title": "delete_folderOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
save_to_folder
Advertised by the server during the last successful anonymous check.

File one video in a folder of its tab: a find (find_id) or any public YouTube video (video_id) in a long or packaging folder, a hook (hook_id) in a short one, with an optional note. A video already in the folder (the same find, another run's find of it, the same hook) returns that item with created false; a note given then replaces its note. Filing does not keep it. Same as POST /v1/folders/{folder_id}/items. Free. Errors: 404 find_not_found or hook_not_found, 422 kind_mismatch, 409 folder_full (2000 items).

save_to_folder input schema
{
  "type": "object",
  "title": "save_to_folderArguments",
  "properties": {
    "note": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Note",
      "default": null,
      "description": "optional, up to 1000 characters"
    },
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "find_id": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Find Id",
      "default": null,
      "description": "a Long form or Packaging find of yours; or give hook_id"
    },
    "hook_id": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Hook Id",
      "default": null,
      "description": "a Short form hook of yours; or give find_id"
    },
    "video_id": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Video Id",
      "default": null,
      "description": "any public YouTube video by its 11-character id, for a long or packaging folder"
    },
    "folder_id": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Folder Id",
      "default": null,
      "description": "one of your folders (from list_folders or create_folder)"
    }
  }
}
save_to_folder output schema
{
  "type": "object",
  "title": "save_to_folderOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
edit_folder_item
Advertised by the server during the last successful anonymous check.

Set the note on one item of a folder. Returns the item. Same as PATCH /v1/folders/{folder_id}/items/{item_id}. Free. Errors: 404 item_not_found.

edit_folder_item input schema
{
  "type": "object",
  "title": "edit_folder_itemArguments",
  "properties": {
    "note": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Note",
      "default": null,
      "description": "the note, up to 1000 characters; \"\" or null clears it"
    },
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "item_id": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Item Id",
      "default": null,
      "description": "an item of that folder (from get_folder or save_to_folder)"
    },
    "folder_id": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Folder Id",
      "default": null,
      "description": "one of your folders (from list_folders or create_folder)"
    }
  }
}
edit_folder_item output schema
{
  "type": "object",
  "title": "edit_folder_itemOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
remove_from_folder
Advertised by the server during the last successful anonymous check.

Take one video out of a folder; the hook or find and its keep stay. Returns {"item_id", "removed": true}. Same as DELETE /v1/folders/{folder_id}/items/{item_id}. Free. Errors: 404 item_not_found (also on a second remove).

remove_from_folder input schema
{
  "type": "object",
  "title": "remove_from_folderArguments",
  "properties": {
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "item_id": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Item Id",
      "default": null,
      "description": "an item of that folder (from get_folder or save_to_folder)"
    },
    "folder_id": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Folder Id",
      "default": null,
      "description": "one of your folders (from list_folders or create_folder)"
    }
  }
}
remove_from_folder output schema
{
  "type": "object",
  "title": "remove_from_folderOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
share_folder
Advertised by the server during the last successful anonymous check.

Turn on a folder's share link: share_url is a public read-only page anyone with it opens signed out (hookdetector.com/f/<token>); api_url is the same as JSON (get_shared_folder). It shows the folder's name, description and items with their notes, never anything about the account. A shared folder keeps its link. Same as POST /v1/folders/{folder_id}/share. Free.

share_folder input schema
{
  "type": "object",
  "title": "share_folderArguments",
  "properties": {
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "folder_id": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Folder Id",
      "default": null,
      "description": "one of your folders (from list_folders or create_folder)"
    }
  }
}
share_folder output schema
{
  "type": "object",
  "title": "share_folderOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
unshare_folder
Advertised by the server during the last successful anonymous check.

Turn a folder's share link off: the old link stops working at once; share_folder again makes a new one. Same as DELETE /v1/folders/{folder_id}/share. Free.

unshare_folder input schema
{
  "type": "object",
  "title": "unshare_folderArguments",
  "properties": {
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "folder_id": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Folder Id",
      "default": null,
      "description": "one of your folders (from list_folders or create_folder)"
    }
  }
}
unshare_folder output schema
{
  "type": "object",
  "title": "unshare_folderOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
get_shared_folder
Advertised by the server during the last successful anonymous check.

A shared folder as anyone with its link sees it: name, description, kind, item_count and items (public fields and the owner's note), newest first, paged. No key needed; limited per address (429). A link never made or turned off is 404 shared_folder_not_found. Same as GET /v1/shared/folders/{token}. Free.

get_shared_folder input schema
{
  "type": "object",
  "title": "get_shared_folderArguments",
  "properties": {
    "limit": {
      "type": "integer",
      "title": "Limit",
      "default": 100,
      "maximum": 100,
      "minimum": 1,
      "description": "page size, 1 to 100"
    },
    "token": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Token",
      "default": null,
      "description": "the token of a share link (hookdetector.com/f/<token>)"
    },
    "cursor": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Cursor",
      "default": null
    }
  }
}
get_shared_folder output schema
{
  "type": "object",
  "title": "get_shared_folderOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
swipe_file
Advertised by the server during the last successful anonymous check.

Your swipe file: the title structures of the Packaging finds you kept or filed, each title_template with its title_device, count (how many of those videos use it, one a video) and best (the example with the highest outlier_score, 0 to 100). The most used first; packages is how many videos were read, skipped how many had no structure. Same as GET /v1/swipe. Free.

swipe_file input schema
{
  "type": "object",
  "title": "swipe_fileArguments",
  "properties": {
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    }
  }
}
swipe_file output schema
{
  "type": "object",
  "title": "swipe_fileOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
share_run
Advertised by the server during the last successful anonymous check.

Share a finished Short form run as a public, read-only page anyone can open with the link, no sign-in: each hook's spoken line, on-screen words, idea, plays, creator and its video, in the Hook Detector skill page layout. Nothing about the account is on it. Returns {"run_id", "token", "url"}; give the person the url (https://hookdetector.com/p/<token>). Idempotent: a run keeps one link, so calling again returns the same url. The same body as POST /v1/runs/{run_id}/share; the page's data is GET /v1/shared/{token}, no key needed. Needs the write scope. Free. Errors: 404 run_not_found, 409 run_not_finished while the run works (call get_run with wait_seconds=50 first), 409 run_not_shareable for a failed run or a Long form or Packaging run, 422 invalid_request for a malformed run_id.

share_run input schema
{
  "type": "object",
  "title": "share_runArguments",
  "properties": {
    "run_id": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Run Id",
      "default": null
    },
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    }
  }
}
share_run output schema
{
  "type": "object",
  "title": "share_runOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
start_job
Advertised by the server during the last successful anonymous check.

Start a background job on Hook Detector's job runner. Only echo can be started by hand: it waits `seconds` and answers `message` back with how many attempts it took (a job survives a deploy). At most 3 an hour per account. Free. Same as POST /v1/jobs; read it back with get_job.

start_job input schema
{
  "type": "object",
  "title": "start_jobArguments",
  "properties": {
    "kind": {
      "type": "string",
      "title": "Kind",
      "default": "echo",
      "description": "only echo today"
    },
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "message": {
      "anyOf": [
        {
          "type": "string",
          "maxLength": 200
        },
        {
          "type": "null"
        }
      ],
      "title": "Message",
      "default": null
    },
    "seconds": {
      "type": "integer",
      "title": "Seconds",
      "default": 5,
      "maximum": 120,
      "minimum": 1,
      "description": "1 to 120"
    }
  }
}
start_job output schema
{
  "type": "object",
  "title": "start_jobOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
list_jobs
Advertised by the server during the last successful anonymous check.

This account's background jobs, newest first (tracker refreshes, alerts, reviews, echo): status, attempts, credits, result or error. Free. Same as GET /v1/jobs.

list_jobs input schema
{
  "type": "object",
  "title": "list_jobsArguments",
  "properties": {
    "limit": {
      "type": "integer",
      "title": "Limit",
      "default": 50,
      "maximum": 100,
      "minimum": 1
    },
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    }
  }
}
list_jobs output schema
{
  "type": "object",
  "title": "list_jobsOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
get_job
Advertised by the server during the last successful anonymous check.

One background job of this account. Same as GET /v1/jobs/{job_id}. Errors: 404 job_not_found for another account's job or an unknown id.

get_job input schema
{
  "type": "object",
  "title": "get_jobArguments",
  "required": [
    "job_id"
  ],
  "properties": {
    "job_id": {
      "type": "string",
      "title": "Job Id"
    },
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    }
  }
}
get_job output schema
{
  "type": "object",
  "title": "get_jobOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
search_index
Advertised by the server during the last successful anonymous check.

Search the shared index of public YouTube videos Hook Detector's runs measured, with no run: words by text and meaning, or browse; filters for the multiple, views, long or short, recency, channel size and language. Free. Same as GET /v1/index/search.

search_index input schema
{
  "type": "object",
  "title": "search_indexArguments",
  "properties": {
    "q": {
      "anyOf": [
        {
          "type": "string",
          "maxLength": 200
        },
        {
          "type": "null"
        }
      ],
      "title": "Q",
      "default": null
    },
    "sort": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Sort",
      "default": null,
      "description": "multiple, views, recent or relevance (needs q)"
    },
    "limit": {
      "type": "integer",
      "title": "Limit",
      "default": 24,
      "maximum": 50,
      "minimum": 1
    },
    "cursor": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Cursor",
      "default": null
    },
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "language": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Language",
      "default": null
    },
    "max_views": {
      "anyOf": [
        {
          "type": "integer"
        },
        {
          "type": "null"
        }
      ],
      "title": "Max Views",
      "default": null
    },
    "min_views": {
      "anyOf": [
        {
          "type": "integer"
        },
        {
          "type": "null"
        }
      ],
      "title": "Min Views",
      "default": null
    },
    "channel_size": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Channel Size",
      "default": null,
      "description": "small, medium or large"
    },
    "content_type": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Content Type",
      "default": null,
      "description": "long or short"
    },
    "max_multiple": {
      "anyOf": [
        {
          "type": "number"
        },
        {
          "type": "null"
        }
      ],
      "title": "Max Multiple",
      "default": null
    },
    "min_multiple": {
      "anyOf": [
        {
          "type": "number"
        },
        {
          "type": "null"
        }
      ],
      "title": "Min Multiple",
      "default": null
    },
    "posted_within_days": {
      "anyOf": [
        {
          "type": "integer"
        },
        {
          "type": "null"
        }
      ],
      "title": "Posted Within Days",
      "default": null,
      "description": "30, 90, 365 or 730"
    }
  }
}
search_index output schema
{
  "type": "object",
  "title": "search_indexOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
random_outliers
Advertised by the server during the last successful anonymous check.

Outliers at random from the shared index (5x their channel's usual and over by default), a different set each call. Free. Same as GET /v1/index/random.

random_outliers input schema
{
  "type": "object",
  "title": "random_outliersArguments",
  "properties": {
    "limit": {
      "type": "integer",
      "title": "Limit",
      "default": 12,
      "maximum": 50,
      "minimum": 1
    },
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "language": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Language",
      "default": null
    },
    "max_views": {
      "anyOf": [
        {
          "type": "integer"
        },
        {
          "type": "null"
        }
      ],
      "title": "Max Views",
      "default": null
    },
    "min_views": {
      "anyOf": [
        {
          "type": "integer"
        },
        {
          "type": "null"
        }
      ],
      "title": "Min Views",
      "default": null
    },
    "channel_size": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Channel Size",
      "default": null
    },
    "content_type": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Content Type",
      "default": null
    },
    "max_multiple": {
      "anyOf": [
        {
          "type": "number"
        },
        {
          "type": "null"
        }
      ],
      "title": "Max Multiple",
      "default": null
    },
    "min_multiple": {
      "anyOf": [
        {
          "type": "number"
        },
        {
          "type": "null"
        }
      ],
      "title": "Min Multiple",
      "default": null
    },
    "posted_within_days": {
      "anyOf": [
        {
          "type": "integer"
        },
        {
          "type": "null"
        }
      ],
      "title": "Posted Within Days",
      "default": null
    }
  }
}
random_outliers output schema
{
  "type": "object",
  "title": "random_outliersOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
get_channel
Advertised by the server during the last successful anonymous check.

A channel page: subscribers and totals, every recent long upload scored against the channel itself (outliers at 2x its median and over), and its momentum. Free. Same as GET /v1/channels/{ref}.

get_channel input schema
{
  "type": "object",
  "title": "get_channelArguments",
  "required": [
    "ref"
  ],
  "properties": {
    "ref": {
      "type": "string",
      "title": "Ref",
      "description": "a UC id, @handle or channel address"
    },
    "fresh": {
      "type": "boolean",
      "title": "Fresh",
      "default": false
    },
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    }
  }
}
get_channel output schema
{
  "type": "object",
  "title": "get_channelOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
channel_outliers
Advertised by the server during the last successful anonymous check.

A channel's own outliers (scope own), or outliers for a channel like it from other channels in the shared index (scope niche, 3x and over by default). Free. Same as GET /v1/channels/{ref}/outliers.

channel_outliers input schema
{
  "type": "object",
  "title": "channel_outliersArguments",
  "required": [
    "ref"
  ],
  "properties": {
    "ref": {
      "type": "string",
      "title": "Ref"
    },
    "limit": {
      "type": "integer",
      "title": "Limit",
      "default": 24,
      "maximum": 50,
      "minimum": 1
    },
    "scope": {
      "type": "string",
      "title": "Scope",
      "default": "own",
      "description": "own or niche"
    },
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "min_multiple": {
      "anyOf": [
        {
          "type": "number",
          "maximum": 1000,
          "minimum": 0
        },
        {
          "type": "null"
        }
      ],
      "title": "Min Multiple",
      "default": null
    }
  }
}
channel_outliers output schema
{
  "type": "object",
  "title": "channel_outliersOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
similar_channels
Advertised by the server during the last successful anonymous check.

Channels in the shared index closest to this one by the meaning of their titles, else by shared words, with a similarity 0 to 1. Free. Same as GET /v1/channels/{ref}/similar.

similar_channels input schema
{
  "type": "object",
  "title": "similar_channelsArguments",
  "required": [
    "ref"
  ],
  "properties": {
    "ref": {
      "type": "string",
      "title": "Ref"
    },
    "limit": {
      "type": "integer",
      "title": "Limit",
      "default": 10,
      "maximum": 20,
      "minimum": 1
    },
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    }
  }
}
similar_channels output schema
{
  "type": "object",
  "title": "similar_channelsOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
search_channels
Advertised by the server during the last successful anonymous check.

Channels by name or handle: the shared index first, then YouTube's own channel search. Free. Same as GET /v1/channels/search.

search_channels input schema
{
  "type": "object",
  "title": "search_channelsArguments",
  "required": [
    "q"
  ],
  "properties": {
    "q": {
      "type": "string",
      "title": "Q",
      "maxLength": 100,
      "minLength": 2
    },
    "limit": {
      "type": "integer",
      "title": "Limit",
      "default": 20,
      "maximum": 50,
      "minimum": 1
    },
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    }
  }
}
search_channels output schema
{
  "type": "object",
  "title": "search_channelsOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
niche_report
Advertised by the server during the last successful anonymous check.

A niche report counted from the shared index: videos, channels, median views, the share at 10x and over, the 75th percentile multiple, top outliers and channels, by month. Free. Same as GET /v1/niches/{slug}.

niche_report input schema
{
  "type": "object",
  "title": "niche_reportArguments",
  "required": [
    "slug"
  ],
  "properties": {
    "slug": {
      "type": "string",
      "title": "Slug",
      "description": "lowercase words joined by hyphens"
    },
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    }
  }
}
niche_report output schema
{
  "type": "object",
  "title": "niche_reportOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
list_niches
Advertised by the server during the last successful anonymous check.

The niche explorer: every niche reported, with its headline numbers. Free. Same as GET /v1/niches.

list_niches input schema
{
  "type": "object",
  "title": "list_nichesArguments",
  "properties": {
    "limit": {
      "type": "integer",
      "title": "Limit",
      "default": 50,
      "maximum": 200,
      "minimum": 1
    },
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    }
  }
}
list_niches output schema
{
  "type": "object",
  "title": "list_nichesOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
video_stats
Advertised by the server during the last successful anonymous check.

A video's views per hour and its history from the shared index (new, rising and tracked videos read hourly, the rest daily); a video the index lacks is read now. Free. Same as GET /v1/videos/{video_id}/stats.

video_stats input schema
{
  "type": "object",
  "title": "video_statsArguments",
  "required": [
    "video_id"
  ],
  "properties": {
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "video_id": {
      "type": "string",
      "title": "Video Id",
      "description": "an 11-character YouTube id"
    }
  }
}
video_stats output schema
{
  "type": "object",
  "title": "video_statsOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
list_winners
Advertised by the server during the last successful anonymous check.

Real title and thumbnail changes on other channels: both versions (the pictures as URLs), views per hour before and 24 to 72 hours after, the lift and when the spike came. Free. Same as GET /v1/winners.

list_winners input schema
{
  "type": "object",
  "title": "list_winnersArguments",
  "properties": {
    "sort": {
      "type": "string",
      "title": "Sort",
      "default": "recent",
      "description": "recent or lift"
    },
    "type": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Type",
      "default": null,
      "description": "title or thumbnail"
    },
    "limit": {
      "type": "integer",
      "title": "Limit",
      "default": 24,
      "maximum": 50,
      "minimum": 1
    },
    "cursor": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Cursor",
      "default": null
    },
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "min_lift": {
      "anyOf": [
        {
          "type": "number",
          "minimum": 0
        },
        {
          "type": "null"
        }
      ],
      "title": "Min Lift",
      "default": null
    },
    "min_views": {
      "anyOf": [
        {
          "type": "integer",
          "minimum": 0
        },
        {
          "type": "null"
        }
      ],
      "title": "Min Views",
      "default": null
    },
    "min_vph_before": {
      "anyOf": [
        {
          "type": "number",
          "minimum": 0
        },
        {
          "type": "null"
        }
      ],
      "title": "Min Vph Before",
      "default": null
    }
  }
}
list_winners output schema
{
  "type": "object",
  "title": "list_winnersOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
create_tracker
Advertised by the server during the last successful anonymous check.

Make a tracker of up to 50 channels: hourly reads of their newest uploads, alerts when one reaches the multiple (default 3x its channel) or views per hour, in the app and by email. 1 credit a tracked channel a day. Same as POST /v1/trackers.

create_tracker input schema
{
  "type": "object",
  "title": "create_trackerArguments",
  "required": [
    "name"
  ],
  "properties": {
    "name": {
      "type": "string",
      "title": "Name",
      "maxLength": 80,
      "minLength": 1
    },
    "team": {
      "type": "boolean",
      "title": "Team",
      "default": false,
      "description": "true: your team's tracker, which every member sees and edits"
    },
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "channels": {
      "anyOf": [
        {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        {
          "type": "null"
        }
      ],
      "title": "Channels",
      "default": null,
      "description": "UC ids, @handles or addresses"
    },
    "visibility": {
      "type": "string",
      "title": "Visibility",
      "default": "private",
      "description": "private, shared or public"
    },
    "alert_email": {
      "anyOf": [
        {
          "type": "boolean"
        },
        {
          "type": "null"
        }
      ],
      "title": "Alert Email",
      "default": null
    },
    "vph_threshold": {
      "anyOf": [
        {
          "type": "number",
          "exclusiveMinimum": 0
        },
        {
          "type": "null"
        }
      ],
      "title": "Vph Threshold",
      "default": null
    },
    "multiple_threshold": {
      "anyOf": [
        {
          "type": "number",
          "maximum": 1000,
          "minimum": 1
        },
        {
          "type": "null"
        }
      ],
      "title": "Multiple Threshold",
      "default": null
    }
  }
}
create_tracker output schema
{
  "type": "object",
  "title": "create_trackerOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
list_trackers
Advertised by the server during the last successful anonymous check.

Your trackers, or every public one. Free. Same as GET /v1/trackers.

list_trackers input schema
{
  "type": "object",
  "title": "list_trackersArguments",
  "properties": {
    "scope": {
      "type": "string",
      "title": "Scope",
      "default": "mine",
      "description": "mine or public"
    },
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    }
  }
}
list_trackers output schema
{
  "type": "object",
  "title": "list_trackersOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
get_tracker
Advertised by the server during the last successful anonymous check.

One tracker and its channels. Same as GET /v1/trackers/{tracker_id}.

get_tracker input schema
{
  "type": "object",
  "title": "get_trackerArguments",
  "required": [
    "tracker_id"
  ],
  "properties": {
    "token": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Token",
      "default": null
    },
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "tracker_id": {
      "type": "string",
      "title": "Tracker Id"
    }
  }
}
get_tracker output schema
{
  "type": "object",
  "title": "get_trackerOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
update_tracker
Advertised by the server during the last successful anonymous check.

Rename a tracker, share it, change its alert lines, or turn its daily ideas on. Same as PATCH /v1/trackers/{tracker_id}.

update_tracker input schema
{
  "type": "object",
  "title": "update_trackerArguments",
  "required": [
    "tracker_id"
  ],
  "properties": {
    "name": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Name",
      "default": null
    },
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "tracker_id": {
      "type": "string",
      "title": "Tracker Id"
    },
    "visibility": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Visibility",
      "default": null
    },
    "alert_email": {
      "anyOf": [
        {
          "type": "boolean"
        },
        {
          "type": "null"
        }
      ],
      "title": "Alert Email",
      "default": null
    },
    "daily_ideas": {
      "anyOf": [
        {
          "type": "boolean"
        },
        {
          "type": "null"
        }
      ],
      "title": "Daily Ideas",
      "default": null
    },
    "vph_threshold": {
      "anyOf": [
        {
          "type": "number"
        },
        {
          "type": "null"
        }
      ],
      "title": "Vph Threshold",
      "default": null
    },
    "multiple_threshold": {
      "anyOf": [
        {
          "type": "number"
        },
        {
          "type": "null"
        }
      ],
      "title": "Multiple Threshold",
      "default": null
    }
  }
}
update_tracker output schema
{
  "type": "object",
  "title": "update_trackerOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
delete_tracker
Advertised by the server during the last successful anonymous check.

Delete one of your trackers. Same as DELETE /v1/trackers/{tracker_id}.

delete_tracker input schema
{
  "type": "object",
  "title": "delete_trackerArguments",
  "required": [
    "tracker_id"
  ],
  "properties": {
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "tracker_id": {
      "type": "string",
      "title": "Tracker Id"
    }
  }
}
delete_tracker output schema
{
  "type": "object",
  "title": "delete_trackerOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
add_to_tracker
Advertised by the server during the last successful anonymous check.

Add channels to one of your trackers. Same as POST /v1/trackers/{tracker_id}/channels.

add_to_tracker input schema
{
  "type": "object",
  "title": "add_to_trackerArguments",
  "required": [
    "tracker_id",
    "channels"
  ],
  "properties": {
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "channels": {
      "type": "array",
      "items": {
        "type": "string"
      },
      "title": "Channels",
      "maxItems": 50,
      "minItems": 1
    },
    "tracker_id": {
      "type": "string",
      "title": "Tracker Id"
    }
  }
}
add_to_tracker output schema
{
  "type": "object",
  "title": "add_to_trackerOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
remove_from_tracker
Advertised by the server during the last successful anonymous check.

Take a channel off one of your trackers. Same as DELETE /v1/trackers/{tracker_id}/channels/{ref}.

remove_from_tracker input schema
{
  "type": "object",
  "title": "remove_from_trackerArguments",
  "required": [
    "tracker_id",
    "ref"
  ],
  "properties": {
    "ref": {
      "type": "string",
      "title": "Ref"
    },
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "tracker_id": {
      "type": "string",
      "title": "Tracker Id"
    }
  }
}
remove_from_tracker output schema
{
  "type": "object",
  "title": "remove_from_trackerOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
tracker_feed
Advertised by the server during the last successful anonymous check.

A tracker's channels' uploads, strongest first. Free. Same as GET /v1/trackers/{tracker_id}/feed.

tracker_feed input schema
{
  "type": "object",
  "title": "tracker_feedArguments",
  "required": [
    "tracker_id"
  ],
  "properties": {
    "days": {
      "type": "integer",
      "title": "Days",
      "default": 30,
      "maximum": 3650,
      "minimum": 1
    },
    "sort": {
      "type": "string",
      "title": "Sort",
      "default": "multiple",
      "description": "multiple, vph or recent"
    },
    "limit": {
      "type": "integer",
      "title": "Limit",
      "default": 50,
      "maximum": 100,
      "minimum": 1
    },
    "token": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Token",
      "default": null
    },
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "tracker_id": {
      "type": "string",
      "title": "Tracker Id"
    }
  }
}
tracker_feed output schema
{
  "type": "object",
  "title": "tracker_feedOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
clone_tracker
Advertised by the server during the last successful anonymous check.

Copy a public or shared tracker into a private one of yours. Same as POST /v1/trackers/{tracker_id}/clone.

clone_tracker input schema
{
  "type": "object",
  "title": "clone_trackerArguments",
  "required": [
    "tracker_id"
  ],
  "properties": {
    "token": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Token",
      "default": null
    },
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "tracker_id": {
      "type": "string",
      "title": "Tracker Id"
    }
  }
}
clone_tracker output schema
{
  "type": "object",
  "title": "clone_trackerOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
tracker_alerts
Advertised by the server during the last successful anonymous check.

Your tracker alerts, newest first, with the unread count. Free. Same as GET /v1/trackers/alerts.

tracker_alerts input schema
{
  "type": "object",
  "title": "tracker_alertsArguments",
  "properties": {
    "limit": {
      "type": "integer",
      "title": "Limit",
      "default": 50,
      "maximum": 200,
      "minimum": 1
    },
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "mark_read": {
      "type": "boolean",
      "title": "Mark Read",
      "default": false
    },
    "unread_only": {
      "type": "boolean",
      "title": "Unread Only",
      "default": false
    }
  }
}
tracker_alerts output schema
{
  "type": "object",
  "title": "tracker_alertsOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
create_alert
Advertised by the server during the last successful anonymous check.

Save a search checked daily or weekly; a new video at your line (default 3x its channel) is a hit, emailed, with a Monday digest. 1 credit a check. Same as POST /v1/alerts.

create_alert input schema
{
  "type": "object",
  "title": "create_alertArguments",
  "required": [
    "query"
  ],
  "properties": {
    "email": {
      "anyOf": [
        {
          "type": "boolean"
        },
        {
          "type": "null"
        }
      ],
      "title": "Email",
      "default": null
    },
    "query": {
      "type": "string",
      "title": "Query",
      "maxLength": 200,
      "minLength": 3
    },
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "min_vph": {
      "anyOf": [
        {
          "type": "number",
          "exclusiveMinimum": 0
        },
        {
          "type": "null"
        }
      ],
      "title": "Min Vph",
      "default": null
    },
    "frequency": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Frequency",
      "default": null,
      "description": "daily or weekly"
    },
    "min_multiple": {
      "anyOf": [
        {
          "type": "number",
          "maximum": 1000,
          "minimum": 1
        },
        {
          "type": "null"
        }
      ],
      "title": "Min Multiple",
      "default": null
    }
  }
}
create_alert output schema
{
  "type": "object",
  "title": "create_alertOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
list_alerts
Advertised by the server during the last successful anonymous check.

Your saved searches and their newest hits. Free. Same as GET /v1/alerts.

list_alerts input schema
{
  "type": "object",
  "title": "list_alertsArguments",
  "properties": {
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    }
  }
}
list_alerts output schema
{
  "type": "object",
  "title": "list_alertsOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
get_alert
Advertised by the server during the last successful anonymous check.

One saved search and its hits. Same as GET /v1/alerts/{alert_id}.

get_alert input schema
{
  "type": "object",
  "title": "get_alertArguments",
  "required": [
    "alert_id"
  ],
  "properties": {
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "alert_id": {
      "type": "string",
      "title": "Alert Id"
    }
  }
}
get_alert output schema
{
  "type": "object",
  "title": "get_alertOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
update_alert
Advertised by the server during the last successful anonymous check.

Change a saved search: its words, line, frequency or email. Same as PATCH /v1/alerts/{alert_id}.

update_alert input schema
{
  "type": "object",
  "title": "update_alertArguments",
  "required": [
    "alert_id"
  ],
  "properties": {
    "email": {
      "anyOf": [
        {
          "type": "boolean"
        },
        {
          "type": "null"
        }
      ],
      "title": "Email",
      "default": null
    },
    "query": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Query",
      "default": null
    },
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "min_vph": {
      "anyOf": [
        {
          "type": "number"
        },
        {
          "type": "null"
        }
      ],
      "title": "Min Vph",
      "default": null
    },
    "alert_id": {
      "type": "string",
      "title": "Alert Id"
    },
    "frequency": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Frequency",
      "default": null
    },
    "daily_ideas": {
      "anyOf": [
        {
          "type": "boolean"
        },
        {
          "type": "null"
        }
      ],
      "title": "Daily Ideas",
      "default": null
    },
    "min_multiple": {
      "anyOf": [
        {
          "type": "number"
        },
        {
          "type": "null"
        }
      ],
      "title": "Min Multiple",
      "default": null
    }
  }
}
update_alert output schema
{
  "type": "object",
  "title": "update_alertOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
delete_alert
Advertised by the server during the last successful anonymous check.

Delete a saved search. Same as DELETE /v1/alerts/{alert_id}.

delete_alert input schema
{
  "type": "object",
  "title": "delete_alertArguments",
  "required": [
    "alert_id"
  ],
  "properties": {
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "alert_id": {
      "type": "string",
      "title": "Alert Id"
    }
  }
}
delete_alert output schema
{
  "type": "object",
  "title": "delete_alertOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
run_alert
Advertised by the server during the last successful anonymous check.

Check a saved search now, as a job (read it with get_job). 1 credit. Same as POST /v1/alerts/{alert_id}/run.

run_alert input schema
{
  "type": "object",
  "title": "run_alertArguments",
  "required": [
    "alert_id"
  ],
  "properties": {
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "alert_id": {
      "type": "string",
      "title": "Alert Id"
    }
  }
}
run_alert output schema
{
  "type": "object",
  "title": "run_alertOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
create_webhook
Advertised by the server during the last successful anonymous check.

Post every new tracker alert and saved-search hit to your https URL, signed with the secret this returns once (X-HD-Signature). Same as POST /v1/webhooks.

create_webhook input schema
{
  "type": "object",
  "title": "create_webhookArguments",
  "required": [
    "url"
  ],
  "properties": {
    "url": {
      "type": "string",
      "title": "Url",
      "maxLength": 500,
      "description": "https, port 443, public host"
    },
    "events": {
      "anyOf": [
        {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        {
          "type": "null"
        }
      ],
      "title": "Events",
      "default": null,
      "description": "tracker.alert, search.hit"
    },
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    }
  }
}
create_webhook output schema
{
  "type": "object",
  "title": "create_webhookOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
list_webhooks
Advertised by the server during the last successful anonymous check.

Your webhooks with their events and state (never the secret). Same as GET /v1/webhooks.

list_webhooks input schema
{
  "type": "object",
  "title": "list_webhooksArguments",
  "properties": {
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    }
  }
}
list_webhooks output schema
{
  "type": "object",
  "title": "list_webhooksOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
delete_webhook
Advertised by the server during the last successful anonymous check.

Delete one of your webhooks. Same as DELETE /v1/webhooks/{webhook_id}.

delete_webhook input schema
{
  "type": "object",
  "title": "delete_webhookArguments",
  "required": [
    "webhook_id"
  ],
  "properties": {
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "webhook_id": {
      "type": "string",
      "title": "Webhook Id"
    }
  }
}
delete_webhook output schema
{
  "type": "object",
  "title": "delete_webhookOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
test_webhook
Advertised by the server during the last successful anonymous check.

Send one signed test delivery now and read what your URL answered. Same as POST /v1/webhooks/{webhook_id}/test.

test_webhook input schema
{
  "type": "object",
  "title": "test_webhookArguments",
  "required": [
    "webhook_id"
  ],
  "properties": {
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "webhook_id": {
      "type": "string",
      "title": "Webhook Id"
    }
  }
}
test_webhook output schema
{
  "type": "object",
  "title": "test_webhookOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
webhook_deliveries
Advertised by the server during the last successful anonymous check.

A webhook's deliveries, newest first, with status and your URL's answers. Same as GET /v1/webhooks/{webhook_id}/deliveries.

webhook_deliveries input schema
{
  "type": "object",
  "title": "webhook_deliveriesArguments",
  "required": [
    "webhook_id"
  ],
  "properties": {
    "limit": {
      "type": "integer",
      "title": "Limit",
      "default": 50,
      "maximum": 200,
      "minimum": 1
    },
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "webhook_id": {
      "type": "string",
      "title": "Webhook Id"
    }
  }
}
webhook_deliveries output schema
{
  "type": "object",
  "title": "webhook_deliveriesOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
video_scorecard
Advertised by the server during the last successful anonymous check.

Everything the index knows about one video: the multiple, views per hour and its history, real tags, title and thumbnail changes, our reads, similar videos. Free. Same as GET /v1/videos/{video_id}/scorecard.

video_scorecard input schema
{
  "type": "object",
  "title": "video_scorecardArguments",
  "required": [
    "video_id"
  ],
  "properties": {
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "video_id": {
      "type": "string",
      "title": "Video Id",
      "description": "an 11-character YouTube id"
    }
  }
}
video_scorecard output schema
{
  "type": "object",
  "title": "video_scorecardOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
list_trends
Advertised by the server during the last successful anonymous check.

Rising words and phrases in the shared index's titles, this window against the one before, with counts and growth. Free. Same as GET /v1/trends.

list_trends input schema
{
  "type": "object",
  "title": "list_trendsArguments",
  "properties": {
    "window": {
      "type": "integer",
      "title": "Window",
      "default": 7,
      "description": "7, 30 or 90 days"
    },
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    }
  }
}
list_trends output schema
{
  "type": "object",
  "title": "list_trendsOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
list_formats
Advertised by the server during the last successful anonymous check.

Title formats of the last 90 days with their median multiple, share over 1.5x, adoption and examples, ordered by the Wilson lower bound of that share (computed_from). Formats with at least 8 measured videos from 3 channels (each shows measured beside videos); include_small lists down to 3 from 2. Free. Same as GET /v1/formats.

list_formats input schema
{
  "type": "object",
  "title": "list_formatsArguments",
  "properties": {
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "include_small": {
      "type": "boolean",
      "title": "Include Small",
      "default": false
    }
  }
}
list_formats output schema
{
  "type": "object",
  "title": "list_formatsOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
format_videos
Advertised by the server during the last successful anonymous check.

One title format's videos, strongest first. Free. Same as GET /v1/formats/{format_id}.

format_videos input schema
{
  "type": "object",
  "title": "format_videosArguments",
  "required": [
    "format_id"
  ],
  "properties": {
    "limit": {
      "type": "integer",
      "title": "Limit",
      "default": 50,
      "maximum": 100,
      "minimum": 1
    },
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "format_id": {
      "type": "string",
      "title": "Format Id"
    }
  }
}
format_videos output schema
{
  "type": "object",
  "title": "format_videosOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
daily_ideas
Advertised by the server during the last successful anonymous check.

Your daily idea sets (turn them on per saved search or tracker with daily_ideas), each idea with the real finds behind it, none repeated within a week. Same as GET /v1/ideas/daily.

daily_ideas input schema
{
  "type": "object",
  "title": "daily_ideasArguments",
  "properties": {
    "days": {
      "type": "integer",
      "title": "Days",
      "default": 7,
      "maximum": 60,
      "minimum": 1
    },
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "source_id": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Source Id",
      "default": null
    }
  }
}
daily_ideas output schema
{
  "type": "object",
  "title": "daily_ideasOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
create_agent
Advertised by the server during the last successful anonymous check.

An agent that runs a Video Review on each new long upload of a tracker's channels, inside a monthly credit budget. Same as POST /v1/agents.

create_agent input schema
{
  "type": "object",
  "title": "create_agentArguments",
  "required": [
    "tracker_id",
    "name",
    "monthly_budget"
  ],
  "properties": {
    "name": {
      "type": "string",
      "title": "Name",
      "maxLength": 80,
      "minLength": 1
    },
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "tracker_id": {
      "type": "string",
      "title": "Tracker Id"
    },
    "monthly_budget": {
      "type": "integer",
      "title": "Monthly Budget",
      "maximum": 10000,
      "minimum": 1
    }
  }
}
create_agent output schema
{
  "type": "object",
  "title": "create_agentOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
list_agents
Advertised by the server during the last successful anonymous check.

Your agents with their budgets and this month's spend. Free. Same as GET /v1/agents.

list_agents input schema
{
  "type": "object",
  "title": "list_agentsArguments",
  "properties": {
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    }
  }
}
list_agents output schema
{
  "type": "object",
  "title": "list_agentsOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
get_agent
Advertised by the server during the last successful anonymous check.

One agent and its reviews (done, skipped or failed, and why). Same as GET /v1/agents/{agent_id}.

get_agent input schema
{
  "type": "object",
  "title": "get_agentArguments",
  "required": [
    "agent_id"
  ],
  "properties": {
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "agent_id": {
      "type": "string",
      "title": "Agent Id"
    }
  }
}
get_agent output schema
{
  "type": "object",
  "title": "get_agentOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
update_agent
Advertised by the server during the last successful anonymous check.

Change an agent's name or monthly budget, or pause it. Same as PATCH /v1/agents/{agent_id}.

update_agent input schema
{
  "type": "object",
  "title": "update_agentArguments",
  "required": [
    "agent_id"
  ],
  "properties": {
    "name": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Name",
      "default": null
    },
    "active": {
      "anyOf": [
        {
          "type": "boolean"
        },
        {
          "type": "null"
        }
      ],
      "title": "Active",
      "default": null
    },
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "agent_id": {
      "type": "string",
      "title": "Agent Id"
    },
    "monthly_budget": {
      "anyOf": [
        {
          "type": "integer"
        },
        {
          "type": "null"
        }
      ],
      "title": "Monthly Budget",
      "default": null
    }
  }
}
update_agent output schema
{
  "type": "object",
  "title": "update_agentOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
delete_agent
Advertised by the server during the last successful anonymous check.

Delete one of your agents. Same as DELETE /v1/agents/{agent_id}.

delete_agent input schema
{
  "type": "object",
  "title": "delete_agentArguments",
  "required": [
    "agent_id"
  ],
  "properties": {
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "agent_id": {
      "type": "string",
      "title": "Agent Id"
    }
  }
}
delete_agent output schema
{
  "type": "object",
  "title": "delete_agentOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
get_skeleton
Advertised by the server during the last successful anonymous check.

The skeleton of a long find's whole video, heard by Scribe v2: sections, re-hooks, open loops, payoffs, sponsor and call-to-action spans, the outro, the timed lines. Builds it once for 5 credits as a paid call (status pending; read it with get_call); after that answers the stored one free. Same as POST and GET /v1/finds/{find_id}/skeleton.

get_skeleton input schema
{
  "type": "object",
  "title": "get_skeletonArguments",
  "properties": {
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "find_id": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Find Id",
      "default": null,
      "description": "one of your long finds"
    },
    "idempotency_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Idempotency Key",
      "default": null,
      "description": "1 to 255 visible ASCII characters; the same key and request within 24 hours answer the saved remix and charge nothing; the same key with another request is 422 idempotency_key_reused"
    }
  }
}
get_skeleton output schema
{
  "type": "object",
  "title": "get_skeletonOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
review_video
Advertised by the server during the last successful anonymous check.

Video Review of any public long video (15 credits): five ranked fixes with times and the counted fact behind each, six sub-scores each with what it is computed from, the hook scored and rewritten (labelled as written), the curve (YouTube's replay peaks, not retention; your Studio curve via graph_points; else predicted, not measured), the story with gap rules, pace and loudness per section, the next five uploads. Runs as a job: answers the paid call (pending); get_call reads it. Same as POST /v1/review.

review_video input schema
{
  "type": "object",
  "title": "review_videoArguments",
  "properties": {
    "url": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Url",
      "default": null,
      "description": "a YouTube link: watch, youtu.be or shorts address"
    },
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "graph_points": {
      "anyOf": [
        {
          "type": "array",
          "items": {
            "type": "number"
          }
        },
        {
          "type": "null"
        }
      ],
      "title": "Graph Points",
      "default": null,
      "description": "your real curve from read_retention_graph"
    },
    "idempotency_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Idempotency Key",
      "default": null,
      "description": "1 to 255 visible ASCII characters; the same key and request within 24 hours answer the saved remix and charge nothing; the same key with another request is 422 idempotency_key_reused"
    }
  }
}
review_video output schema
{
  "type": "object",
  "title": "review_videoOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
read_retention_graph
Advertised by the server during the last successful anonymous check.

Read a YouTube Studio audience retention screenshot (base64 PNG or JPEG) into 100 points and the 21 marks it was read from (1 credit). Not the chart is 422 not_a_retention_chart, free. Same as POST /v1/review/graph.

read_retention_graph input schema
{
  "type": "object",
  "title": "read_retention_graphArguments",
  "properties": {
    "image": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Image",
      "default": null,
      "description": "the screenshot as base64"
    },
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "idempotency_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Idempotency Key",
      "default": null,
      "description": "1 to 255 visible ASCII characters; the same key and request within 24 hours answer the saved remix and charge nothing; the same key with another request is 422 idempotency_key_reused"
    }
  }
}
read_retention_graph output schema
{
  "type": "object",
  "title": "read_retention_graphOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
review_hook
Advertised by the server during the last successful anonymous check.

Hook Review of your own opening (3 credits): a YouTube link (its first minute heard by Scribe v2) or a pasted script, scored out of 10 from named counted parts, a verdict per beat, and the promise check against your title and thumbnail words. Same as POST /v1/review/hook.

review_hook input schema
{
  "type": "object",
  "title": "review_hookArguments",
  "properties": {
    "url": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Url",
      "default": null,
      "description": "a YouTube link: watch, youtu.be or shorts address"
    },
    "title": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Title",
      "default": null
    },
    "script": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Script",
      "default": null,
      "description": "or your opening as pasted words"
    },
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "idempotency_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Idempotency Key",
      "default": null,
      "description": "1 to 255 visible ASCII characters; the same key and request within 24 hours answer the saved remix and charge nothing; the same key with another request is 422 idempotency_key_reused"
    },
    "thumbnail_words": {
      "anyOf": [
        {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        {
          "type": "null"
        }
      ],
      "title": "Thumbnail Words",
      "default": null
    }
  }
}
review_hook output schema
{
  "type": "object",
  "title": "review_hookOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
write_hook
Advertised by the server during the last successful anonymous check.

Hook Creator (1 credit): an opening for your subject as beats with seconds counted from their words and a shot plan per line (said, shown, on screen, sound, delivery), labelled as written. Same as POST /v1/openings.

write_hook input schema
{
  "type": "object",
  "title": "write_hookArguments",
  "properties": {
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "subject": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Subject",
      "default": null
    },
    "hook_type": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Hook Type",
      "default": null
    },
    "idempotency_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Idempotency Key",
      "default": null,
      "description": "1 to 255 visible ASCII characters; the same key and request within 24 hours answer the saved remix and charge nothing; the same key with another request is 422 idempotency_key_reused"
    }
  }
}
write_hook output schema
{
  "type": "object",
  "title": "write_hookOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
write_script
Advertised by the server during the last successful anonymous check.

Script Writer (10 credits): hooks, the plan's sections with re-hooks and payoffs, the script, its runtime counted from its words, machine-sounding lines flagged, a fact-check list, Markdown. Runs as a job (get_call reads it). Same as POST /v1/scripts.

write_script input schema
{
  "type": "object",
  "title": "write_scriptArguments",
  "properties": {
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "minutes": {
      "type": "integer",
      "title": "Minutes",
      "default": 8
    },
    "subject": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Subject",
      "default": null
    },
    "audience": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Audience",
      "default": null
    },
    "idempotency_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Idempotency Key",
      "default": null,
      "description": "1 to 255 visible ASCII characters; the same key and request within 24 hours answer the saved remix and charge nothing; the same key with another request is 422 idempotency_key_reused"
    }
  }
}
write_script output schema
{
  "type": "object",
  "title": "write_scriptOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
review_script
Advertised by the server during the last successful anonymous check.

Script Review (3 credits): your pasted script read like a Video Review on estimated times, plus machine-sounding lines and a fact-check list. Runs as a job. Same as POST /v1/scripts/review.

review_script input schema
{
  "type": "object",
  "title": "review_scriptArguments",
  "properties": {
    "title": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Title",
      "default": null
    },
    "script": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Script",
      "default": null
    },
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "idempotency_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Idempotency Key",
      "default": null,
      "description": "1 to 255 visible ASCII characters; the same key and request within 24 hours answer the saved remix and charge nothing; the same key with another request is 422 idempotency_key_reused"
    }
  }
}
review_script output schema
{
  "type": "object",
  "title": "review_scriptOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
review_edit
Advertised by the server during the last successful anonymous check.

Edit Review of a cut before upload (25 credits; an unlisted or public YouTube link up to 60 minutes): cuts a minute, long shots, dead air, sound changes, on-screen text, and the notes as Premiere CSV, Resolve EDL and FCPXML markers. Runs as a job. Same as POST /v1/review/edit; to review the file itself (when YouTube refuses our servers the link is refused free with upload_instead), send it to POST /v1/review/edit/upload.

review_edit input schema
{
  "type": "object",
  "title": "review_editArguments",
  "properties": {
    "url": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Url",
      "default": null,
      "description": "a YouTube link: watch, youtu.be or shorts address"
    },
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "idempotency_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Idempotency Key",
      "default": null,
      "description": "1 to 255 visible ASCII characters; the same key and request within 24 hours answer the saved remix and charge nothing; the same key with another request is 422 idempotency_key_reused"
    }
  }
}
review_edit output schema
{
  "type": "object",
  "title": "review_editOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
write_upload_pack
Advertised by the server during the last successful anonymous check.

The upload pack (1 credit) for a long find or your video: a description with chapters that pass YouTube's rules, tags and hashtags. Runs as a job. Same as POST /v1/seo.

write_upload_pack input schema
{
  "type": "object",
  "title": "write_upload_packArguments",
  "properties": {
    "url": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Url",
      "default": null,
      "description": "a YouTube link: watch, youtu.be or shorts address"
    },
    "links": {
      "anyOf": [
        {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        {
          "type": "null"
        }
      ],
      "title": "Links",
      "default": null
    },
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "find_id": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Find Id",
      "default": null
    },
    "idempotency_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Idempotency Key",
      "default": null,
      "description": "1 to 255 visible ASCII characters; the same key and request within 24 hours answer the saved remix and charge nothing; the same key with another request is 422 idempotency_key_reused"
    }
  }
}
write_upload_pack output schema
{
  "type": "object",
  "title": "write_upload_packOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
clean_recording
Advertised by the server during the last successful anonymous check.

The talking-head cleaner (15 credits), like 1of10's AutoCut: silences tightened, retakes, false starts, stutters and fillers cut with a reason on every cut, other microphones or cameras synced to the first, as Premiere XML, FCPXML and a CMX 3600 EDL. Takes 1 to 4 file_id values staged with POST /v1/clean/files (a chat cannot send the file itself). Runs as a job; get_call reads it. Same as POST /v1/clean.

clean_recording input schema
{
  "type": "object",
  "title": "clean_recordingArguments",
  "properties": {
    "files": {
      "anyOf": [
        {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        {
          "type": "null"
        }
      ],
      "title": "Files",
      "default": null
    },
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "idempotency_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Idempotency Key",
      "default": null,
      "description": "1 to 255 visible ASCII characters; the same key and request within 24 hours answer the saved remix and charge nothing; the same key with another request is 422 idempotency_key_reused"
    }
  }
}
clean_recording output schema
{
  "type": "object",
  "title": "clean_recordingOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
trending_shorts
Advertised by the server during the last successful anonymous check.

Trending Shorts: the short videos (TikToks, Instagram Reels, YouTube Shorts) rising now on a subject, fastest first. Each item has 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 it 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 v2 heard it). At most 20 items, 2 per creator; a video with no post date is never listed. unavailable names a platform whose search did not answer. Free (credits_charged 0); the same subject, platform and window within 30 minutes is served from the cache, and fresh fetches are limited per account per hour (429 rate_limited). 503 search_unavailable when no platform answered. Needs read. Same as GET /v1/shorts/trending.

trending_shorts input schema
{
  "type": "object",
  "title": "trending_shortsArguments",
  "properties": {
    "window": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Window",
      "default": null,
      "description": "24h, 7d (default) or 30d: how recently they were posted"
    },
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "subject": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Subject",
      "default": null,
      "description": "what the videos are about, 2 to 120 characters"
    },
    "platform": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Platform",
      "default": null,
      "description": "tiktok, instagram or youtube; leave out for all three"
    }
  }
}
trending_shorts output schema
{
  "type": "object",
  "title": "trending_shortsOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
review_short
Advertised by the server during the last successful anonymous check.

Rate my Reel or Short: read one public short the way Hook Detector reads a clip (its first frames, its words heard by ElevenLabs Scribe v2) and rate it against the real finds on its subject (the Short form search over the last 30 days, ranked by their multiple over the creator's usual). Returns review_id, url, platform, video_id, subject, subject_from, clip (first_line, first_word_s, words_per_minute, longest_pause_s, words, heard_by, not_heard_why, onscreen_text, plays, multiple), reads (five, in order: stop, gap, momentum, feeling, reach; each with verdict strong, moderate or weak, says, computed_from counted against the finds, and finds: 1 to 3 ids from `finds`), finds (each a real video with its url, plays, multiple, first_line and pace), finds_from, label, credits_charged and credits. Never a guessed number. Costs 3 credits, taken first and given back on any failure: 422 invalid_link (before any charge), 404 video_not_found (private or removed), 422 not_a_short, 422 review_not_possible (too few finds on the subject), 503 model_unavailable. 402 insufficient_credits under 3; limited per account per hour (429). Takes up to about 90 seconds. Needs research. Same as POST /v1/review/short.

review_short input schema
{
  "type": "object",
  "title": "review_shortArguments",
  "properties": {
    "url": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Url",
      "default": null,
      "description": "a public Instagram Reel, TikTok video or YouTube Short link, starting with https://"
    },
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "subject": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Subject",
      "default": null,
      "description": "what the clip is about, 2 to 120 characters; leave out to read it from the clip"
    },
    "idempotency_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Idempotency Key",
      "default": null,
      "description": "retry safely: the same key and arguments within 24 hours answer the saved review and charge nothing"
    }
  }
}
review_short output schema
{
  "type": "object",
  "title": "review_shortOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
research_keywords
Advertised by the server during the last successful anonymous check.

Keyword research for YouTube. volume: always estimated (YouTube publishes no search counts): estimated_monthly_searches with a range, a 0 to 100 score and band, its source (Google Trends with the YouTube filter against fixed anchors; or, when Trends is busy, YouTube autocomplete and the top results' views) and basis. competition: 0 to 100 counted from the top 10 long-form results (big channels, posted in the last year, the keyword in the title, how many beat their own channel 2x). score: value 0 to 100 (60% volume, 40% the room competition leaves), band very_low, low, medium, high or very_high (vidIQ's brackets) and the basis naming its inputs. related, questions, rising: terms from autocomplete and the top results' titles, each with its source. Costs 2 credits, refunded when the read fails; a bad request is 422 and costs nothing. Takes about 10 to 30 seconds. Same as POST /v1/keywords. Read it back with get_call.

research_keywords input schema
{
  "type": "object",
  "title": "research_keywordsArguments",
  "properties": {
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "country": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Country",
      "default": null,
      "description": "a two-letter country code, default US"
    },
    "keyword": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Keyword",
      "default": null,
      "description": "the words people would type into YouTube search, 2 to 100 characters"
    },
    "language": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Language",
      "default": null,
      "description": "a two-letter language code for the autocomplete, default en"
    },
    "idempotency_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Idempotency Key",
      "default": null,
      "description": "1 to 255 visible ASCII characters; the same key and request within 24 hours answer the saved remix and charge nothing; the same key with another request is 422 idempotency_key_reused"
    }
  }
}
research_keywords output schema
{
  "type": "object",
  "title": "research_keywordsOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
comment_insights
Advertised by the server during the last successful anonymous check.

What a subject's audience asks and struggles with: the top comments of its outliers (videos that beat their own channel; or your video_ids, or a channel's best uploads), grouped into questions, pain points and requests. clusters: label, kind (question, pain_point, request, other), count (comments in the group, counted in code, never by the model), likes, videos, and quotes: two real comments found word for word, each with its link (url opens the comment). videos: what was read, with views, multiple (views over the channel's usual), comments_read and comments_off. basis says what the counts are from. Costs 3 credits, refunded when the read fails; a request naming nothing or two things is 422 and costs nothing. Takes about 60 to 100 seconds (measured 2026-10-26). Same as POST /v1/comments/insights. Read it back with get_call.

comment_insights input schema
{
  "type": "object",
  "title": "comment_insightsArguments",
  "properties": {
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "channel": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Channel",
      "default": null,
      "description": "a YouTube @handle or channel link: its uploads that beat its usual views"
    },
    "subject": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Subject",
      "default": null,
      "description": "what the videos are about, 3 to 120 characters: its outliers are read. Send this, video_ids or channel"
    },
    "video_ids": {
      "anyOf": [
        {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        {
          "type": "null"
        }
      ],
      "title": "Video Ids",
      "default": null,
      "description": "1 to 8 YouTube video ids or links whose comments to read"
    },
    "idempotency_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Idempotency Key",
      "default": null,
      "description": "1 to 255 visible ASCII characters; the same key and request within 24 hours answer the saved remix and charge nothing; the same key with another request is 422 idempotency_key_reused"
    }
  }
}
comment_insights output schema
{
  "type": "object",
  "title": "comment_insightsOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
find_sponsors
Advertised by the server during the last successful anonymous check.

Who sponsors the channels on a subject: in the descriptions of its outliers, sponsor lines, promo codes and offer links found in code, plus Scrape Creators' sponsor read of videos YouTube marks as paid promotion, each named by one model read and kept only when the brand's name is in its own line. sponsors: brand, kind (sponsor, affiliate, other), channel_count and video_count (counted in code), channels, and videos with the evidence line as written, the code and the link. Costs 1 credit, refunded when the read fails. Same as POST /v1/sponsors. Read it back with get_call.

find_sponsors input schema
{
  "type": "object",
  "title": "find_sponsorsArguments",
  "properties": {
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "subject": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Subject",
      "default": null,
      "description": "3 to 120 characters"
    },
    "idempotency_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Idempotency Key",
      "default": null,
      "description": "1 to 255 visible ASCII characters; the same key and request within 24 hours answer the saved remix and charge nothing; the same key with another request is 422 idempotency_key_reused"
    }
  }
}
find_sponsors output schema
{
  "type": "object",
  "title": "find_sponsorsOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
get_team
Advertised by the server during the last successful anonymous check.

Your team, or team null: its name, pool (credits), seats, each member's name and role. The owner also sees every member's email, monthly cap and spend, open invites (never their links) and the newest 50 charges to the pool (who, what kind, credits, when; never what it was about). your_credits is your own balance, untouched while you are a member. Free. Same as GET /v1/team.

get_team input schema
{
  "type": "object",
  "title": "get_teamArguments",
  "properties": {
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    }
  }
}
get_team output schema
{
  "type": "object",
  "title": "get_teamOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
create_team
Advertised by the server during the last successful anonymous check.

Create a team this account owns, funding its pool from your balance with credits (402 when you hold less). Members' spending comes from the pool; you keep spending your own balance. 409 already_in_team. Needs admin. Free. Same as POST /v1/team.

create_team input schema
{
  "type": "object",
  "title": "create_teamArguments",
  "required": [
    "name"
  ],
  "properties": {
    "name": {
      "type": "string",
      "title": "Name",
      "description": "the team's name, 1 to 80 characters"
    },
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "credits": {
      "anyOf": [
        {
          "type": "integer"
        },
        {
          "type": "null"
        }
      ],
      "title": "Credits",
      "default": null,
      "description": "credits to move from your balance into the team's pool now; 0 or left out moves none"
    }
  }
}
create_team output schema
{
  "type": "object",
  "title": "create_teamOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
move_team_credits
Advertised by the server during the last successful anonymous check.

Move credits between your balance and the team's pool; 402 when the side giving holds less, and nothing moves. Owner only. Needs admin. Free. Same as POST /v1/team/credits.

move_team_credits input schema
{
  "type": "object",
  "title": "move_team_creditsArguments",
  "required": [
    "credits",
    "direction"
  ],
  "properties": {
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "credits": {
      "type": "integer",
      "title": "Credits",
      "description": "1 to 1,000,000"
    },
    "direction": {
      "enum": [
        "in",
        "out"
      ],
      "type": "string",
      "title": "Direction",
      "description": "\"in\": your balance to the pool; \"out\": the pool back to you"
    }
  }
}
move_team_credits output schema
{
  "type": "object",
  "title": "move_team_creditsOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
invite_to_team
Advertised by the server during the last successful anonymous check.

Invite someone: returns link (and its token), a single-use page valid 7 days, shown here only, and emailed to that address (emailed true; when false, give the link to the person yourself). Someone signed in with Google must hold that email to accept. Owner only. Needs admin. Free. Same as POST /v1/team/invites.

invite_to_team input schema
{
  "type": "object",
  "title": "invite_to_teamArguments",
  "required": [
    "email"
  ],
  "properties": {
    "email": {
      "type": "string",
      "title": "Email",
      "description": "the email of the person invited"
    },
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    }
  }
}
invite_to_team output schema
{
  "type": "object",
  "title": "invite_to_teamOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
revoke_team_invite
Advertised by the server during the last successful anonymous check.

Revoke an open invite: its link stops working at once. Owner only. Needs admin. Free. Same as DELETE /v1/team/invites/{invite_id}.

revoke_team_invite input schema
{
  "type": "object",
  "title": "revoke_team_inviteArguments",
  "required": [
    "invite_id"
  ],
  "properties": {
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "invite_id": {
      "type": "string",
      "title": "Invite Id",
      "description": "from get_team invites"
    }
  }
}
revoke_team_invite output schema
{
  "type": "object",
  "title": "revoke_team_inviteOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
view_team_invite
Advertised by the server during the last successful anonymous check.

What an invite joins, before accepting: the team's name, who invited you, its seats, when the link expires, and whether you are signed in with the invited email (null for an account with no email). Free. Same as GET /v1/team/invites/{token}.

view_team_invite input schema
{
  "type": "object",
  "title": "view_team_inviteArguments",
  "required": [
    "token"
  ],
  "properties": {
    "token": {
      "type": "string",
      "title": "Token",
      "description": "the hdt_... token, the part of the link after #"
    },
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    }
  }
}
view_team_invite output schema
{
  "type": "object",
  "title": "view_team_inviteOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
accept_team_invite
Advertised by the server during the last successful anonymous check.

Join the team an invite names. From then on what you spend comes from the team's pool; your own runs stay private and your own balance waits for you. Each link works once (409 invite_used) for 7 days (410 invite_expired). Needs admin. Free. Same as POST /v1/team/invites/{token}/accept.

accept_team_invite input schema
{
  "type": "object",
  "title": "accept_team_inviteArguments",
  "required": [
    "token"
  ],
  "properties": {
    "token": {
      "type": "string",
      "title": "Token",
      "description": "the hdt_... token, the part of the link after #"
    },
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    }
  }
}
accept_team_invite output schema
{
  "type": "object",
  "title": "accept_team_inviteOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
remove_team_member
Advertised by the server during the last successful anonymous check.

The owner removes a member; anyone leaves with member_id "me". A removed member's next spend comes from their own balance, never the pool. The owner leaving with members is 409 team_has_members; alone it ends the team and returns the pool. Needs admin. Free. Same as DELETE /v1/team/members/{member_id}.

remove_team_member input schema
{
  "type": "object",
  "title": "remove_team_memberArguments",
  "required": [
    "member_id"
  ],
  "properties": {
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "member_id": {
      "type": "string",
      "title": "Member Id",
      "description": "a member_id from get_team, or \"me\" to leave"
    }
  }
}
remove_team_member output schema
{
  "type": "object",
  "title": "remove_team_memberOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
set_team_member_cap
Advertised by the server during the last successful anonymous check.

Set a member's monthly cap on the pool (null removes it). A run started near the cap holds only what is left, so the cap is never passed mid-run (403 team_cap_reached below a run's minimum or a tool's price). Owner only. Needs admin. Free. Same as PATCH /v1/team/members/{member_id}.

set_team_member_cap input schema
{
  "type": "object",
  "title": "set_team_member_capArguments",
  "required": [
    "member_id",
    "monthly_cap"
  ],
  "properties": {
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "member_id": {
      "type": "string",
      "title": "Member Id",
      "description": "a member_id from get_team"
    },
    "monthly_cap": {
      "anyOf": [
        {
          "type": "integer"
        },
        {
          "type": "null"
        }
      ],
      "title": "Monthly Cap",
      "description": "1 to 1,000,000 credits a UTC month, or null for no cap"
    }
  }
}
set_team_member_cap output schema
{
  "type": "object",
  "title": "set_team_member_capOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
thumbnail_attention
Advertised by the server during the last successful anonymous check.

Where the eye is predicted to land on a thumbnail: the picture with a heatmap over it (PNG, base64), a 16 by 9 grid, the peak, the share in each third, the concentration and the share in YouTube's length corner. UNISAL (AUC 0.895 on MIT1003): faces draw it strongly, printed words less than people read them; a prediction, not a click-through rate. Free. Same as POST /v1/thumbnails/attention.

thumbnail_attention input schema
{
  "type": "object",
  "title": "thumbnail_attentionArguments",
  "properties": {
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "thumbnail": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Thumbnail",
      "default": null,
      "description": "a JPEG, PNG or WebP in base64 or an i.ytimg.com URL"
    },
    "video_url": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Video Url",
      "default": null,
      "description": "or a YouTube video: its own thumbnail is read"
    }
  }
}
thumbnail_attention output schema
{
  "type": "object",
  "title": "thumbnail_attentionOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
create_poll
Advertised by the server during the last successful anonymous check.

Put 2 to 4 packages to Hook Detector's users, who pick the one they would click, blind. Closes at votes_wanted or after 7 days. 2 credits, given back when a picture cannot be stored. Results with get_poll. Same as POST /v1/polls.

create_poll input schema
{
  "type": "object",
  "title": "create_pollArguments",
  "required": [
    "options"
  ],
  "properties": {
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "options": {
      "type": "array",
      "items": {},
      "title": "Options",
      "description": "2 to 4 packages, each {title, thumbnail}: a title of 1 to 100 characters and a JPEG, PNG or WebP in base64 or an i.ytimg.com URL"
    },
    "subject": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Subject",
      "default": null,
      "description": "what the video is about"
    },
    "question": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Question",
      "default": null,
      "description": "default: Which would you click?"
    },
    "votes_wanted": {
      "anyOf": [
        {
          "type": "integer"
        },
        {
          "type": "null"
        }
      ],
      "title": "Votes Wanted",
      "default": null,
      "description": "10 to 200, default 30"
    },
    "idempotency_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Idempotency Key",
      "default": null,
      "description": "1 to 255 visible ASCII characters; the same key and request within 24 hours answer the saved remix and charge nothing; the same key with another request is 422 idempotency_key_reused"
    }
  }
}
create_poll output schema
{
  "type": "object",
  "title": "create_pollOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
list_polls
Advertised by the server during the last successful anonymous check.

Your package votes, newest first, with their results so far. Free. Same as GET /v1/polls.

list_polls input schema
{
  "type": "object",
  "title": "list_pollsArguments",
  "properties": {
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    }
  }
}
list_polls output schema
{
  "type": "object",
  "title": "list_pollsOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
get_poll
Advertised by the server during the last successful anonymous check.

One poll: yours with votes, shares, 95% intervals, the leader once the votes separate, the slots and the reasons; or one you voted on with its shares. Free. Same as GET /v1/polls/{poll_id}.

get_poll input schema
{
  "type": "object",
  "title": "get_pollArguments",
  "required": [
    "poll_id"
  ],
  "properties": {
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "poll_id": {
      "type": "string",
      "title": "Poll Id",
      "description": "the poll's id"
    }
  }
}
get_poll output schema
{
  "type": "object",
  "title": "get_pollOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
delete_poll
Advertised by the server during the last successful anonymous check.

Delete your poll, its votes and its pictures. Free. Same as DELETE /v1/polls/{poll_id}.

delete_poll input schema
{
  "type": "object",
  "title": "delete_pollArguments",
  "required": [
    "poll_id"
  ],
  "properties": {
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "poll_id": {
      "type": "string",
      "title": "Poll Id",
      "description": "the poll's id"
    }
  }
}
delete_poll output schema
{
  "type": "object",
  "title": "delete_pollOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
next_poll
Advertised by the server during the last successful anonymous check.

A poll of another Hook Detector user waiting for your vote (not one you passed in skip): its options in an order of your own, no counts; poll null when none is waiting. Free. Same as GET /v1/polls/next.

next_poll input schema
{
  "type": "object",
  "title": "next_pollArguments",
  "properties": {
    "skip": {
      "anyOf": [
        {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        {
          "type": "null"
        }
      ],
      "title": "Skip",
      "default": null,
      "description": "poll ids to pass over in this ask"
    },
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    }
  }
}
next_poll output schema
{
  "type": "object",
  "title": "next_pollOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
vote_poll
Advertised by the server during the last successful anonymous check.

Vote on a poll with the slot you picked, and an optional reason. One vote an account, never on your own or your team's poll. Answers the shares so far. Free. Same as POST /v1/polls/{poll_id}/vote.

vote_poll input schema
{
  "type": "object",
  "title": "vote_pollArguments",
  "required": [
    "poll_id",
    "slot"
  ],
  "properties": {
    "slot": {
      "type": "integer",
      "title": "Slot",
      "description": "the slot you picked in next_poll's answer"
    },
    "reason": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Reason",
      "default": null,
      "description": "optional: why, at most 140 characters"
    },
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "poll_id": {
      "type": "string",
      "title": "Poll Id",
      "description": "the poll's id"
    }
  }
}
vote_poll output schema
{
  "type": "object",
  "title": "vote_pollOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
close_poll
Advertised by the server during the last successful anonymous check.

End your poll now: no more votes, the votes so far kept with its results. Free. Same as POST /v1/polls/{poll_id}/close.

close_poll input schema
{
  "type": "object",
  "title": "close_pollArguments",
  "required": [
    "poll_id"
  ],
  "properties": {
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    },
    "poll_id": {
      "type": "string",
      "title": "Poll Id",
      "description": "the poll's id"
    }
  }
}
close_poll output schema
{
  "type": "object",
  "title": "close_pollOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
video_badges
Advertised by the server during the last successful anonymous check.

Badges for up to 50 YouTube videos at once, as the Chrome extension shows them on every card: views, the multiple over the channel's usual views (long against long uploads, Shorts against Shorts) with its band, and views per hour with its basis. Ids with no badge are in unread with why. Free. Same as GET /v1/videos/badges.

video_badges input schema
{
  "type": "object",
  "title": "video_badgesArguments",
  "required": [
    "ids"
  ],
  "properties": {
    "ids": {
      "type": "array",
      "items": {
        "type": "string"
      },
      "title": "Ids",
      "description": "1 to 50 YouTube video ids"
    },
    "api_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Api Key",
      "default": null
    }
  }
}
video_badges output schema
{
  "type": "object",
  "title": "video_badgesOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
list_research
Advertised by the server during the last successful anonymous check.

The research findings counted from Hook Detector's shared index of public YouTube data: openings (which openings sit before a steeper or flatter first minute of YouTube's "most replayed" heatmap, replay intensity, not retention), title-devices (which title devices go with higher multiples, by niche) and title-thumbnail-overlap (title and thumbnail word overlap against the multiple). Each page with n per run and how many claims reproduced on a second sample of other channels, did not, or had too few videos. No key needed; limited per address (429). Free. Same as GET /v1/findings.

list_research input schema
{
  "type": "object",
  "title": "list_researchArguments",
  "properties": {}
}
list_research output schema
{
  "type": "object",
  "title": "list_researchOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}
get_research
Advertised by the server during the last successful anonymous check.

One research page: every claim with the first run's and the second run's numbers (each run a sample of different channels), n per run and per cell, shown_a and shown_b (over the sample bar), reproduced (true, false, or null for too few) and direction, plus measures naming what each number is computed from. 404 page_not_found for an unknown slug, 422 for a malformed one. No key needed; limited per address. Free. Same as GET /v1/findings/{slug}.

get_research input schema
{
  "type": "object",
  "title": "get_researchArguments",
  "required": [
    "slug"
  ],
  "properties": {
    "slug": {
      "type": "string",
      "title": "Slug",
      "description": "openings, title-devices or title-thumbnail-overlap"
    }
  }
}
get_research output schema
{
  "type": "object",
  "title": "get_researchOutput",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string",
      "title": "Result"
    }
  }
}

Prompts

0 advertised

No prompt definitions available.

Resources

0 advertised

No resource definitions available.

90-day uptime

A lightweight connection check about every six hours, sampled on hosts with many listings. It shows the server answered, not that its tools work.

Too few checks for a percentage yet (2 of 2 answered).

  1. 2026-11-06: 1 of 1 checks answered
  2. 2026-11-07: 1 of 1 checks answered

30-day availability

Aggregated from read-only remote checks; this is not a tool-execution or security guarantee.

Successful checks
1 of 1 (100%)
Average latency
1018 ms
95th percentile latency
1018 ms

Maintenance history

Public check and source-refresh events. Editorial and ownership records remain private.

  • · probe · passed · 1018 ms · metadata changed · 154 tool(s), 0 resource(s), 0 resource template(s), 0 prompt(s); warnings: Does not report a server version

Connect from your client

Each snippet registers the same endpoint; pick the client you use.

This server is operated by the named third party. Listing and endpoint checks are not an endorsement or a security audit. Review the operator's permissions, terms, and data handling before connecting. How listings are checked and corrected.

Claude Code

Run this once in a terminal. It registers the remote server. Use --scope project to write .mcp.json for the repo.
Claude Code configuration
claude mcp add --transport http hook-detector-mcp https://api.hookdetector.com/mcp

Codex

Run this once. Codex stores MCP servers in ~/.codex/config.toml (or .codex/config.toml in a trusted project).
Codex configuration
codex mcp add hook-detector-mcp --url https://api.hookdetector.com/mcp

Cursor

Merge this server into .cursor/mcp.json in your project, then enable hook-detector-mcp in your MCP settings.
Cursor configuration
{
  "mcpServers": {
    "hook-detector-mcp": {
      "url": "https://api.hookdetector.com/mcp"
    }
  }
}

Cline

Merge this into cline_mcp_settings.json (Cline → MCP Servers → Configure).
Cline configuration
{
  "mcpServers": {
    "hook-detector-mcp": {
      "type": "streamableHttp",
      "url": "https://api.hookdetector.com/mcp",
      "disabled": false
    }
  }
}

VS Code

Merge this server into .vscode/mcp.json. Use MCP: List Servers to start hook-detector-mcp and inspect its tools.
VS Code configuration
{
  "servers": {
    "hook-detector-mcp": {
      "type": "http",
      "url": "https://api.hookdetector.com/mcp"
    }
  }
}

Gemini CLI

Run this once. It writes .gemini/settings.json in the current project; pass --scope user for ~/.gemini/settings.json.
Gemini CLI configuration
gemini mcp add --transport http hook-detector-mcp https://api.hookdetector.com/mcp

Claude Desktop

Open Claude Settings > Connectors > Add custom connector, enter this server URL and enable it for the conversation. This is a manual remote-connector handoff, not a desktop JSON configuration.
Claude Desktop configuration
https://api.hookdetector.com/mcp

Windsurf

Merge this into ~/.codeium/windsurf/mcp_config.json, then refresh MCP in Cascade. Windsurf remote servers use serverUrl, not url.
Windsurf configuration
{
  "mcpServers": {
    "hook-detector-mcp": {
      "serverUrl": "https://api.hookdetector.com/mcp"
    }
  }
}

Let your agent find its tools

Find servers like Hook Detector MCP from your agent. Add Koskamo once to search the index for endpoints, access requirements, and client setup. Public discovery needs no key.

Do you run Hook Detector MCP?

Prove ownership with a DNS record, a well-known file, or your Official MCP Registry entry, and your edits publish immediately. Proofs are re-checked weekly; admins can still hide or remove a listing.

Claim this listing