Hooks

The hook is what a short-form post says or shows in its first seconds. Virlo has the hooks of about 980,000 videos and 440,000 photo slideshows from TikTok, YouTube and Instagram, with how each post did.

At a glance
What it does
Shows which openings work on short-form video, with real examples and templates for writing your own.
You send
Optional filters. Search also needs a phrase or a hook type, and agent hooks needs your agent's ID.
You get back
Real hooks with their posts (views, creator, link), a scoreboard of hook types, or templates.
Cost
Free: hook type stats and library categories. $0.10: templates. $0.25: everything else (agent hooks are free with Data Intelligence, or before any are found). Each page of results is charged.

Which one do I need?

Key terms

  • Hook (hook_text): the opening, word for word. For a video, the first line spoken in about the first 6 seconds, else the on-screen text in the opening shot. For a slideshow, the first slide's text.
  • Virality Score (weighted_score): how far a post beat its creator's follower count, with extra credit for big accounts, so every creator size and platform shares one scale. Formula: ln(views ÷ followers) × ln(followers).
    • 35 and up: exceptional
    • 25 to 35: very strong
    • 18 to 25: strong
    • 10 to 18: promising
    • 0 to 10: more views than followers, but not a standout
    • Below 0: fewer views than followers (about half of all videos)
  • Outlier ratio (outlier_ratio): views ÷ followers.
  • Usage count (usage_count): how many posts across Virlo open with the same words. 1 means an original line.
  • Strong hit rate (strong_hit_rate): the share of a hook type's videos scoring 18 or more. 0.28 means 28%.

Hook types, explained

Every hook has a hook_type (what the opening line does). Videos usually also have a visual_hook_type (what the opening shot shows). Slideshows and some videos have no visual type (null or none) but still appear in results. Videos with no clear hook get hook_type none, shown only in the stats and never filterable.

The 16 hook types: question, bold_claim, shock_statement, story_tease, tutorial_promise, controversy (takes a side), before_after (a transformation reveal), pov_setup ("POV:"), statistic, direct_address ("If you have oily skin…"), trend_reference, cliffhanger (holds back the payoff), negation ("Stop doing this"), relatable_scenario, comparison and mystery_setup (a puzzle to solve).

The 11 visual hook types: text_hook (big on-screen text), extreme_closeup, before_state, shocking_image, aesthetic_setup, person_speaking_to_camera, motion_action, crowded_scene, mystery_object, dramatic_zoom and animal_pet.

Content categories

The category filter takes only these 28 values. The template library has its own categories.

art_design, automotive, beauty, business_career, crafts_diy, education, entertainment, fashion, finance, fitness, food_beverage, gaming, health_wellness, home_garden, kids_content, lifestyle, music, news_politics, parenting_family, pets, real_estate, relationships_dating, religion_spirituality, science_nature, sports, tech, travel, other


GET/v1/hooks/types

Hook types and stats

Which kinds of hooks win most often, overall or on one platform or category. It lists every type plus none, minus types with no videos in your platform or category.

Cost per request:Free
  • Sort by strong_hit_rate to see what works. Common is not the same as effective: across all videos, the two most-used types, tutorial_promise and bold_claim, have the two lowest hit rates.
  • Check video_count. A rare type can top the list on a tiny sample: cliffhanger leads overall with only 268 videos.
  • Videos only, not slideshows, refreshed daily. share_of_corpus is each type's share of the slice you asked for, so the rows add up to 1.

Query parameters

All optional. page and limit are rejected (400).

  • Name
    dimension
    Type
    string
    Description

    hook_type (default) or visual_hook_type.

  • Name
    platform
    Type
    string
    Description

    tiktok, youtube or instagram.

  • Name
    category
    Type
    string
    Description

    One of the 28 content categories.

  • Name
    sort
    Type
    string
    Description

    Highest first: video_count (default), strong_hit_rate, median_weighted_score or p90_weighted_score.

Request

GET
/v1/hooks/types
curl "https://api.virlo.ai/v1/hooks/types?sort=strong_hit_rate" \
  -H "Authorization: Bearer YOUR_API_KEY"

Response

{
  "data": {
    "dimension": "hook_type",
    "platform": "all",
    "category": "all",
    "snapshot_date": "2026-09-24",
    "types": [
      {
        "value": "cliffhanger",
        "video_count": 268,
        "share_of_corpus": 0.0003,
        "total_views": 189174617,
        "avg_views": 705875,
        "median_views": 8817,
        "avg_weighted_score": 5.77,
        "median_weighted_score": 8.65,
        "p90_weighted_score": 35.95,
        "strong_hit_rate": 0.2786
      },
      {
        "value": "pov_setup",
        "video_count": 43974,
        "share_of_corpus": 0.0449,
        "total_views": 32986509016,
        "avg_views": 750137,
        "median_views": 19811,
        "avg_weighted_score": 2.95,
        "median_weighted_score": 5.05,
        "p90_weighted_score": 35.93,
        "strong_hit_rate": 0.2729
      }
    ]
  }
}

GET/v1/hooks/trending

The top hooks from posts published in the last 7, 14 or 30 days, ranked by Virality Score.

Cost per request:$0.25

Query parameters

All optional.

  • Name
    days
    Type
    integer
    Description

    7 (default), 14 or 30.

  • Name
    category
    Type
    string
    Description

    One of the 28 content categories.

  • Name
    platform
    Type
    string
    Description

    tiktok, youtube or instagram.

  • Name
    content_type
    Type
    string
    Description

    video (default), slideshow or all. Slideshows currently time out here with a 503. Use search with content_type=slideshow and a hook_type instead (all time, not just recent).

  • Name
    hook_type, visual_hook_type
    Type
    string
    Description

    One of the hook types. visual_hook_type leaves out all slideshows.

Details for developers

Search and agent hooks use this shape too. Each hook appears once, from its strongest post.

  • brand_safety_tier is safe, low_risk or medium_risk (riskier and adult posts are left out). It can be null on some slideshows.
  • usage_count matches words, ignoring capitals and spacing but not punctuation.
  • video.shares is null on almost all YouTube posts, and video.duration on slideshows.
  • Only YouTube author_handle values start with @.

Request

GET
/v1/hooks/trending
curl "https://api.virlo.ai/v1/hooks/trending?category=fitness&days=7&limit=10" \
  -H "Authorization: Bearer YOUR_API_KEY"

Response

{
  "data": [
    {
      "hook_text": "10 signals your body is losing FAT",
      "hook_type": "tutorial_promise",
      "visual_hook_type": "text_hook",
      "content_type": "video",
      "platform": "tiktok",
      "category": "fitness",
      "language": "en",
      "brand_safety_tier": "safe",
      "outlier_ratio": 64.43,
      "weighted_score": 35.88,
      "usage_count": 1,
      "video": {
        "video_id": "7570d1bc-f0c6-4aa1-8f13-98fa919bcc2f",
        "url": "https://www.tiktok.com/@scully_fitness/video/7686773818983042325",
        "thumbnail_url": "https://auth.virlo.ai/storage/v1/object/public/thumbnails/8ed30a72cd9c1f9ab952eca2a4cf6462e9583076bd2f69f43e572f790dd0ae77.jpg",
        "views": 354321,
        "likes": 10851,
        "comments": 34,
        "shares": 271,
        "duration": 6,
        "publish_date": "2026-09-18T07:28:22+00:00",
        "author_handle": "scully_fitness",
        "author_followers": 5499
      }
    }
  ],
  "pagination": { "page": 1, "limit": 10, "total": 11, "total_pages": 2, "has_next_page": true, "has_prev_page": false }
}

GET/v1/hooks/search

Search hooks

Finds hooks by phrase or by type, across all time: the top hooks built on a phrase like "nobody talks about", or the post a pasted hook came from (a reverse lookup). Needs a phrase (q) or a type (hook_type, visual_hook_type). Other filters alone are rejected (400).

Cost per request:$0.25

Searching by phrase

Results come in three groups, in this order, named in match_type:

  1. exact: the hook is your text, ignoring capitals and spacing but not punctuation. Always first, even if the post did poorly.
  2. contains: your words, whole and side by side, ignoring punctuation, emoji and line breaks between them. Highest Virality Score first.
  3. similar: near-matches, closest first. Only for a q of 12+ characters, when the page isn't full.

A paste whose punctuation between words differs lands in contains. Punctuation inside a word counts: dont won't find "don't". match_score (0 to 1) rates the whole hook against your text, so long hooks score low. Only similar is sorted by it.

Browsing by type

Without q, you get the strongest hooks of a type, all time, in mixed languages unless you set language. match_type and match_score are null.

Query parameters

days and sort are rejected (400).

  • Name
    q
    Type
    string
    Description

    3 to 200 characters, including a word of 3 or more letters or digits.

  • Name
    content_type
    Type
    string
    Description

    video (default), slideshow or all.

  • Name
    hook_type, visual_hook_type, platform, category, language, min_views, page, limit
    Type
    string | integer
    Description

    As on trending.

Request

GET
/v1/hooks/search
curl "https://api.virlo.ai/v1/hooks/search?q=nobody%20talks%20about&language=en&limit=10" \
  -H "Authorization: Bearer YOUR_API_KEY"

Phrase search

{
  "data": [
    {
      "hook_text": "NOBODY TALKS ABOUT",
      "match_type": "exact",
      "match_score": 1,
      "weighted_score": -0.43,
      "usage_count": 1,
      "video": { "views": 2863, "author_handle": "hello.inertia" }
    },
    {
      "hook_text": "Nobody talks about\nhow hard\nit is\nto go from this...",
      "match_type": "contains",
      "match_score": 0.422222,
      "weighted_score": 61.21,
      "usage_count": 4,
      "video": { "views": 7014838, "author_handle": "naysnetwork" }
    },
    {
      "hook_text": "Nobody talks about hard it is\nto go from this...",
      "match_type": "contains",
      "match_score": 0.452381,
      "weighted_score": 53.64,
      "usage_count": 1,
      "video": { "views": 3838283, "author_handle": "mmadsgains" }
    }
  ],
  "pagination": { "page": 1, "limit": 10, "total": 11, "total_pages": 2, "has_next_page": true, "has_prev_page": false }
}

GET/v1/hooks/library

Hook template library

More than 4,100 hand-picked hook templates in 33 categories, each with an example and a note on why it works. Most have [blanks] to fill in, and some are complete lines. They come in a fixed order, not ranked by performance.

Cost per request:$0.10

You pay even when nothing matches. tags is currently always empty.

Query parameters

All optional.

  • Name
    category
    Type
    string
    Description

    A slug from library categories, like educational-hooks. A misspelled slug returns an empty list.

  • Name
    psychology_tag
    Type
    string
    Description

    One tag, matched exactly, spaces and capitals included: social proof works, social_proof finds nothing. There are about 3,500 tags and no list. If a tag finds nothing, try q: it skips tags but searches the psychology note.

  • Name
    q
    Type
    string
    Description

    Text in the template, example or psychology note, any case. 2 to 200 characters.

  • Name
    page, limit
    Type
    integer
    Description

    Defaults 1 and 20, limit up to 100. total is exact.

Request

GET
/v1/hooks/library
curl "https://api.virlo.ai/v1/hooks/library?psychology_tag=curiosity&limit=5" \
  -H "Authorization: Bearer YOUR_API_KEY"

Response

{
  "data": [
    {
      "hook_text": "Here's what happened when I [took big risk].",
      "example": "Here's what happened when I quit my job without a backup plan.",
      "psychology": "Fear + curiosity about bold decisions.",
      "psychology_tags": ["fear", "curiosity"],
      "tags": [],
      "library_category": { "slug": "case-studies-proof-hooks", "name": "Case Studies & Proof Hooks" }
    }
  ],
  "pagination": { "page": 1, "limit": 5, "total": 115, "total_pages": 23, "has_next_page": true, "has_prev_page": false }
}

GET/v1/hooks/library/categories

Library categories

The 33 library categories, each with its template count. Pass a slug as the library's category. description is always null.

Cost per request:Free

Request

GET
/v1/hooks/library/categories
curl "https://api.virlo.ai/v1/hooks/library/categories" \
  -H "Authorization: Bearer YOUR_API_KEY"

Response

{
  "data": [
    { "slug": "educational-hooks", "name": "Educational Hooks", "description": null, "hook_count": 200 }
  ]
}

GET/v1/agents/:id/hooks

Agent hooks

The strongest hooks in the videos your Content Research Agent has collected, ranked by Virality Score.

$0.25 per request. Free while coverage.videos_with_hooks is 0, and always free when the agent has Data Intelligence on (data_intelligence_enabled: true).

  • Videos only. Each video counts once.
  • usage_count counts matching hooks across all of Virlo, not just this agent.
  • pagination.total is exact, with no 1,000-result cap.
  • Unknown or someone else's agent: 404. A malformed ID currently gives a 500 instead.

Coverage

coverage.videos_collected counts the agent's distinct videos, and videos_with_hooks those with an analyzed hook. Agents have hooks with or without Data Intelligence, but only analyzed videos have one. Off-topic, year-old and underperforming videos are not analyzed, so the second number is normally well below the first. videos_with_hooks: 0 usually means a new agent or nothing analyzed yet: try trending meanwhile.

Parameters

category, visual_hook_type, language and content_type are rejected (400) here.

  • Name
    id
    Type
    string
    Required
    *
    Description

    Your agent's full 36-character ID (a UUID), from GET /v1/agents.

  • Name
    platform, hook_type, min_views, sort
    Type
    string | integer
    Description

    As on trending.

  • Name
    page, limit
    Type
    integer
    Description

    Defaults 1 and 20, limit up to 100.

Request

GET
/v1/agents/:id/hooks
curl "https://api.virlo.ai/v1/agents/{agent_id}/hooks?limit=30" \
  -H "Authorization: Bearer YOUR_API_KEY"

Response

{
  "data": {
    "agent_id": "c1e483c3-...",
    "coverage": { "videos_collected": 311, "videos_with_hooks": 65 },
    "hooks": [
      {
        "hook_text": "Have you ever wondered what all these espresso tools actually do?",
        "hook_type": "question",
        "visual_hook_type": "text_hook",
        "platform": "youtube",
        "weighted_score": 41.55,
        "usage_count": 1,
        "video": { "views": 24275222, "author_handle": "@Ethanrodecoffee", "author_followers": 1260000 }
      }
    ]
  },
  "pagination": { "page": 1, "limit": 30, "total": 65, "total_pages": 3, "has_next_page": true, "has_prev_page": false }
}

Paging, costs and errors

  • Top 1,000. Trending and search stop at the top 1,000 results (page 50 at limit=20).
  • Trending and search total isn't a full count. It counts results through this page, plus one if more exist (page 1 at limit=10 shows 11). Keep paging while has_next_page is true.
  • Charges. Empty results are charged, except agent hooks with videos_with_hooks: 0. Errors never are. X-Cost shows the charge in dollars and X-Credits-Used in credits (1 credit = $0.01).

Errors come back with message, error, statusCode and code (more on errors):

  • 400: a bad value, an unknown parameter (library categories ignores them) or a page past the top 1,000. The message lists valid values.
  • 402: your balance is too low.
  • 503: the query ran past about 8 seconds. Retrying the same call usually fails again. For trending slideshows, use search instead.

Was this page helpful?