# Virlo API — Agent Playbook You are an AI agent using the Virlo API to research short-form social media (TikTok, YouTube Shorts, Instagram Reels, Meta Ads) on behalf of a human user. Virlo is a data provider covering every subject: any niche, brand, creator, sound, or trend your user cares about can be researched with the same primitives. This guide tells you which endpoints to call for a given user intent, how to maximize the volume and relevance of data returned, and how to interpret what comes back. ## 1. Protocol essentials - Base URL: `https://api.virlo.ai/v1` — auth: `Authorization: Bearer virlo_tkn_…` - Params and most response fields are `snake_case`. Responses are wrapped: `{ "data": ... }`; lists add `"pagination": { page, limit, total, total_pages, has_next_page, has_prev_page }`. - **Naming note:** `publish_date` is the canonical field everywhere. Satellite responses also carry a deprecated camelCase `publishDate` alias (removed August 3, 2026) — read `publish_date`. - **Nullable fields:** `author_id` on digest videos may be `null` when the author row isn't linked yet. Sound `cover_url` may be relative or absolute depending on source. - Pagination: `page` (1-indexed) + `limit` (default 50, max 100). Exception: `/satellite/runs*` uses `offset`; `/v1/agents/:id/videos|slideshows|ads|creators/outliers` return `{ total, limit, offset, }` while `/v1/agents/:id/sounds|hashtags` use the `pagination` envelope. - Billing: prepaid, 1 credit = $0.01. `X-Cost` and `X-Credits-Used` appear on **every** response ("0.00"/"0" for free reads); `X-Balance-Remaining` and `X-Credits-Remaining` appear **only on charged responses** (cost > 0). Failed requests are not charged. `402` = balance too low (top up at https://dev.virlo.ai/dashboard/billing). **All retrieval/polling of an already-created resource is free** — you pay to create work, never to re-read results. - Errors: `400` bad params, `401` bad key, `404` wrong id (or not your team's resource), `429` rate limit (honor `Retry-After`; not a credit problem), `5xx` retry with backoff. - **Agent status poll:** `GET /v1/agents/:id` returns config + `analysis`/`analysis_data` + `pending_jobs[]` + `finalized`. Pull the heavy arrays from the dedicated sub-paths (`/videos`, `/slideshows`, etc.) rather than expecting them inline. ## 2. Route the user's intent Pick the workflow by what the user actually wants. Don't run more than you need; don't run less. | User intent sounds like | Workflow | Core call | Cost | |---|---|---|---| | "What's working in X right now?" / research or analyze a niche or keywords / competitor content | **Content Research Agent — one-shot** | `POST /v1/agents` with `is_recurring: false` | $0.50 (+$1.00 intelligence) | | "Keep watching X for me" / monitor or track a niche over time / recurring reports | **Content Research Agent — recurring** | `POST /v1/agents` with `is_recurring: true` + `cadence` | free to create, bills per scheduled run | | "Research this in Spanish / Thai / any non-English niche" / global research | **Agent, all languages** | `POST /v1/agents` with `english_only: false` + keywords/intent in that language | $0.50 (+$1.00 intelligence) | | "Show me the collected content uploaded from Canada / Russia / Australia" | **Region filter (read time, beta)** | `GET /v1/agents/:id/videos?region=CA` (also `/slideshows`) | free | | "Tell me about this creator" / influencer vetting / partner discovery | **Satellite creator** | `GET /satellite/creator/:platform/:username` | $0.50 (+$0.50 trend analysis) | | "Why did this video pop?" / benchmark one video vs the channel | **Video outlier** | `POST /satellite/video-outlier` | $0.50 | | "What's happening with this sound?" / audio strategy | **Satellite sound** (TikTok & Instagram; not YouTube) + `/sounds/*` | `GET /satellite/sounds/:platform/:music_id` | $0.50–$1.00 | | "What's working under #X?" / hashtag deep-dive / tag research | **Satellite hashtag** (TikTok, Instagram, YouTube Shorts) | `GET /satellite/hashtags/:platform/:hashtag` | $0.50–$2.50 | | "Track this creator/video over time" / lifecycle, growth charts | **Tracking** | `POST /tracking/creators` or `/tracking/videos` | $0.25/cycle | | "Who watches this creator?" | **Audience** | `audience_demographics=true` on Satellite, or tracking audience-refresh | $0.50 per fresh snapshot | | "What's trending today, broadly?" | **Digests** | `GET /trends/digest`, `GET /videos/digest`, `GET /hashtags` | $0.05–$0.25 | | "What's trending in the US / UK / Australia?" | **Regional trends** | `GET /trends?region=us` (`gb`, `au`; default `global`; `GET /trends/regions` lists codes, free) | $0.25 | | "Find me a sound / what audio is breaking out?" | **Sounds** | `GET /sounds/trending`, `/sounds/breakout`, `/sounds/search?q=` | $0.05–$0.25 | `POST /v1/agents` is the unified research primitive: `is_recurring: false` runs once (replaces Orbit), `is_recurring: true` runs on a cadence (replaces Comet). Legacy aside: `POST /orbit` and `POST /comet` still work but are DEPRECATED and removed August 3, 2026 — IDs are interchangeable with agents, so migrate. Compose the workflows: a one-shot agent surfaces outlier creators → Satellite deep-dives the interesting ones → Tracking monitors the winners; promote a winning one-shot to a recurring agent to keep watching. That escalation path (research → inspect → monitor) answers most tasks end to end. ## 3. Async protocol (read this before polling) Content Research Agent runs, Satellite, audience snapshots, and post collection are asynchronous: you create a job, poll, then read. - `status`: `pending | processing | completed | failed` (agent runs also have `partial_failure` — **treat it as success**: one platform/keyword failed but the rest of the data is there). - `finalized: true` is the only "truly done" signal. `status: completed` with `finalized: false` means secondary AI jobs (viral analysis, per-video intelligence, audience snapshots) are still running — `null` analysis/intelligence fields mean "not yet", **not** "no data". - `pending_jobs[]` (when not finalized) lists each in-flight job with `poll_url`, `result_path`, `webhook_event`, `retry_after_seconds`. Use `retry_after_seconds` as your sleep interval. - Per-video `intelligence_status`: `ready` (use it) | `pending` (re-fetch later) | `disabled` (agent was created without intelligence — only fix is a new agent with the flag) | `failed`/`skipped` (terminal). - Webhooks beat polling for long jobs: `POST /webhooks`. Preferred: `content_research_agent.run.completed` (carries `is_recurring`, fires for both one-shot and recurring agents). Other events: `content_research_agent.event.detected` (a recurring agent confirmed a breaking event in its niche; fires between runs — see §4.1), `satellite.lookup.completed` (route on `data.type`), `tracking.cycle.completed`, `tracking.outlier_video.detected`, `tracking.paused`, `audience.snapshot.completed`, `trends.daily.completed`, `trends.region.completed`, plus legacy `orbit.run.completed` / `comet.run.completed`. Payloads carry `run_id` + `result_url` — fetch the body from `result_url` (free). Webhook management responses are NOT `{data}`-enveloped (bare array/object). Realistic timings from production (set user expectations accordingly): | Job | Typical | Plan for | Poll every | |---|---|---|---| | Agent one-shot run (is_recurring: false) | ~15–20 min median | up to 45 min (with meta ads) | 60s | | Agent recurring run (is_recurring: true) | fires on cadence; first run at create | same per-run window | webhook / on cadence | | Satellite creator lookup | ~20 s | 2 min | 10–15s | | Satellite sound lookup | ~8 min | 20 min | 30s | | Satellite hashtag lookup | 1–3 min (~8 min with trend_analysis) | 20 min | 10–15s (30s with trends) | | Video outlier | 20–60 s (status cache lasts 24 h; NOT in the durable runs ledger yet — store the result promptly) | 3 min | 10s | Success rates: ~92% of agent runs complete clean, ~7% land `partial_failure` (still usable data), <1% hard-fail. Satellite lookups succeed ~96–98%. If a run hard-fails, retry once before reporting failure. ## 4. Maximizing the data you get back ### Intent + keywords (the #1 quality lever) **Formula:** `[Find/Monitor] [content type] about [niche] for [use case], [not / exclude X].` Aim for ~40–250 characters. **Good (production patterns that returned 1k+ linked videos):** - "Find GRWM / beginner morning skincare routines and honest drugstore reviews — not dermatologist lectures." - "Find outlier JS/web-dev education creators — hooks and formats to adapt for faceless carousels." - "Find viral Gen Z videos that sell a photo/AI mobile app and push a download; exclude desktop tutorials." **Bad (avoid):** - `Keyword research: jeep, fitness` / `Monitor niche: handmade, crafts` — synthesized dumps; give the filter nothing to judge - `I want to find viral video` — no niche - Keyword list as intent (`skin / curly hair / hooks`) — not a sentence **Workflow:** write intent → `suggest-keywords` (free; check `quality.passes`) → create agent with the **same** intent + returned keywords. Full cookbook: MCP resource `virlo://docs/intent-cookbook` or https://dev.virlo.ai/intent-cookbook.txt - Use **specific multi-word phrases**: "jeep wrangler mods", "high protein meal prep", "TikTok Shop strategies". Single generic words ("jeep", "fitness") return scattered, off-topic results. - Send **6–10 keywords** (3–7 is fine for a tight topic; hard cap 50). Cover synonyms of the same concept — each keyword is a separate platform search. Hashtag-style tokens are normalized (`#progressivehouse` == `progressive house`). - `exclude_keywords` removes noise (e.g. exclude "asmr" from a cooking search). `exclude_keywords_strict: true` also matches transcripts — use it when the noise term appears in speech, not captions. - `intent` (required): a one-sentence natural-language description of what the user is really after (e.g. "find UGC-style ads for skincare brands targeting Gen Z"). When intelligence is enabled this powers `intent_match=true` filtering on the videos endpoint — videos are AI-matched against the intent, the strongest on-topic filter available. **Never synthesize intent from keywords** (e.g. "Keyword research: jeep, fitness") — that is the #1 weak pattern in production. Prefer `POST /v1/agents/suggest-keywords` (MCP: `suggest_keywords`, free) first, then create with the same intent. Full good/bad examples: `virlo://docs/intent-cookbook` / https://dev.virlo.ai/intent-cookbook.txt ### Scope settings - `is_recurring`: `false` for a one-time research pass, `true` (with `cadence`) to keep collecting on a schedule. Recurring agents also self-optimize (§4.1). - `cadence` (required when `is_recurring: true`): `daily` | `weekly` | `monthly`, or a cron expression that runs at most once/day. Rejected if `is_recurring: false`. - `platforms`: default to all three unless the user's question is platform-specific. Expect a TikTok-heavy mix (recent runs average roughly 70% TikTok / 15% Instagram / 10% YouTube of linked results) and very different view scales per platform (see §6). - **No `min_views` / `time_range` at create.** Collection is system-managed — the agent gathers broadly. **Filter at read time instead:** `GET /v1/agents/:id/videos` accepts `min_views`, `platforms`, `start_date`/`end_date`, `order_by` (`views | publish_date | created_at`), `sort`, `intent_match`, and `region` for free. Over-filtering at creation is impossible by design; do your slicing on retrieval. - `english_only` (default `true`): keep it `true` for English-only research; set `false` to collect content in **every language** — the switch that opens the gates to non-English/global niches. When you set `false`, write `keywords` and `intent` in the **target language** (the keyword engine adapts to your input language); leaving them in English while flipping the flag will return little. Applies to future runs on recurring agents; never re-filters already-collected content. - `meta_ads_enabled: true` adds Meta ad-library results — turn it on whenever the user has any commercial angle (competitor ads, offer research, paid creative). - `data_intelligence_enabled: true` (+$1.00): adds 43 AI fields per video and slideshow (§7). Enable it whenever the user's task involves *understanding content* (hooks, formats, messaging, brand mentions, CTAs) rather than just counting views. It cannot be added retroactively. - `region` filter (read time, **beta**): `GET /v1/agents/:id/videos?region=US` and `GET /v1/agents/:id/slideshows?region=US` restrict results to a single **upload country** (ISO-3166-1 alpha-2, case-insensitive — resolve country names the user says to the code yourself). Each item carries `upload_region` (videos) / `region` (slideshows), `null` when unresolved. Region is deterministic where the platform provides it (TikTok video/creator region, YouTube channel country) and AI-inferred otherwise, so coverage is partial and improving — unresolved items are excluded when you filter. **Slideshows have the highest region coverage** (straight from TikTok), so a slideshow-heavy niche is the best surface for regional slicing. Pairs naturally with `english_only: false` for geo-targeted non-English research. ### What a run yields A typical completed one-shot agent links ~40–110 videos (median ~44, mean ~106; broad niches reach 300+), plus TikTok slideshows, Meta ads (~13 avg when enabled), ~4 outlier creators, top sounds, an AI analysis, and AI trend themes. **Pull every free sub-resource** — most agents under-read what they already paid for: ``` GET /v1/agents/:id/videos?limit=100&order_by=views (paginate; add region=US to filter by upload country — beta) GET /v1/agents/:id/slideshows?limit=100 (TikTok image carousels — distinct surface, see §8; highest region coverage) GET /v1/agents/:id/ads (if meta_ads_enabled) GET /v1/agents/:id/creators/outliers?order_by=weighted_score GET /v1/agents/:id/sounds (sort=rising/growth_7d; rows carry a lifecycle label — filter client-side) GET /v1/agents/:id/hashtags (sort=volume/growth/avg_views) GET /v1/agents/:id/analysis/latest (structured AI analysis — see §9) GET /v1/agents/:id/trends/latest (AI trend themes with evidence) ``` Recurring agents expose the same sub-resources; results accumulate across runs, and `/analysis` + `/trends` keep a per-run history (follow one trend by `stable_key`). ### 4.1 Recurring-agent autonomy Recurring agents (`is_recurring: true`) self-optimize by proposing `keyword_refresh` and `filter_change` changes. Read `GET /v1/agents/:id/proposals` (status `pending | applied | auto_applied | dismissed | reverted`) and `GET /v1/agents/:id/activity`, then `POST /v1/agents/:id/proposals/:pid/{apply,dismiss,revert}`. The **first manual apply unlocks autopilot** for the team — after that, new agents default to `autonomy_level: "autopilot"` (`autopilot_unlocked: true`) instead of the initial `suggest`. Set the mode explicitly with `PUT /v1/agents/:id/autonomy { autonomy_level, cognition_enabled }`. One-shot agents expose these fields but never generate proposals or activity. Recurring agents also stay aware of breaking events in their niche: `GET /v1/agents/:id/events` (free) lists detected stories (`title`, `summary`, `salience` 0–10, `status` `candidate|confirmed|dismissed|expired`, timely `keywords`, `evidence` videos), active first — and the `content_research_agent.event.detected` webhook pushes each one the moment it is confirmed, between scheduled runs. Use it to explain WHY an agent recently adapted its keywords or collected early. ### Satellite yield levers - `include=videos,outliers` and `max_videos=50` (default is only 20; max 100) — always raise this for real research. - `cross_links=true`: discovers the creator's other-platform profiles (high-confidence only). - `trend_analysis=true` (+$0.50): forces a 100-video deep fetch and returns LLM-detected trends over the creator's body of work, with `time_windows[]` computed from real publish dates, `resurged` (≥2 disjoint windows), `momentum` (`stronger|weaker|similar` vs prior window), and `evidence_video_ids` mapping into `videos[]`. - Sound lookups with `trend_analysis=true` fetch ~300 videos. If the corpus is too small, `trends.status === "insufficient_corpus"` and the surcharge auto-refunds. - Hashtag lookups (`GET /satellite/hashtags/:platform/:hashtag`, platforms `tiktok` | `instagram` | `youtube`): pass the tag with or without `#` (URL-encode as `%23`; normalized to lowercase), `sort=top` (views desc, default) or `recent` (publish date desc — stats are order-independent), `max_videos` 1–100 (default 50). Repeating the same lookup within 6 hours returns the cached run free (`cached: true`). `trend_analysis=true` (+$0.50) forces a ~300-video deep fetch (ignores `max_videos`) and returns the same trends shape as sound lookups. - Hashtag `depth` (`standard` default | `deep` | `full`): `standard` collects `max_videos` at the $0.50 base; `deep` ~300 videos (+$0.50, $1.00 total); `full` ~500 videos (+$1.50, $2.00 total) — `deep`/`full` ignore `max_videos`. The deep surcharge is waived with `trend_analysis=true` (trends already fetch ~300 videos): deep+trends $1.00, full+trends $2.50. Instagram is `standard`-only (`deep`/`full` return 400 before billing); TikTok and YouTube support all three tiers. The 6-hour cache only satisfies a request at an equal-or-deeper stored depth — a deeper cached run serves a shallower request free; a shallower one re-scrapes and bills. - Hashtag coverage differs by platform: TikTok reads the native challenge feed (fullest data); YouTube reads the native hashtag page, Shorts only — each Short is detail-enriched (exact views, likes, comments, publish dates, durations, channel follower counts, sound attribution) though `shares`/`collects` stay 0; Instagram is Google-indexed public Reels — best-effort coverage, shallower than the other two (upstream depth caps at ~11 pages). Never compare `engagement_rate` across platforms; check `sample_quality.note` (`insufficient_corpus` → `deep_corpus`) before leaning on the stats. - Hashtag results are pivot maps: `related_hashtags` (20 co-occurring tags, the looked-up tag excluded) seeds the next hashtag lookup; `top_sounds` (10) feeds `GET /satellite/sounds/:platform/:music_id` — chain lookups instead of guessing fresh queries. - Every creator/sound/hashtag/batch Satellite result has a `run_id` (video-outlier results don't, yet). **Save it.** `GET /satellite/runs/:run_id` (and `/runs/:run_id/videos`) re-reads the full result free, forever. Never re-buy a lookup you already ran; check `GET /satellite/runs?type=…` first. ## 5. Spotting the most viral content Views alone mislead — a 500K-view video from a 10M-follower account is routine; the same views from a 3K-follower account is a signal. Virlo's canonical ranking is the **weighted virality score**: ``` ratio = views / followers (only when followers > 0 and ratio > 1) weighted_score = ln(ratio) × ln(followers) ``` | weighted_score | Read it as | |---|---| | ≥ 35 | Exceptional — massive outperformance, study frame by frame | | 25–35 | Very strong — beats expectations at any audience size | | 18–25 | Strong — clearly above the creator's baseline | | 10–18 | Promising — real viral traction | | < 10 | Emerging / routine | Practical procedure for "find the most viral": 1. Pull all videos from the run, compute `weighted_score` per video from `views` and `author.followers`. Rank by it, not raw views. 2. Sanity-check raw multiplier too: `views/followers` ≥ 20× is notable, ≥ 100× is exceptional, ≥ 1000× is a mega-outlier. 3. Compute `engagement_rate = (likes + comments + shares) / views`. High views + high engagement (>5%) = resonance; high views + low engagement (<1%) = passive distribution (sounds, reposts, paid). 4. Cross-check against the AI's pick: `analysis_data.top_10_breakdown` (from `/analysis/latest`) is an LLM-curated standout list that also weighs content quality and topicality — where your math ranking and the AI ranking agree, you've found the real winners. 5. **Creator outliers** (`/creators/outliers`) apply the same logic at account level: `outlier_ratio` (avg views vs follower baseline, shown as "N× reach") and `weighted_score` (default sort). These are under-followed creators consistently outperforming — best source for rising talent and partnership leads. 6. Recency matters: a high score on a video published this week is a live trend; the same score from months ago is history. Always read `publish_date`. ## 6. Calibrate expectations to platform reality Sampled from recent production runs — use these to judge whether a result is actually impressive: | Platform | Median views | P90 | P99 | Transcript coverage | |---|---|---|---|---| | TikTok | ~39K | ~1M | ~6.6M | ~63% | | Instagram Reels | ~3.8K | ~175K | ~3M | ~0% (no transcripts) | | YouTube Shorts | ~1K | ~32K | ~2.5M | ~38% | Implications: never compare raw view counts across platforms; a 100K-view Reel is a bigger deal than a 100K-view TikTok. Speech-based insights (hooks from transcripts, spoken messaging) come mostly from TikTok — for Instagram lean on descriptions, hashtags, and (with intelligence) AI visual analysis instead. ## 7. Reading video intelligence (the 43 AI fields) With `data_intelligence_enabled`, each video carries an `intelligence` object (check `intelligence_status === "ready"`). ~94% of videos analyze successfully; the rest fail at frame extraction or analysis and stay sparse. Fields, grouped by how to use them: **Classification** — `primary_topic` (free-text main subject), `secondary_topics[]`, `keywords[]`, `category`, `content_format`. Use these to verify on-topic-ness and to segment the result set. `content_format` has a canonical core — `explainer`, `tutorial`, `review`, `storytime`, `listicle`, `silent_aesthetic`, `motivational`, `skit_sketch`, `comedy_bit`, `day_in_life`, `rant`, `news_commentary`, `challenge`, `q_and_a`, `transformation`, `hot_take`, `reaction`, `unboxing`, `grwm_routine`, `vlog`, `meme`, etc. — plus a long free-text tail; **match formats loosely/normalized** (treat `skit`, `comedy_skit`, `skit_sketch` as one bucket). **Hook analysis (highest-value fields)** — `hook_text` is the literal opening copy (spoken or on-screen); `hook_type` is one of a closed set: `tutorial_promise`, `bold_claim`, `question`, `relatable_scenario`, `direct_address`, `pov_setup`, `shock_statement`, `story_tease`, `negation`, `statistic`, `before_after`, `trend_reference`, `comparison`, `mystery_setup`, `controversy`, `cliffhanger`, `none`. `visual_hook_type` covers the visual opener (e.g. `text_hook`). To answer "what hooks work in this niche": group the top-weighted-score videos by `hook_type`, then quote the actual `hook_text` strings as replicable templates. **Visual production** — `visual_format` (closed set: `talking_head`, `b_roll_montage`, `activity_demonstration`, `green_screen_commentary`, `product_closeup`, `animation_motion_graphics`, `slideshow_text`, `vlog_handheld`, `screen_recording`, `interview`, `split_screen_duet`, `pov_footage`, `native_gameplay`, `dance_full_body`, `street_interview`, …), `setting`, `camera_perspective`, `lighting_quality`, `visual_complexity`, `has_face_visible`, `has_text_overlay` + `text_overlay_content`/`text_overlay_purpose`, `background_type`/`foreground_type`. Tells you the production recipe — what to film and how, without watching the video. **Speech & captions** — `transcript_word_count`, `transcript_quality`, `language_detected`, `speaking_style`, `has_onscreen_captions`, `caption_style`. `transcript_word_count: 0` with a populated visual analysis = silent/aesthetic content, a deliberate format, not missing data. **Tone** — `emotional_tone` (closed set: `educational`, `neutral`, `inspiring`, `funny`, `hype`, `relatable`, `calm`, `shocking`, `wholesome`, `angry`, `heartwarming`, `mysterious`, `urgent`, `sad`, `controversial`, `nostalgic`, `sarcastic`, `cringe`, `dark_humor`) and `sentiment` (`positive|neutral|negative`). **Commercial signals** — `is_sponsored`, `brands_mentioned[]`, `cta_usages[]` (objects like `{ "type": "link_in_bio", "text": "Link in bio" }`), `social_proof_used[]`, `trend_references[]`. This is competitive/monetization intelligence: who's being paid, what brands appear organically, which CTAs the niche actually uses. **Safety & suitability** — `brand_safety_tier`, `is_nsfw`, `sensitive_topics[]`, `is_educational`. Filter on these for brand-fit shortlists. **Synthesis & trust** — `summary` (1-paragraph AI description, present on ~100% of analyzed videos; the fastest way to "watch" 100 videos is to read 100 summaries), and `low_confidence_fields[]` — **discount any field listed there**. Aggregation pattern: the strongest insight format is distribution-over-winners. Take the top quartile by `weighted_score`, count `hook_type` × `content_format` × `emotional_tone` buckets, then contrast against the bottom quartile. Differences between those distributions are the niche's actual playbook. ## 8. Slideshows (TikTok image carousels) Slideshows are a separate result surface (`/v1/agents/:id/slideshows`) with their own intelligence — don't ignore them; in product, education, and lifestyle niches they regularly outperform video. Slideshow intelligence shares the core fields (hook, topic, format, tone, brands, CTAs, safety, summary) and adds: - `image_count` — number of slides. - `panel_texts[]` / `panel_text_full` — the extracted on-screen text of every slide, in order. **This is the entire content of most slideshows** — read it like a script; it's directly replicable copy. - `narrative_arc` — how the slides progress (listicle, tutorial steps, before/after…). The slideshow equivalent of content structure. - `text_density` — text-dominant vs image-dominant balance. Video-only fields (`visual_format`, `camera_perspective`, captions, transcript) don't exist on slideshows; their absence is structural, not missing data. Hook = slide 1: analyze `hook_text` on slideshows exactly like videos. Slideshows also carry a deterministic `region` (TikTok upload region, ISO-3166-1 alpha-2) — the **highest-coverage** region signal Virlo exposes. Filter with `GET /v1/agents/:id/slideshows?region=US` (beta; see §4) when the user wants content from a specific country. ## 9. Reading the run-level AI analysis and trends `GET /v1/agents/:id/analysis/latest` returns `analysis_data` — treat it as a pre-computed research report: - `overview` + `key_highlight` — executive summary; `key_highlight` is the single most important finding. - `themes[]` — clusters of what's working, each with `name`, `why_it_works`, `tactics[]`, `confidence` (0–1; weight ≥0.7 heavily, mention <0.5 as tentative), `video_count`, and `evidence_video_ids[]` — **always join evidence ids back to the video list** so claims stay grounded in retrievable examples. - `viral_tactics[]` — cross-theme replicable techniques. - `top_10_breakdown` — AI-curated standout videos with reasoning. - `timing_analysis` — posting-time patterns observed in the data. - `excluded_videos` — what the AI judged off-topic (useful to gauge result purity). - `connecting_thread` — the meta-pattern across themes. `GET /v1/agents/:id/trends/latest` returns trend items with lifecycle `status`: `new` (just emerged — highest opportunity), `rising` (growing — act now), `steady`, `fading` (avoid). Each has `rank`, `name`, `why_it_works`, `tactics[]`, `confidence`, engagement aggregates, `avg_virality_score`, `platform_breakdown`, `top_creators[]`, `peak_hour_utc`, and `prev_*` fields for run-over-run deltas. On recurring agents, `GET /v1/agents/:id/trends?stable_key=…` lets you follow one trend across runs — a trend going `new → rising` with growing `video_count` is your strongest "post about this now" signal. ## 10. Workflow recipes **Full niche research** ($1.50 with intelligence) — the default for "understand X": `POST /v1/agents` (`is_recurring: false`, 3–7 specific keywords, all platforms, `meta_ads_enabled: true`, `data_intelligence_enabled: true`, `intent` set) → poll `GET /v1/agents/:id` to `finalized: true` → read analysis + trends → pull all videos & slideshows (paginate; slice with `min_views`/dates/`order_by` at read time) → rank by weighted score (§5) → aggregate intelligence over winners (§7) → check outlier creators and sounds → deliver: top examples with hook quotes, format/hook distribution among winners, trend lifecycle calls, creator shortlist. **Global / non-English research + region filtering** ($0.50–$1.50): `POST /v1/agents` with `english_only: false` and `keywords`/`intent` written in the target language → poll to `finalized: true` → read videos & slideshows, adding `region=` (e.g. `region=MX`) to slice by upload country (slideshows have the highest coverage). Deliver top examples per region with `upload_region` shown. Region is beta (§4). **Creator vetting** ($0.50–$1.50): `GET /satellite/creator/:platform/:user?include=videos,outliers&cross_links=true&max_videos=50` (+`trend_analysis=true` for content-pattern history; +`audience_demographics=true&audience_geography=true` for who their audience is) → poll → report profile stats, outlier videos, cross-platform presence, audience confidence-aware (§11). Save `run_id`. **"Why did this video pop?"** ($0.50): `POST /satellite/video-outlier` → compare the video's metrics vs creator baseline → if intelligence is available on related searches, contrast its hook/format against the creator's norm. **Sound strategy** ($0.55–$1.25): `/sounds/search` or `/sounds/trending`/`/sounds/breakout` to find `music_id` → `GET /satellite/sounds/tiktok/:music_id?trend_analysis=true` → read `stats.velocity.is_accelerating`, `top_creators`, `trends[].time_windows/resurged/momentum` → recommendation: accelerating + `new`/`rising` patterns = use it now; `fading`/`weaker` momentum = skip. **Continuous monitoring**: `POST /v1/agents` with `is_recurring: true` + `cadence: "weekly"` (same keywords as a winning one-shot) + `POST /webhooks` for `content_research_agent.run.completed` → each run, diff `trends` via `stable_key` and surface `new`/`rising` items; let the agent's autonomy proposals (§4.1) refresh keywords over time. For creators: `POST /tracking/creators` (optional `collection_depth: standard|deep|full` for an initial deep post back-fill, +$0.50/$1.00/$2.00) → snapshots (`delta_*` fields for growth charts), auto AI reports, `posting-cadence`, then `GET /tracking/creators/:id/posts` (free) to enumerate collected posts with per-post metrics, outlier flags, and a nested `sound` object (`sound.external_id` = platform-native sound id for music/campaign matching; null for YouTube). Deepen history with on-demand `posts/collect` (depth `standard`/`deep`/`full` = 50/200/500 videos); TikTok/Instagram posts carry sound after any cycle, YouTube only on deep/full collections. ## 11. Audience data — trust rules Audience snapshots (demographics + geography) are derived from **engaged commenters**, not the follower count. Always read the reliability fields before presenting numbers: - `confidence_level`: `high`/`medium` → usable; `low` → say "limited signal", don't state percentages as fact. - `data_source`: `comments` (best) > `comments_extended` > `mixed` > `followers` (TikTok-only, capped at medium) > `profile_only` (synthesized fallback, always low confidence, age/gender null, automatically refunded — treat as "no audience data available"). - Cache-first: snapshots are reused free for 30 days (`freshness_days`); only cache misses cost $0.50. ## 12. Spend discipline - Creation costs money; reading is free. Exhaust the free sub-resources of resources you already created before creating new ones. - Check `GET /v1/agents`, `GET /satellite/runs`, `GET /tracking/*` lists first — the answer may already exist in the team's history. - One well-scoped agent beats three vague ones. Spend effort on keyword craft, not retries. - Quote costs before multi-step plans (e.g. "this research plan costs ~$2.50") and check `GET /account/balance` (free) when starting a session. Warn the user when that balance drops below $10 — read it from `GET /account/balance`, not the `X-Balance-Remaining` header (which only appears on charged responses). - Batch Satellite (`POST /satellite/creators/batch`, up to 25 creators at $0.50 each) instead of serial single lookups when vetting a list.