{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://artifacts.apis.io/hookdetector-com/json-schema/hookdetector-com-find-out-schema.json",
  "title": "FindOut",
  "description": "One result of a long or packaging run. Sent back exactly as stored: a long find has\nthe opening fields, a packaging find the title and thumbnail fields, and a field one\nkind does not have is absent, never null.",
  "x-generated": "2026-11-11",
  "x-method": "derived",
  "x-generator": "derive-json-schema.py",
  "x-source": "openapi/hookdetector-com-openapi.json#/components/schemas/FindOut",
  "properties": {
    "find_id": {
      "type": "string",
      "format": "uuid",
      "title": "Find Id",
      "description": "the id to keep, reject or read it by; absent on an item of `progress.items`"
    },
    "run_id": {
      "type": "string",
      "format": "uuid",
      "title": "Run Id",
      "description": "the run that found it"
    },
    "kind": {
      "type": "string",
      "enum": [
        "long",
        "packaging"
      ],
      "title": "Kind",
      "description": "long or packaging"
    },
    "rank": {
      "type": "integer",
      "title": "Rank",
      "description": "its place in the run, from 1: the strongest outlier first"
    },
    "verdict": {
      "type": [
        "string",
        "null"
      ],
      "enum": [
        "keep",
        "reject",
        null
      ],
      "title": "Verdict",
      "description": "keep, reject or null"
    },
    "video_id": {
      "type": "string",
      "title": "Video Id",
      "description": "the YouTube video id"
    },
    "platform": {
      "type": "string",
      "title": "Platform",
      "description": "always \"youtube\""
    },
    "title": {
      "type": "string",
      "title": "Title",
      "description": "the video's title as YouTube shows it, word for word (runs of spaces read as one)"
    },
    "channel": {
      "type": [
        "string",
        "null"
      ],
      "title": "Channel",
      "description": "the channel's name"
    },
    "channel_handle": {
      "type": [
        "string",
        "null"
      ],
      "title": "Channel Handle",
      "description": "the channel's @handle, without the @"
    },
    "watch_url": {
      "type": "string",
      "title": "Watch Url",
      "description": "where to watch it"
    },
    "player_url": {
      "type": "string",
      "title": "Player Url",
      "description": "an iframe src that plays it (16:9)"
    },
    "thumbnail_url": {
      "type": "string",
      "title": "Thumbnail Url",
      "description": "the video's thumbnail on YouTube's image host; `thumbnail_urls` lists every size, best first, and a size a video lacks answers 404"
    },
    "views": {
      "type": "integer",
      "title": "Views",
      "description": "views when the run read it"
    },
    "likes": {
      "type": [
        "integer",
        "null"
      ],
      "title": "Likes",
      "description": "likes, when YouTube gave them"
    },
    "comments": {
      "type": [
        "integer",
        "null"
      ],
      "title": "Comments",
      "description": "comments, when YouTube gave them"
    },
    "age_days": {
      "type": [
        "number",
        "null"
      ],
      "title": "Age Days",
      "description": "days since it was posted, when the run read it"
    },
    "views_per_day": {
      "type": [
        "number",
        "null"
      ],
      "title": "Views Per Day",
      "description": "views divided by age_days"
    },
    "baseline_videos": {
      "type": [
        "integer",
        "null"
      ],
      "title": "Baseline Videos",
      "description": "how many recent uploads the channel's usual was measured on"
    },
    "found_by": {
      "type": [
        "string",
        "null"
      ],
      "title": "Found By",
      "description": "the search that surfaced it"
    },
    "duration_s": {
      "type": [
        "integer",
        "null"
      ],
      "title": "Duration S",
      "description": "length in seconds; always over 180"
    },
    "posted_at": {
      "type": [
        "string",
        "null"
      ],
      "title": "Posted At",
      "description": "when it was posted (ISO 8601). Exact when `posted_exact` is true; otherwise the latest it can be, from YouTube's \"3 months ago\", and never before 2023 either way"
    },
    "posted_exact": {
      "type": "boolean",
      "title": "Posted Exact",
      "description": "whether posted_at is the real timestamp"
    },
    "channel_median_views": {
      "type": [
        "integer",
        "null"
      ],
      "title": "Channel Median Views",
      "description": "the channel's usual: the median views of its recent long-form uploads"
    },
    "outlier": {
      "type": [
        "number",
        "null"
      ],
      "title": "Outlier",
      "description": "views divided by channel_median_views: 28.2 means 28 times what this channel normally gets"
    },
    "outlier_score": {
      "type": "integer",
      "title": "Outlier Score",
      "description": "the video on one fixed scale, 0 to 100: the outlier, its reach, how fresh it is and how hard its viewers reacted, placed between two fixed anchors. Nothing else a run found moves it, so finds from different runs rank together; it moves only with the video's own numbers and age"
    },
    "channel_size": {
      "type": [
        "string",
        "null"
      ],
      "enum": [
        "small",
        "medium",
        "large",
        null
      ],
      "title": "Channel Size",
      "description": "the channel by what its uploads usually get (channel_median_views): small, medium or large"
    },
    "seen_before": {
      "type": "boolean",
      "title": "Seen Before",
      "description": "true when an earlier run of yours on this tab already found this video; a keep or reject made then is already on it"
    },
    "analysis_from_earlier_read": {
      "type": "boolean",
      "title": "Analysis From Earlier Read",
      "description": "true when the opening or the package analysis is the one an earlier run of yours wrote for this video; the numbers are this run's"
    },
    "channel_id": {
      "type": [
        "string",
        "null"
      ],
      "title": "Channel Id",
      "description": "the channel's YouTube id (UC...)"
    },
    "thumbnail_urls": {
      "items": {
        "type": "string"
      },
      "type": "array",
      "title": "Thumbnail Urls",
      "description": "every thumbnail size, best first; a size a video lacks answers 404"
    },
    "score": {
      "type": [
        "number",
        "null"
      ],
      "title": "Score",
      "description": "the ranking score outlier_score is scaled from"
    },
    "hook_lines": {
      "type": "integer",
      "title": "Hook Lines",
      "description": "long: how many lines of `intro` the hook reaches, counted from line 1; the last may be only partly the hook, `hook_words` counts exactly"
    },
    "hook_end_s": {
      "type": "number",
      "title": "Hook End S",
      "description": "long: the second the hook ends, to a tenth, never null. Inside a line it is estimated at the opening's own pace"
    },
    "hook_start_s": {
      "type": "number",
      "title": "Hook Start S",
      "description": "long: the second the hook starts: 0 or close to it, later when a sponsor read or a greeting came first"
    },
    "hook_words": {
      "type": "integer",
      "title": "Hook Words",
      "description": "long: the hook's length in words"
    },
    "auto_captions": {
      "type": "boolean",
      "title": "Auto Captions",
      "description": "long: true when the opening carries almost no punctuation (auto captions): its sentences are not marked and names may be misheard"
    },
    "about_subject": {
      "type": [
        "string",
        "null"
      ],
      "enum": [
        "yes",
        "partly",
        null
      ],
      "title": "About Subject",
      "description": "long: whether the video is about the run's subject, read from its title and opening in the call that analyses it: yes, or partly (the subject is one part of it, or it covers a neighbouring subject). Null when the question was not asked (a channel or video link) or not answered. A video the model answers \"no\" for is never delivered"
    },
    "hook_type_other": {
      "type": [
        "string",
        "null"
      ],
      "title": "Hook Type Other",
      "description": "long: the device in words when hook_type is \"other\""
    },
    "title_device_other": {
      "type": [
        "string",
        "null"
      ],
      "title": "Title Device Other",
      "description": "packaging: the device in words when title_device is \"other\""
    },
    "multiple": {
      "type": [
        "number",
        "null"
      ],
      "title": "Multiple",
      "description": "views over channel_median_views, one decimal: 12.4 means 12.4 times what this channel normally gets (the number 1of10 and vidIQ show). Null when the channel was not measured"
    },
    "multiple_band": {
      "type": [
        "string",
        "null"
      ],
      "enum": [
        "10x",
        "5x",
        "2x",
        "under 2x",
        null
      ],
      "title": "Multiple Band",
      "description": "the multiple's band: 10x (10 or more), 5x (5 to under 10), 2x (2 to under 5), under 2x (a video asked for by its address, or a bar set under 2); null with no multiple"
    },
    "content_type": {
      "type": [
        "string",
        "null"
      ],
      "enum": [
        "long",
        "short",
        null
      ],
      "title": "Content Type",
      "description": "the kind of video: long (long-form) or short (a YouTube Short)"
    },
    "outlier_tier": {
      "type": [
        "string",
        "null"
      ],
      "enum": [
        "outlier",
        "strong",
        "breakout",
        null
      ],
      "title": "Outlier Tier",
      "description": "outlier (under 5 times the channel's usual), strong (5 to 10) or breakout (10 or more)"
    },
    "likes_per_1k": {
      "type": [
        "number",
        "null"
      ],
      "title": "Likes Per 1K",
      "description": "likes for every 1,000 views"
    },
    "comments_per_1k": {
      "type": [
        "number",
        "null"
      ],
      "title": "Comments Per 1K",
      "description": "comments for every 1,000 views"
    },
    "style_match": {
      "type": [
        "boolean",
        "null"
      ],
      "title": "Style Match",
      "description": "when the request asked for a kind of opening or title: whether this one is that kind. Null when it asked for none"
    },
    "angle": {
      "type": [
        "string",
        "null"
      ],
      "title": "Angle",
      "description": "the specific angle the video takes on the subject"
    },
    "format": {
      "type": [
        "string",
        "null"
      ],
      "title": "Format",
      "description": "long: the kind of video, one of talking_head, voiceover, vlog, tutorial, interview, documentary, challenge, review, story, other"
    },
    "opens_with": {
      "type": [
        "string",
        "null"
      ],
      "title": "Opens With",
      "description": "long: how the first seconds begin, one of cold_open, greeting, teaser, question, sponsor, other"
    },
    "thumbnail_checked": {
      "type": "boolean",
      "title": "Thumbnail Checked",
      "description": "packaging: true when thumbnail_url is the size YouTube really served and the analysis looked at"
    },
    "thumbnail_sha256": {
      "type": "string",
      "title": "Thumbnail Sha256",
      "description": "packaging: a digest of the picture the analysis looked at; a later run reuses the analysis only while YouTube serves the same picture"
    },
    "analysed_at": {
      "type": "string",
      "title": "Analysed At",
      "description": "packaging: when the thumbnail was looked at (ISO 8601); a reused analysis keeps its own date"
    },
    "title_chars": {
      "type": "integer",
      "title": "Title Chars",
      "description": "packaging: the title's length in characters"
    },
    "cut_on_phone": {
      "type": "boolean",
      "title": "Cut On Phone",
      "description": "packaging: true when the title is longer than 60 characters, where a phone roughly cuts a title. Approximate: YouTube publishes no limit, and the cut moves with the screen, the font and the letters (about 58 to 62)"
    },
    "thumbnail_text_role": {
      "type": [
        "string",
        "null"
      ],
      "title": "Thumbnail Text Role",
      "description": "packaging: how the words on the thumbnail stand to the title, one of repeats_title, adds_to_title, contrasts_title, no_text. Counted in code from `title_overlap` where that can be (repeats_title at 0.7 or more, adds_to_title under 0.3); between, the model's reading"
    },
    "title_overlap": {
      "type": [
        "number",
        "null"
      ],
      "title": "Title Overlap",
      "description": "packaging: the share of the thumbnail's words that are also in the title, 0 to 1, counted in code. null when the thumbnail has no words, or its words are in a script the title does not use"
    },
    "on_subject": {
      "type": [
        "string",
        "null"
      ],
      "enum": [
        "yes",
        "partly",
        "no",
        null
      ],
      "title": "On Subject",
      "description": "packaging: whether the look, told the run's subject, judged the package to be about it: yes, or partly (it covers the subject as one part of something broader). A package judged no is never delivered. null when the run was on a channel's or a video's address, when the look gave no usable verdict, or when the analysis is one an earlier run wrote (its subject may have been another)"
    },
    "thumbnail_faces": {
      "type": [
        "integer",
        "null"
      ],
      "title": "Thumbnail Faces",
      "description": "packaging: how many faces the thumbnail shows"
    },
    "thumbnail_colors": {
      "type": [
        "string",
        "null"
      ],
      "title": "Thumbnail Colors",
      "description": "packaging: the colours that carry the frame"
    },
    "why": {
      "type": [
        "string",
        "null"
      ],
      "title": "Why",
      "description": "one line naming the outlier in words"
    },
    "language": {
      "type": [
        "string",
        "null"
      ],
      "title": "Language",
      "description": "the language spoken, as YouTube names the caption track"
    },
    "analysed": {
      "type": "boolean",
      "title": "Analysed",
      "description": "false when the model could not analyse it: the find is still real, its analysis fields are null"
    },
    "hook": {
      "type": "string",
      "title": "Hook",
      "description": "long: the hook, verbatim: the lines of `intro` the analysis marks as the hook, cut at the last sentence end that keeps it inside 25 seconds and 60 words, a leading sponsor read or greeting left out. The whole opening is `intro_text`"
    },
    "hook_seconds": {
      "type": [
        "number",
        "null"
      ],
      "title": "Hook Seconds",
      "description": "long: the second the hook ends; the same number as `hook_end_s`, kept for older readers"
    },
    "intro": {
      "items": {
        "type": "object"
      },
      "type": "array",
      "title": "Intro",
      "description": "long: the opening as it was said, cut by the transcript's own clock: lines of n (from 1), t (start second, null when the transcript had no clock) and text"
    },
    "intro_text": {
      "type": "string",
      "title": "Intro Text",
      "description": "long: the same words as one string"
    },
    "intro_seconds": {
      "type": "integer",
      "title": "Intro Seconds",
      "description": "long: how much of the opening was read"
    },
    "hook_type": {
      "type": [
        "string",
        "null"
      ],
      "title": "Hook Type",
      "description": "long: since 2026-10-02: the device the opening uses, one of curiosity_gap, contrarian_claim, specific_number, identity_callout, stakes_threat, promise_payoff, pattern_interrupt, story_open, question, direct_command, other (then `hook_type_other` names it in two to five words). Null on older hooks and when the write-up gave none. A client should treat an unknown value as other."
    },
    "mechanism": {
      "type": [
        "string",
        "null"
      ],
      "title": "Mechanism",
      "description": "long: why the opening holds a viewer, quoting it word for word"
    },
    "promise": {
      "type": [
        "string",
        "null"
      ],
      "title": "Promise",
      "description": "what the viewer is told they will get"
    },
    "open_loop": {
      "type": [
        "string",
        "null"
      ],
      "title": "Open Loop",
      "description": "long: the question the opening leaves unanswered"
    },
    "title_link": {
      "type": [
        "string",
        "null"
      ],
      "title": "Title Link",
      "description": "long: how the first lines pay off the title"
    },
    "beats": {
      "items": {
        "type": "object"
      },
      "type": "array",
      "title": "Beats",
      "description": "long: the opening in consecutive parts: from and to (line numbers of `intro`), beat (one of hook, stakes, proof, credibility, context, promise, preview, story, payoff, cta, other) and does"
    },
    "template": {
      "type": [
        "string",
        "null"
      ],
      "title": "Template",
      "description": "long: the hook as a fill-in structure with {placeholders}"
    },
    "steal_it": {
      "type": [
        "string",
        "null"
      ],
      "title": "Steal It",
      "description": "how another niche reuses the move"
    },
    "audience": {
      "type": [
        "string",
        "null"
      ],
      "title": "Audience",
      "description": "the viewer it is made for"
    },
    "title_device": {
      "type": [
        "string",
        "null"
      ],
      "title": "Title Device",
      "description": "packaging: the title's device, one of curiosity_gap, contrarian_claim, specific_number, how_to, listicle, challenge, story, question, warning, comparison, authority, timeliness, identity_callout, other"
    },
    "title_template": {
      "type": [
        "string",
        "null"
      ],
      "title": "Title Template",
      "description": "packaging: the title as a fill-in structure with {placeholders}"
    },
    "thumbnail_text": {
      "type": [
        "string",
        "null"
      ],
      "title": "Thumbnail Text",
      "description": "packaging: the words printed on the thumbnail, as a vision model read them; null when none"
    },
    "thumbnail_subject": {
      "type": [
        "string",
        "null"
      ],
      "title": "Thumbnail Subject",
      "description": "packaging: what the picture shows"
    },
    "thumbnail_emotion": {
      "type": [
        "string",
        "null"
      ],
      "title": "Thumbnail Emotion",
      "description": "packaging: the feeling it signals"
    },
    "thumbnail_composition": {
      "type": [
        "string",
        "null"
      ],
      "title": "Thumbnail Composition",
      "description": "packaging: how the frame is built"
    },
    "thumbnail_devices": {
      "items": {
        "type": "string"
      },
      "type": "array",
      "title": "Thumbnail Devices",
      "description": "packaging: up to four of face_closeup, big_text, arrow_or_circle, before_after, number, contrast_color, object_hero, screenshot, split_screen, action_shot, collage, minimal, other"
    },
    "gap": {
      "type": [
        "string",
        "null"
      ],
      "title": "Gap",
      "description": "packaging: what the thumbnail shows that the title does not say, and the question that leaves"
    },
    "visible_action": {
      "type": [
        "string",
        "null"
      ],
      "title": "Visible Action",
      "description": "packaging: the single thing happening in the picture, one sentence with a verb (a model's reading)"
    },
    "title_carries": {
      "type": [
        "string",
        "null"
      ],
      "title": "Title Carries",
      "description": "packaging: what only the title tells the viewer"
    },
    "picture_adds": {
      "type": [
        "string",
        "null"
      ],
      "title": "Picture Adds",
      "description": "packaging: what only the picture adds that the title does not say"
    },
    "text_box": {
      "items": {
        "type": "integer"
      },
      "type": [
        "array",
        "null"
      ],
      "title": "Text Box",
      "description": "packaging: the box around the biggest line of printed words, [left, top, right, bottom] in thousandths of the width and height; null with no words"
    },
    "thumb_metrics": {
      "type": [
        "object",
        "null"
      ],
      "title": "Thumb Metrics",
      "description": "packaging: the thumbnail measured in code from its pixels: palette (three colours with their share %), brightness, contrast, saturation and busyness (0 to 100), text_px_210 (the biggest words' height when the thumbnail is 210 px wide, from text_box), text_reads_at_210 (6.2 px or more), badge_corner_clear (no words where YouTube prints the length) and basis. Null when the picture could not be read"
    }
  },
  "additionalProperties": true,
  "type": "object"
}