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.
- 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: truewhen you create an agent. A recurring agent can also turn it on later.- You get back
- An
intelligenceblock on each video and slideshow. Videos also getintent_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
- You create a Content Research Agent with
data_intelligence_enabled: true. - The agent collects videos and slideshows for your keywords. Each round of collecting is a run.
- 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
intelligenceblock. - 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:
| Group | What it tells you | Fields |
|---|---|---|
| Content | Topic, category and kind of content | 5 |
| Visual | Format, camera, setting, lighting | 11 |
| Hook and on-screen text | The opening line and the text on screen | 6 |
| Captions and transcript | Burned-in captions, spoken words, language | 7 |
| Tone | Mood, sentiment, speaking style | 3 |
| Brand safety | Safety tier, sensitive topics, sponsorship, brands named | 5 |
| Engagement tactics | Calls to action, social proof, trend references | 3 |
| Summary | Short summary, educational or not, what the AI was unsure about | 3 |
| People on screen | Who appears, apparent age and gender, faceless or on camera | 17 |
| AI use | Whether the video itself was made with AI | 4 |
| Brands and products on screen | Logos, products and characters in frame | 3 |
| Animals | Which animals appear and whether they are the subject | 3 |
| Screen and audio | Screen recordings, and whether anyone speaks | 2 |
| Edit structure | Compilations, rankings, reposts, before-and-after, watermarks | 7 |
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.
Creator lookups with data_intelligence get only the first eight groups: 43 fields per video, 35 per slideshow.
Filters to apply yourself
| You want | Check that |
|---|---|
| Faceless videos | presence_style is faceless_voiceover, faceless_silent or hands_or_pov |
| A person talking to the camera | presence_style is on_camera_presenter and is_silent is false |
| No AI-made content | ai_provenance is none |
| Original posts only | is_repost and is_compilation are false |
| Sponsored posts | is_sponsored is true |
| An animal is the star | animal_presence is animal_featured. For pets, also check animals_visible has dog, cat or other_pet |
| No talking | is_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 type | Without | With Data Intelligence | When you pay |
|---|---|---|---|
| Runs once | $0.50 | $1.50 | When you create it, even if the run later fails |
| Recurring (runs on a schedule) | $0.50 per run | $1.50 per run | After 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-Costheader). - 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
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
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_status | Meaning | What to do |
|---|---|---|
ready | The fields are in. | Use them. |
pending | Data 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". |
disabled | Data Intelligence is off for this agent. | Turn it on for future runs, or ignore the field. |
What to plan for:
- Not every video gets fields. On one agent, about a third of its videos were still
pendingthree days after the run. Almost all hadintent_matchfalse: videos that don't fit your intent usually skip the full review. finalized: truedoes not wait for the fields. The agent can report its run as finished (Get agent) while videos are stillpending. Read again later to pick them up.- 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 becauselow_confidence_fieldsincludestier3_unavailable. - Turned it on later? Videos from earlier runs show
pendingand usually stay that way.
Details for developers
- Single-value fields can be
nullin areadyblock when the AI could not tell, including many true/false fields. For example,hook_typeisnullwhen the video has no opening line at all. A weak opening line getsnone. - List fields are
[], nevernull.is_nsfw,is_sponsored,is_educationalandis_multilingualare nevernulleither: they readfalsewhen nothing was found, sofalsecan also mean "couldn't tell". low_confidence_fieldscan hold names that aren't fields, likeinferred_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
readyat 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_matchcan arrive beforeintelligence. Videos that getfalseusually never get the other fields.- Videos only. Slideshows don't get it.
- It sits next to
intelligenceon the video, not inside it. It isnullwhen 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
trueif 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
Good for browsing, not counting. It filters one page at a time. A page can be short or empty while later pages still have matches, and total counts only that page (with limit=1, usually 0). To count matches, read every page without the filter and count intent_match.matches yourself, as Example 1 does.
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
tutorialorreview. Usually one of the 52 listed values.
Visual
- Name
visual_format- Type
- enum
- Description
- Main visual style, like
talking_headorscreen_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
questionorbold_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.
0when 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
safetounsafe.
- 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 }.textis 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_unavailablehere 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_presencewith 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
peoplewas 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
nonetofully_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.
nullunless 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
tiktokorcapcut.
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_stepsorbefore_after.
- Name
text_density- Type
- enum
- Description
- Whether text or images carry the post:
text_dominant,balancedorimage_dominant.
- Name
image_count- Type
- integer
- Description
- Panels analyzed, up to 10. The slideshow's
imageslist 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 benull.
- 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"
}
