# 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 quirk:** Satellite creator lookup / run re-read videos use `publishDate` (camelCase); Orbit/Comet videos use `publish_date`. Check both when sorting by 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`. - Billing: prepaid, 1 credit = $0.01. Every response carries `X-Cost`, `X-Balance-Remaining`. 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. - **Orbit status poll:** `GET /orbit/:id` embeds full inline results when complete (large). MCP `get_keyword_search_results` with `data_type=status` strips those arrays — use `data_type=videos` etc. for the actual items. ## 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?" / market or niche research / competitor content | **Orbit** (one-time keyword search) | `POST /orbit` | $0.50 (+$1.00 intelligence) | | "Keep watching X for me" / recurring reports | **Comet** (scheduled Orbit) | `POST /comet` | $0.50/run | | "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) + `/sounds/*` | `GET /satellite/sounds/tiktok/:music_id` | $0.50–$1.00 | | "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 | | "Find me a sound / what audio is breaking out?" | **Sounds** | `GET /sounds/trending`, `/sounds/breakout`, `/sounds/search?q=` | $0.05–$0.25 | Compose them: Orbit surfaces outlier creators → Satellite deep-dives the interesting ones → Tracking monitors the winners. That escalation path (search → inspect → monitor) answers most research tasks end to end. ## 3. Async protocol (read this before polling) Orbit, Comet runs, Satellite, audience snapshots, and post collection are asynchronous: you create a job, poll, then read. - `status`: `queued | processing | completed | failed` (Orbit/Comet 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` (search was created without intelligence — only fix is a new search with the flag) | `failed`/`skipped` (terminal). - Webhooks beat polling for long jobs: `POST /webhooks` with events like `orbit.run.completed`, `comet.run.completed`, `satellite.lookup.completed` (route on `data.type`), `tracking.cycle.completed`, `tracking.outlier_video.detected`, `audience.snapshot.completed`. Payloads carry `run_id` + `result_url` — fetch the body from `result_url` (free). Realistic timings from production (set user expectations accordingly): | Job | Typical | Plan for | Poll every | |---|---|---|---| | Orbit / Comet run | ~15–20 min median | up to 45 min | 60s | | Satellite creator lookup | ~20 s | 2 min | 10–15s | | Satellite sound lookup | ~8 min | 20 min | 30s | | Video outlier | 20–60 s (result expires in 5 min — read promptly) | 3 min | 10s | Success rates: ~92% of Orbit/Comet 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 ### Keywords (the #1 quality lever) - 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 **3–7 keywords** per Orbit (max 10; Comet max 20). Cover synonyms and adjacent phrasings of the same concept — each keyword is a separate platform search, so variety widens the net without widening the topic. - `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`: 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. ### Scope settings - `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). - `time_period`: `this_month` is the best default — `today`/`this_week` can return thin results in narrow niches; `this_year` dilutes recency. Match it to the question ("right now" → `this_week`). - `min_views`: leave unset or low (0–1000) at search time. **Filter at retrieval instead** — the videos endpoint accepts `min_views`, `platforms`, `start_date`/`end_date`, `order_by` (`views | publish_date | created_at`) for free, so over-filtering at creation just throws away data you paid for. - `enable_meta_ads: 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 40+ 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. ### What a run yields A typical completed Orbit 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 /orbit/:id/videos?limit=100&order_by=views (paginate to the end) GET /orbit/:id/slideshows?limit=100 (TikTok image carousels — distinct surface, see §8) GET /orbit/:id/ads (if meta ads enabled) GET /orbit/:id/creators/outliers?order_by=weighted_score GET /orbit/:id/sounds GET /orbit/:id/analysis/latest (structured AI analysis — see §9) GET /orbit/:id/trends/latest (AI trend themes with evidence) ``` Comet exposes the same sub-resources per configuration; results accumulate across cycles. ### 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. - Every Satellite result has a `run_id`. **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 40+ 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 (`/orbit/: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. ## 9. Reading the run-level AI analysis and trends `GET /orbit/: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 /orbit/:id/trends/latest` returns trend items with lifecycle `status`: `new` (just emerged — highest opportunity), `rising` (growing — act now), `steady`, `fading` (avoid). Each has `name`, `why_it_works`, `tactics[]`, `confidence`, engagement aggregates, `platform_breakdown`, `top_creators[]`, `peak_hour_utc`, and `prev_*` fields for cycle-over-cycle deltas. For Comets, `/trends` + `stable_key` lets you follow one trend across cycles — 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 /orbit` (3–7 specific keywords, all platforms, `this_month`, `enable_meta_ads: true`, `data_intelligence_enabled: true`, `intent` set) → poll to `finalized` → read analysis + trends → pull all videos & slideshows (paginate) → 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. **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**: successful Orbit → `POST /comet` with the same keywords (weekly cadence default) + `POST /webhooks` for `comet.run.completed` → each cycle, diff `trends` via `stable_key` and surface `new`/`rising` items. For creators: `POST /tracking/creators` → snapshots (`delta_*` fields for growth charts), auto AI reports, `posting-cadence`, on-demand `posts/collect` (depth `standard`/`deep`/`full` = 50/200/500 videos). ## 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 /orbit`, `GET /comet`, `GET /satellite/runs`, `GET /tracking/*` lists first — the answer may already exist in the team's history. - One well-scoped Orbit 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 `X-Balance-Remaining` drops below $10. - Batch Satellite (`POST /satellite/creators/batch`, up to 25 creators at $0.50 each) instead of serial single lookups when vetting a list.