Data Intelligence

Data Intelligence is an add-on for Content Research Agents. AI labels most of the videos an agent collects with 79 fields, like hook, format, tone and brand safety. You can then compare videos by what is in them, not just by views.

At a glance
What it does
Labels each video an agent collects: hook, format, tone, brand safety, who is on screen and more.
You send
data_intelligence_enabled: true when you create an agent. A recurring agent can also turn it on later.
You get back
An intelligence block on each video and slideshow. Videos also get intent_match: does this video fit your brief?
Cost
$1.00 extra per run, so a run costs $1.50 instead of $0.50. Reading the results is free.
How long
A run usually takes under 20 minutes. Fields keep arriving after it finishes, and some videos never get them.

How it works

  1. You create a Content Research Agent with data_intelligence_enabled: true.
  2. The agent collects videos and slideshows for your keywords. Each round of collecting is a run.
  3. AI reviews each one. It looks at still frames when it can, and reads the caption and what is said (for a slideshow, its images and their text). It writes what it finds into an intelligence block.
  4. You read the results for free with Get videos and Get slideshows.

Each video and slideshow also has an intelligence_status that says whether its fields are in yet. See When fields are missing.


What you get

The 79 fields fall into 14 groups:

GroupWhat it tells youFields
ContentTopic, category and kind of content5
VisualFormat, camera, setting, lighting11
Hook and on-screen textThe opening line and the text on screen6
Captions and transcriptBurned-in captions, spoken words, language7
ToneMood, sentiment, speaking style3
Brand safetySafety tier, sensitive topics, sponsorship, brands named5
Engagement tacticsCalls to action, social proof, trend references3
SummaryShort summary, educational or not, what the AI was unsure about3
People on screenWho appears, apparent age and gender, faceless or on camera17
AI useWhether the video itself was made with AI4
Brands and products on screenLogos, products and characters in frame3
AnimalsWhich animals appear and whether they are the subject3
Screen and audioScreen recordings, and whether anyone speaks2
Edit structureCompilations, rankings, reposts, before-and-after, watermarks7

You can't ask the API for only faceless videos, say. You sort the results yourself, in code or a spreadsheet (Use cases). Only intent_match has a built-in filter.

Filters to apply yourself

You wantCheck that
Faceless videospresence_style is faceless_voiceover, faceless_silent or hands_or_pov
A person talking to the camerapresence_style is on_camera_presenter and is_silent is false
No AI-made contentai_provenance is none
Original posts onlyis_repost and is_compilation are false
Sponsored postsis_sponsored is true
An animal is the staranimal_presence is animal_featured. For pets, also check animals_visible has dog, cat or other_pet
No talkingis_silent is true

on_camera_presenter means someone faces the camera and addresses it at some point, speaking or not. For stricter talking-head videos, where that is the main shot throughout, use visual_format talking_head.


Pricing

Data Intelligence adds $1.00 to each run, however many videos it collects.

Agent typeWithoutWith Data IntelligenceWhen you pay
Runs once$0.50$1.50When you create it, even if the run later fails
Recurring (runs on a schedule)$0.50 per run$1.50 per runAfter each run finishes, starting with the first
  • Recurring agents are free to create, but creating one with Data Intelligence needs $1.50 in your balance. A run where some keywords or platforms failed (partial_failure) is still charged. A recurring run that fails outright is not charged.
  • Recurring charges are automatic. Check them in your balance and usage history (Billing and pricing). No API response reports them (no X-Cost header).
  • Reading is free. With Data Intelligence on, the agent's hook list is free too. Without it, each hook-list request costs $0.25, or nothing while the agent has no hooks yet.

Turn it on

Add data_intelligence_enabled: true when you create an agent. The first run starts right away. In every example on this page, replace YOUR_API_KEY with your API key.

Create an agent with Data Intelligence

POST
/v1/agents
curl -X POST https://api.virlo.ai/v1/agents \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Skincare Routine Research",
    "is_recurring": false,
    "intent": "Find beginner glass-skin routines that name drugstore products, not dermatologist lectures",
    "keywords": ["glass skin routine", "skincare routine for beginners", "drugstore skincare routine"],
    "platforms": ["tiktok", "youtube"],
    "data_intelligence_enabled": true
  }'

A one-time agent can't run again, so turn it on at creation. A recurring agent can turn it on or off at any time with Update agent. The change applies from the next run, sets the price of future runs, and does not go back over earlier videos.

Turn it on for a recurring agent

PUT
/v1/agents/:id
curl -X PUT https://api.virlo.ai/v1/agents/{agent_id} \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "data_intelligence_enabled": true }'

When fields are missing

intelligence_status on each video and slideshow tells you why intelligence might be null:

intelligence_statusMeaningWhat to do
readyThe fields are in.Use them.
pendingData Intelligence is on, but the fields aren't in yet.Check again later. If it is still pending the next day, it will probably never get them. It never turns into "failed".
disabledData Intelligence is off for this agent.Turn it on for future runs, or ignore the field.

What to plan for:

  1. Not every video gets fields. On one agent, about a third of its videos were still pending three days after the run. Almost all had intent_match false: videos that don't fit your intent usually skip the full review.
  2. finalized: true does not wait for the fields. The agent can report its run as finished (Get agent) while videos are still pending. Read again later to pick them up.
  3. YouTube videos often lack the picture fields. When the AI can't get a video's frames, it works from the caption and speech only. Every field that needs the picture is then null: format, setting, camera, lighting, on-screen text, faceless or on camera, products, animals and watermarks. It can hit more than half of an agent's YouTube videos. You can spot them because low_confidence_fields includes tier3_unavailable.
  4. Turned it on later? Videos from earlier runs show pending and usually stay that way.
Details for developers
  • Single-value fields can be null in a ready block when the AI could not tell, including many true/false fields. For example, hook_type is null when the video has no opening line at all. A weak opening line gets none.
  • List fields are [], never null. is_nsfw, is_sponsored, is_educational and is_multilingual are never null either: they read false when nothing was found, so false can also mean "couldn't tell".
  • low_confidence_fields can hold names that aren't fields, like inferred_region. Ignore names you don't recognize.
  • Videos flagged by Virlo's minor-safety check are dropped from every list, and no field marks them. That is one reason a page can hold fewer videos than you asked for.
  • Fields without Data Intelligence. A video Virlo already reviewed for another agent can come back ready at no charge. Don't count on it.

Intent matching

Every agent has an intent: one plain sentence saying what you want to find. With Data Intelligence on, each video also gets intent_match, a yes or no answer to "does this video fit my intent?"

  • AI decides from the caption, hashtags and spoken words when the video is collected. It never looks at the picture. So write your intent about the topic ("honest reviews of drugstore moisturizers"), not the look ("talking head"), and check the look with the fields. The Intent cookbook has more on writing intents.
  • intent_match can arrive before intelligence. Videos that get false usually never get the other fields.
  • Videos only. Slideshows don't get it.
  • It sits next to intelligence on the video, not inside it. It is null when Data Intelligence was off when the video was collected, or the check failed.
{
  "id": "b3a1f892-7c4e-4d8a-9f12-6e8b4a2c1d05",
  "url": "https://www.tiktok.com/@creator/video/7392847561023456789",
  "views": 2847000,
  "intelligence": { ... },
  "intent_match": {
    "matches": true,
    "reasoning": "The caption 'my honest review after 30 days' and the transcript describe the creator testing a moisturizer and giving their own opinion, which fits the intent."
  }
}
  • Name
    matches
    Type
    boolean
    Description

    true if the video fits your intent.

  • Name
    reasoning
    Type
    string
    Description

    Why, usually one sentence quoting the caption, hashtags or transcript. Sometimes it is a machine string instead, like jev subject=0.93 stuffing=0.05, so check before showing it to clients. Don't parse it.

Filter videos by intent match

Add intent_match=true (or false) to Get videos:

GET /v1/agents/:id/videos?intent_match=true

Ignore the intent_summary ({ matched, total_evaluated }) that some run-finished webhooks carry. It counts every video checked for the agent so far, not just that run.


Use cases

The code below is for your developer. Each snippet takes videos, the list from Get videos (response['data']['videos']), and skips videos with no fields yet.

Brand safety filtering

Drop risky videos before you repost creator content or place ads next to it.

def is_brand_safe(v):
    intel = v.get('intelligence')
    if not intel:
        return False  # not reviewed yet, so not confirmed safe
    topics = set(intel.get('sensitive_topics') or [])
    return (
        intel.get('brand_safety_tier') in ('safe', 'low_risk')
        and not intel.get('is_nsfw')
        and topics <= {'none'}  # nothing sensitive: the list is empty or only 'none'
    )

safe_videos = [v for v in videos if is_brand_safe(v)]

Which hooks show up most

Count the most common kinds of opening line in your niche. Common is not the same as successful: to see which hooks get the most views, group by hook_type the way the next snippet groups by format. Hooks explains each hook type.

from collections import Counter

hook_counts = Counter(
    v['intelligence']['hook_type']
    for v in videos
    if v.get('intelligence') and v['intelligence'].get('hook_type')
)
print(hook_counts.most_common(5))

Which formats get the most views

Compare one platform at a time, and use the median, not the average, so one viral hit doesn't skew a format.

from statistics import median

# (platform, format) -> view counts
views = {}
for v in videos:
    fmt = (v.get('intelligence') or {}).get('visual_format')
    if fmt:
        views.setdefault((v['platform'], fmt), []).append(v['views'] or 0)

median_views = {
    key: median(counts)
    for key, counts in views.items()
    if len(counts) >= 5  # skip formats with too few videos to judge
}
# each platform, best format first
for (platform, fmt), mid in sorted(median_views.items(), key=lambda kv: (kv[0][0], -kv[1])):
    print(platform, fmt, int(mid))

Competitor brand mentions

brands_mentioned comes from what is said and written. brands_visible comes from logos and products on screen. This counts each brand once per video.

from collections import Counter

brand_counts = Counter()
for v in videos:
    intel = v.get('intelligence') or {}
    brands = set(intel.get('brands_mentioned') or []) | set(intel.get('brands_visible') or [])
    brand_counts.update(brands)

print(brand_counts.most_common(10))

Find one exact format: text-story videos

Some formats are easier to find by look than by keyword. Text-story videos show a person, nobody talks, and more than 50 words of on-screen text tell the story. Use the agent's intent for the topic and these fields for the look.

def is_text_story(v):
    intel = v.get('intelligence') or {}
    overlay = intel.get('text_overlay_content') or ''
    return (
        intel.get('has_face_visible') is True
        and intel.get('is_silent') is True
        and len(overlay.split()) > 50
    )

text_stories = [v for v in videos if is_text_story(v)]

End-to-end examples

These use Python and the requests library. Every response wraps its results in a data object, which is why the code reads ['data'].

Example 1: Find talking-head product reviews

A brand team wants reviews where a creator talks to the camera, for a user-generated content (UGC) campaign. intent_match checks the topic. The fields check the look.

Step 1: Create the agent. This costs $1.50.

import time
import requests

API = 'https://api.virlo.ai/v1'
HEADERS = {'Authorization': 'Bearer YOUR_API_KEY'}

agent = requests.post(f'{API}/agents', headers=HEADERS, json={
    'name': 'UGC Talking Head Reviews',
    'is_recurring': False,
    'intent': (
        'Honest product reviews where a creator tries a product and gives '
        'their own opinion, not paid ads or sponsored posts'
    ),
    'keywords': ['honest product review', 'trying this product', 'is it worth it review'],
    'platforms': ['tiktok', 'instagram'],
    'data_intelligence_enabled': True,
}).json()

agent_id = agent['data']['id']

Step 2: Wait for the run to finish. Check the agent again every 30 seconds (this is called polling) until finalized is true. Give up after about 45 minutes, or if the latest run's status is failed.

deadline = time.time() + 45 * 60  # give up after 45 minutes
while True:
    info = requests.get(f'{API}/agents/{agent_id}', headers=HEADERS).json()['data']
    if (info.get('latest_run') or {}).get('status') == 'failed':
        raise RuntimeError('The run failed. Check the agent before trying again.')
    if info.get('finalized') is True:
        break
    if time.time() > deadline:
        raise TimeoutError('The run is still going after 45 minutes.')
    time.sleep(30)

Step 3: Read every video. Ask for 100 per page until a page comes back empty. A page can hold fewer than 100 even when more remain.

def all_videos(agent_id):
    """Yield every video the agent collected, 100 per page."""
    seen = set()
    page = 1
    while True:
        batch = requests.get(
            f'{API}/agents/{agent_id}/videos',
            headers=HEADERS,
            params={'limit': 100, 'page': page},
        ).json()['data']['videos']
        if not batch:
            return
        for video in batch:
            if video['id'] not in seen:  # pages can overlap, so skip videos we have already seen
                seen.add(video['id'])
                yield video
        page += 1

videos = list(all_videos(agent_id))

Some videos can still show intelligence_status: pending right after the run. Run Steps 3 and 4 again later to pick them up.

Step 4: Keep the videos that fit.

def is_talking_head_review(v):
    intel = v.get('intelligence') or {}
    fits_intent = (v.get('intent_match') or {}).get('matches') is True
    return (
        fits_intent
        and intel.get('presence_style') == 'on_camera_presenter'
        and intel.get('is_silent') is False
        and intel.get('is_sponsored') is False
    )

picks = [v for v in videos if is_talking_head_review(v)]
print(f'{len(picks)} of {len(videos)} videos fit')
for v in picks[:10]:
    print(v['url'], '|', v['intent_match']['reasoning'])

Example 2: Daily brand-safety audit

A media buyer checks a fitness niche every day for videos that would be unsafe next to supplement ads. cadence: 'daily' with Data Intelligence costs $1.50 a day.

agent = requests.post(f'{API}/agents', headers=HEADERS, json={
    'name': 'Daily Brand Safety Audit - Fitness',
    'is_recurring': True,
    'cadence': 'daily',
    'intent': (
        'Everyday creators sharing their own fitness transformation or '
        'weight loss journey, not gym ads or supplement promos'
    ),
    'keywords': ['fitness transformation', 'weight loss journey', 'body transformation progress'],
    'platforms': ['tiktok', 'youtube'],
    'data_intelligence_enabled': True,
}).json()

agent_id = agent['data']['id']

After each run (when last_run_at changes or a webhook arrives, see Example 3), split the videos into safe and flagged. This checks every video collected so far. To report only new ones, skip IDs you already checked. It reuses is_brand_safe (Brand safety filtering) and all_videos (Example 1).

safe, flagged = [], []
for v in all_videos(agent_id):
    if not v.get('intelligence'):
        continue  # no fields yet
    if is_brand_safe(v):
        safe.append(v)
    else:
        flagged.append(v)

print(f'Brand-safe: {len(safe)}  Flagged: {len(flagged)}')
for v in flagged[:10]:
    intel = v['intelligence']
    print(v['url'], intel['brand_safety_tier'], intel['sensitive_topics'])

Example 3: Start work from a webhook

A webhook lets Virlo tell your server when a run finishes, so you don't have to poll. React to content_research_agent.run.completed:

from flask import Flask, request

app = Flask(__name__)

@app.post('/webhooks/virlo')
def handle_webhook():
    payload = request.json
    if payload['event'] == 'content_research_agent.run.completed':
        # Reply fast. Do the slow work (reading videos) in a background job.
        schedule_intent_review(payload['data'])
    return '', 200

schedule_intent_review is your own code that reads and filters videos as in Example 1. The agent's ID is not at the top of data, and where it sits depends on how the run ended. Agent run finished shows the one-line lookup. The webhook can arrive before every video's fields are in, so read again later if many are still pending.


Field reference

All 79 fields sit in the intelligence object on a video. enum means one value from a fixed list (see Allowed values), object is a small record, and [] means a list. See When fields are missing for when a field is null.

All 79 video fields

Content

  • Name
    primary_topic
    Type
    string
    Description
    Main topic.
  • Name
    secondary_topics
    Type
    string[]
    Description
    Other topics it covers.
  • Name
    keywords
    Type
    string[]
    Description
    Keywords that describe the content.
  • Name
    category
    Type
    string
    Description
    Broad subject area, like beauty. Usually one of the 28 listed values.
  • Name
    content_format
    Type
    string
    Description
    Kind of content, like tutorial or review. Usually one of the 52 listed values.

Visual

  • Name
    visual_format
    Type
    enum
    Description
    Main visual style, like talking_head or screen_recording.
  • Name
    visual_complexity
    Type
    enum
    Description
    How busy the picture is.
  • Name
    camera_perspective
    Type
    enum
    Description
    Camera angle and position.
  • Name
    setting
    Type
    enum
    Description
    Where it was filmed.
  • Name
    lighting_quality
    Type
    enum
    Description
    Lighting quality.

Hook and on-screen text

  • Name
    hook_text
    Type
    string
    Description
    The opening line, spoken or on screen.
  • Name
    hook_type
    Type
    enum
    Description
    Kind of hook, like question or bold_claim.
  • Name
    visual_hook_type
    Type
    enum
    Description
    What the opening shot shows.
  • Name
    has_text_overlay
    Type
    boolean
    Description
    Text is shown on screen.
  • Name
    text_overlay_purpose
    Type
    enum
    Description
    What the main on-screen text is for.
  • Name
    text_overlay_content
    Type
    string
    Description
    The main on-screen text, word for word.

Captions and transcript

  • Name
    has_onscreen_captions
    Type
    boolean
    Description
    Burned-in captions are shown.
  • Name
    caption_style
    Type
    enum
    Description
    Style of those captions.
  • Name
    transcript_quality
    Type
    enum
    Description
    How clean the transcript of the spoken words is.
  • Name
    transcript_word_count, transcript_character_count
    Type
    integer
    Description
    Size of the transcript. 0 when nobody speaks or no transcript was made.
  • Name
    language_detected
    Type
    string
    Description
    Two-letter language code, like en.
  • Name
    is_multilingual
    Type
    boolean
    Description
    More than one language is spoken.

Tone

  • Name
    emotional_tone
    Type
    enum
    Description
    Main mood.
  • Name
    sentiment
    Type
    enum
    Description
    Positive, negative, neutral or mixed.
  • Name
    speaking_style
    Type
    enum
    Description
    How the speaker talks.

Brand safety

  • Name
    brand_safety_tier
    Type
    enum
    Description
    How safe it is for brands, from safe to unsafe.
  • Name
    is_nsfw
    Type
    boolean
    Description
    Not safe for work.
  • Name
    sensitive_topics
    Type
    enum[]
    Description
    Sensitive topics found. ["none"] when there are none.
  • Name
    is_sponsored
    Type
    boolean
    Description
    Sponsored content.
  • Name
    brands_mentioned
    Type
    string[]
    Description
    Brands named in what is said or written.

Engagement tactics

  • Name
    cta_usages
    Type
    object[]
    Description
    Calls to action, each { type, text }. text is the exact wording.
  • Name
    social_proof_used
    Type
    enum[]
    Description
    Social proof used, like testimonials or statistics.
  • Name
    trend_references
    Type
    string[]
    Description
    Trends or challenges it refers to.

Summary

  • Name
    summary
    Type
    string
    Description
    Short summary, usually 2 or 3 sentences.
  • Name
    is_educational
    Type
    boolean
    Description
    The video mainly teaches something.
  • Name
    low_confidence_fields
    Type
    string[]
    Description
    Fields the AI was unsure about. tier3_unavailable here means the frames were not analyzed.

People on screen

Read from the picture. These describe how people appear, not who they are, and are empty or null when nobody is detected.

  • Name
    people
    Type
    object[]
    Description
    One record per person: { role, is_synthetic, apparent_gender, apparent_age_bracket, apparent_age_min, apparent_age_max, confidence }.
  • Name
    presence_style
    Type
    enum
    Description
    Faceless or on camera. Combines on_screen_presence with whether anyone speaks. Use this one for faceless filters.
  • Name
    on_screen_presence
    Type
    enum
    Description
    What the camera shows of a person, from the picture only.
  • Name
    people_source
    Type
    enum
    Description
    How people was worked out: from frames, from the words, or not at all.
  • Name
    people_count_bucket
    Type
    enum
    Description
    Roughly how many people appear.

AI use

Whether the video was made with AI. A tutorial about ChatGPT is not AI-made.

  • Name
    ai_provenance
    Type
    enum
    Description
    How much of the video is AI-made, from none to fully_ai.
  • Name
    ai_elements
    Type
    enum[]
    Description
    Each AI-made part found. Videos can mix them, like a real creator with an AI voiceover.
  • Name
    ai_confidence
    Type
    enum
    Description
    How sure the AI-use answer is.
  • Name
    ai_reasoning
    Type
    string
    Description
    Why it chose that ai_provenance.

Brands and products on screen

From the picture, so they catch a logo nobody mentions.

  • Name
    brands_visible
    Type
    string[]
    Description
    Brand names and logos seen in frame.
  • Name
    product_presence
    Type
    enum
    Description
    Whether a product is the subject, and whether it is branded.
  • Name
    ip_characters
    Type
    string[]
    Description
    Known characters, like franchise characters or mascots.

Animals

  • Name
    animals_visible
    Type
    enum[]
    Description
    Kinds of animal on screen.
  • Name
    animal_presence
    Type
    enum
    Description
    Whether an animal is the subject or just in the background.
  • Name
    has_animal
    Type
    boolean
    Description
    Any animal appears.

Screen and audio

  • Name
    screen_content_type
    Type
    enum
    Description
    What a recorded screen shows. null unless a screen fills much of the picture.
  • Name
    is_silent
    Type
    boolean
    Description
    Nobody speaks (text or music only).

Edit structure

  • Name
    is_compilation
    Type
    boolean
    Description
    Unrelated clips stitched together.
  • Name
    is_ranking
    Type
    boolean
    Description
    A ranking or tier list, like "top 5".
  • Name
    is_repost
    Type
    boolean
    Description
    Reuploaded content rather than original.
  • Name
    is_before_after
    Type
    boolean
    Description
    A before-and-after transformation.
  • Name
    is_layered_composition
    Type
    boolean
    Description
    Visual layers stacked, like a reaction over a clip.
  • Name
    has_platform_watermark
    Type
    boolean
    Description
    A platform or editing-app watermark is visible.
  • Name
    watermark_source
    Type
    string
    Description
    Where the watermark is from, like tiktok or capcut.

presence_style, subject_gender_skew, ai_provenance and the has_* people and animal fields are calculated from the other fields, so they always agree with them. Filter on these instead of digging through people.


Slideshow intelligence

Slideshows are photo carousels, mostly from TikTok. Each gets its own intelligence block with 70 fields and the same intelligence_status values as videos. They drop 16 video fields about motion, camera work and speech, and add 7 about the slides, which Virlo calls panels. The other 63 fields work as on videos.

  • Name
    narrative_arc
    Type
    enum
    Description
    How the panels tell the story, like listicle, tutorial_steps or before_after.
  • Name
    text_density
    Type
    enum
    Description
    Whether text or images carry the post: text_dominant, balanced or image_dominant.
  • Name
    image_count
    Type
    integer
    Description
    Panels analyzed, up to 10. The slideshow's images list can hold more.
  • Name
    panel_texts
    Type
    string[]
    Description
    The text on each panel, in order.
  • Name
    panel_text_full
    Type
    string
    Description
    All panel text in one string, labeled Panel 1: ..., Panel 2: .... Can be null.
  • Name
    panel_text_word_count, panel_text_character_count
    Type
    integer
    Description
    Words and characters in panel_text_full.

Video fields slideshows don't have: visual_format, camera_perspective, lighting_quality, visual_complexity, scene_changed, visual_hook_type, has_text_overlay, text_overlay_content, text_overlay_purpose, has_onscreen_captions, caption_style, transcript_quality, transcript_word_count, transcript_character_count, speaking_style, is_layered_composition.

Slideshows don't get intent_match, so the intent_match filter does nothing on Get slideshows.

Example slideshow intelligence (shortened)

This leaves out the people, AI use, brands on screen, animals, screen and edit structure fields. Real slideshows include them.

{
  "intelligence_status": "ready",
  "intelligence": {
    "primary_topic": "Minimal morning skincare routine for oily skin",
    "secondary_topics": ["niacinamide benefits", "SPF layering"],
    "keywords": ["skincare", "oily skin", "morning routine"],
    "category": "beauty",
    "content_format": "tutorial",
    "narrative_arc": "tutorial_steps",
    "text_density": "balanced",
    "image_count": 5,
    "panel_texts": [
      "Stop wasting money on a 12-step routine",
      "Step 1: gentle cleanser",
      "Step 2: niacinamide serum",
      "Step 3: lightweight moisturizer",
      "Step 4: SPF 50"
    ],
    "panel_text_full": "Panel 1: Stop wasting money on a 12-step routine\n\nPanel 2: Step 1: gentle cleanser\n\nPanel 3: Step 2: niacinamide serum\n\nPanel 4: Step 3: lightweight moisturizer\n\nPanel 5: Step 4: SPF 50",
    "panel_text_word_count": 33,
    "panel_text_character_count": 185,
    "language_detected": "en",
    "is_multilingual": false,
    "emotional_tone": "inspiring",
    "sentiment": "positive",
    "has_face_visible": false,
    "background_type": "real_world_photo",
    "background_reasoning": "Product photos on a bathroom counter fill every panel.",
    "foreground_type": "real_photo_subject",
    "foreground_reasoning": "Each panel centers one skincare product with a text caption.",
    "setting": "indoor_bathroom",
    "hook_text": "Stop wasting money on a 12-step routine",
    "hook_type": "bold_claim",
    "brand_safety_tier": "safe",
    "is_nsfw": false,
    "sensitive_topics": ["none"],
    "is_educational": true,
    "is_sponsored": false,
    "brands_mentioned": ["CeraVe", "La Roche-Posay"],
    "cta_usages": [
      { "type": "link_in_bio", "text": "products linked below" }
    ],
    "trend_references": ["skin minimalism"],
    "social_proof_used": [],
    "summary": "Four-step morning routine for oily skin, presented as a numbered carousel with affordable product picks.",
    "low_confidence_fields": []
  }
}

Allowed values

A field typed enum takes one value from these lists. New values may be added, so handle ones you don't recognize. category and content_format are looser: the AI usually picks from their lists, but other values can appear.

All allowed values

category

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

content_format

tutorial, storytime, rant, review, comedy_bit, motivational, news_commentary, news_report, reaction, listicle, challenge, day_in_life, q_and_a, explainer, educational_breakdown, hot_take, unboxing, transformation, commentary_voiceover, grwm_routine, haul_restock, cooking_recipe, workout_demo, meditation, asmr, time_lapse, skit_sketch, lip_sync_dance, trend_performance, prank, experiment, street_interview, reddit_reading_storytime, silent_aesthetic, location_showcase, travel_guide, gameplay_commentary, gameplay_lets_play, stream_highlight, podcast_clip, interview_clip, ranking, compilation, tier_list, documentary_short, fancam_edit, testimonial, product_demo, ad_creative, pov_scenario, quiz_or_test, other

background_type

solid_color, illustrated_scene, real_world_photo, digital_screen_capture

A phone filmed on a desk is real_world_photo. Only direct screenshots are digital_screen_capture.

foreground_type

text_only, real_photo_subject, illustrated_subject, meme, chart_or_data_ui, none_or_minimal

hook_type

question, bold_claim, shock_statement, story_tease, tutorial_promise, controversy, before_after, pov_setup, statistic, direct_address, trend_reference, cliffhanger, negation, relatable_scenario, comparison, mystery_setup, none

speaking_style

conversational, formal, hype_energy, whisper_asmr, voiceover_narration, comedic, storytelling, instructional, monotone, aggressive, deadpan, shouting, flirty

emotional_tone

funny, inspiring, shocking, educational, controversial, heartwarming, wholesome, angry, sad, hype, calm, sarcastic, nostalgic, cringe, dark_humor, urgent, mysterious, relatable, neutral

sentiment

positive, negative, neutral, mixed

cta_usages type

follow, subscribe, like_video, comment, share, save_post, tag_friend, link_in_bio, visit_website, dm_message, buy, pre_order, download, sign_up, use_code, enter_giveaway, vote, book_appointment

social_proof_used

testimonial, before_after_results, statistic_cited, celebrity_mention, expert_endorsement, popularity_claim, user_count, award_mention

sensitive_topics

mild_profanity, strong_profanity, violence_described, drug_reference, alcohol, tobacco_vaping, gambling, controversial_politics, religious_discussion, mental_health, eating_disorders, self_harm_reference, sexual_content, hate_speech, medical_claims, financial_advice, weapons, none

["none"] means nothing sensitive was found. none can also appear next to other values, so check for anything other than none.

brand_safety_tier

safe, low_risk, medium_risk, high_risk, unsafe

transcript_quality

clean, partial, garbled

visual_format

talking_head, pov_footage, interview, street_interview, screen_recording, text_messaging_thread, slideshow_text, animation_motion_graphics, b_roll_montage, whiteboard_presentation, vlog_handheld, activity_demonstration, product_closeup, green_screen_commentary, split_screen_duet, gameplay_background, native_gameplay, stream_overlay, pip_stream_layout, food_overhead, dance_full_body, outfit_showcase, clip_compilation, live_performance, other

visual_complexity

minimal_clean, moderate, busy

caption_style

standard_subtitles, animated_word_by_word, large_bold_centered, karaoke_highlight, meme_top_bottom

camera_perspective

selfie_front, rear_camera, tripod_static, handheld_moving, overhead_topdown, drone_aerial

setting

indoor_home_general, indoor_bedroom, indoor_bathroom, indoor_kitchen, indoor_living_room, indoor_studio, indoor_gym, indoor_office, indoor_classroom, outdoor_urban, outdoor_nature, outdoor_beach, outdoor_pool, car, vehicle_other, restaurant_cafe, store_retail, event_venue, studio_set, indoor_salon_spa, indoor_medical_clinic, indoor_workshop_garage, outdoor_rural_farm, sports_venue, stage_performance, construction_site, place_of_worship, generic

lighting_quality

professional, cinematic, natural_good, golden_hour, natural_dim, ring_light, harsh_artificial, mixed, low_quality

visual_hook_type

text_hook, extreme_closeup, before_state, shocking_image, aesthetic_setup, person_speaking_to_camera, motion_action, crowded_scene, mystery_object, dramatic_zoom, animal_pet, none

text_overlay_purpose

title, list_item, statistic, quote, dialogue_label, chapter_marker, branding, cta, meme_caption, none

narrative_arc (slideshows)

listicle, story_progression, before_after, comparison, escalating_reveal, single_idea_expanded, q_and_a, meme_setup_punchline, tutorial_steps, screenshot_dump, none

text_density (slideshows)

text_dominant, balanced, image_dominant

people_source

visual (from frames), text (guessed from the words when no frames were usable), none

people_count_bucket

none, one, two, small_group_3_5, large_group_6_plus, crowd

on_screen_presence

face_presenting, face_not_presenting, body_no_face, hands_or_pov_only, animated_or_avatar_character, no_person

presence_style

on_camera_presenter, on_camera_subject, faceless_voiceover, faceless_silent, hands_or_pov, avatar_or_animated, no_person

apparent_gender

Used by people[].apparent_gender, primary_subject_gender and genders_present: female, male, ambiguous. ambiguous means the AI looked and could not tell.

subject_gender_skew

all_female, all_male, mixed, ambiguous, none

apparent_age_bracket

Used by people[].apparent_age_bracket, primary_subject_age_bracket and age_brackets_present: infant_0_3, child_4_12, teen_13_17, adult_18_24, adult_25_34, adult_35_49, adult_50_64, senior_65_plus, unknown

person role and confidence

people[].role: primary, secondary, background

people[].confidence, primary_subject_confidence and ai_confidence: high, medium, low

ai_provenance

none, ai_assisted, ai_voice, ai_visuals, ai_presenter, fully_ai, unknown

From least to most AI-made. ai_assisted: AI only helped edit or caption. ai_voice: real footage, AI voice. ai_visuals: AI-made images or video, no AI presenter. ai_presenter: an AI avatar presents. fully_ai: footage, voice and presenter are all AI.

ai_elements

synthetic_presenter, generated_imagery, generated_video, face_swap_or_clone, synthetic_voice, ai_captions_or_edit

product_presence

branded_product_featured, unbranded_product_featured, product_incidental, no_product

To find no-name products, use unbranded_product_featured. Empty brand lists can't tell you that.

animals_visible

dog, cat, other_pet, bird, horse, livestock, wildlife, aquatic, other

animal_presence

animal_featured (the video is about an animal), animal_incidental, none

screen_content_type

gameplay, text_conversation, notes_or_document, social_feed, app_ui, website, video_playback, other


Full example

A complete video from Get videos on an agent with Data Intelligence on. publish_date has no time zone marker. Read it as UTC.

Show the full video (JSON)

Video with intelligence

{
  "id": "b3a1f892-7c4e-4d8a-9f12-6e8b4a2c1d05",
  "url": "https://www.tiktok.com/@glowbysara/video/7392847561023456789",
  "description": "the glass skin routine that changed my life, step by step for beginners #skincare #glassskin #routine",
  "platform": "tiktok",
  "views": 2847000,
  "likes": 341200,
  "shares": 89400,
  "comments": 12800,
  "bookmarks": 267000,
  "publish_date": "2026-04-22T14:30:00",
  "author": {
    "country": "US",
    "username": "glowbysara",
    "verified": true,
    "followers": 890000,
    "avatar_url": "https://auth.virlo.ai/storage/v1/object/public/avatars/example.jpg"
  },
  "hashtags": ["skincare", "glassskin", "routine"],
  "thumbnail_url": "https://auth.virlo.ai/storage/v1/object/public/thumbnails/example.webp",
  "keyword_found_by": "glass skin routine",
  "is_duet": false,
  "is_stitch": false,
  "upload_region": "US",
  "upload_region_source": "tiktok_region",
  "intelligence": {
    "primary_topic": "beginner glass skin routine",
    "secondary_topics": ["glass skin", "drugstore skincare", "beginner skincare"],
    "keywords": ["glass skin", "double cleanse", "hyaluronic acid", "niacinamide", "SPF"],
    "category": "beauty",
    "content_format": "tutorial",
    "visual_format": "product_closeup",
    "visual_complexity": "moderate",
    "camera_perspective": "tripod_static",
    "setting": "indoor_bathroom",
    "lighting_quality": "ring_light",
    "has_face_visible": true,
    "scene_changed": true,
    "background_type": "real_world_photo",
    "background_reasoning": "The same bathroom vanity is behind the creator in every shot.",
    "foreground_type": "real_photo_subject",
    "foreground_reasoning": "The creator and the skincare products are the focus.",
    "hook_text": "the glass skin routine that changed my life",
    "hook_type": "bold_claim",
    "visual_hook_type": "before_state",
    "has_text_overlay": true,
    "text_overlay_purpose": "list_item",
    "text_overlay_content": "Step 1: double cleanse",
    "has_onscreen_captions": true,
    "caption_style": "animated_word_by_word",
    "transcript_quality": "clean",
    "transcript_word_count": 312,
    "transcript_character_count": 1680,
    "language_detected": "en",
    "is_multilingual": false,
    "emotional_tone": "inspiring",
    "sentiment": "positive",
    "speaking_style": "conversational",
    "brand_safety_tier": "safe",
    "is_nsfw": false,
    "sensitive_topics": ["none"],
    "is_sponsored": false,
    "brands_mentioned": ["CeraVe", "The Ordinary"],
    "cta_usages": [
      { "type": "follow", "text": "follow for part 2" },
      { "type": "save_post", "text": "save this routine" }
    ],
    "social_proof_used": ["before_after_results", "popularity_claim"],
    "trend_references": ["glass skin"],
    "summary": "Step-by-step beginner glass skin routine using drugstore products, from double cleansing to SPF. The creator talks to the camera in a bathroom and shows before and after results.",
    "is_educational": true,
    "low_confidence_fields": [],
    "people": [
      {
        "role": "primary",
        "is_synthetic": false,
        "apparent_gender": "female",
        "apparent_age_bracket": "adult_18_24",
        "apparent_age_min": 20,
        "apparent_age_max": 26,
        "confidence": "high"
      }
    ],
    "people_source": "visual",
    "people_count_bucket": "one",
    "on_screen_presence": "face_presenting",
    "presence_style": "on_camera_presenter",
    "primary_subject_gender": "female",
    "primary_subject_age_bracket": "adult_18_24",
    "primary_subject_age_min": 20,
    "primary_subject_age_max": 26,
    "primary_subject_confidence": "high",
    "genders_present": ["female"],
    "age_brackets_present": ["adult_18_24"],
    "subject_gender_skew": "all_female",
    "has_minor_visible": false,
    "has_real_person": true,
    "has_baby_visible": false,
    "has_senior_visible": false,
    "ai_provenance": "none",
    "ai_elements": [],
    "ai_confidence": "high",
    "ai_reasoning": "Real creator speaking to camera. No AI-made imagery, voice or presenter detected.",
    "brands_visible": ["CeraVe", "The Ordinary"],
    "product_presence": "branded_product_featured",
    "ip_characters": [],
    "animals_visible": [],
    "animal_presence": "none",
    "has_animal": false,
    "screen_content_type": null,
    "is_silent": false,
    "is_compilation": false,
    "is_ranking": false,
    "is_repost": false,
    "is_before_after": true,
    "is_layered_composition": false,
    "has_platform_watermark": false,
    "watermark_source": null
  },
  "intent_match": {
    "matches": true,
    "reasoning": "The caption and transcript walk through a beginner glass skin routine step by step and name drugstore products, which fits the intent."
  },
  "sound": null,
  "intelligence_status": "ready"
}

Was this page helpful?