Track creators and videos

Watch competitors, vet a creator before a paid deal, or see how a sponsored post performs. Virlo re-checks a TikTok, YouTube, or Instagram creator or video on your schedule.

At a glance
What it does
Watches a creator or one video, and writes a fresh AI report on each check.
You send
A creator's handle or profile link, or a video link, plus how often to check.
You get back
An ID right away. Then numbers, history, AI reports, and alerts such as breakout videos.
Cost
$0.25 to start, then $0.25 per check until you pause or stop. Reading is free.
How long
The first report takes 1 to 2 minutes for a creator, and under a minute for a video.

How tracking works

  1. Start with Track a creator or Track a video. You get an id and pay $0.25, which covers the first check.
  2. Wait for the first check. Ask for the item again every 15 seconds, or whatever retry_after_seconds says (free; this is called polling), until enrichment_status is ready or failed. Or use the tracking.cycle.completed webhook.
  3. Read the results, free: snapshots (history), the AI report, signals (alerts), and posts.
  4. It repeats at $0.25 per check until you pause or stop.
FieldValues
enrichment_statusReport progress: pending, processing, ready, or failed. failed: 3 attempts in a row failed (for example, a wrong handle). A new item shows this within about a minute. Tracking pauses, and a failed first check is refunded.
statusactive, paused (no checks or charges: you paused it, or Virlo did after 3 failures or a low balance), or deleted (stopped, but still readable by id).

A check (the API also says cycle or scrape) fetches the latest numbers, saves them as a snapshot, and writes an AI report. An audience snapshot is different: a $0.50 profile of a creator's commenters.

Details for developers

Base URL: https://api.virlo.ai/v1/tracking. Results come in data, and lists add pagination. Times are ISO 8601 in UTC. Unknown parameters return 400. Rates are fractions (0.05 = 5%) unless noted. Tracked creators also have pending_jobs and finalized, as in How results load. Tracked videos don't.

Images. For tracked creators, avatar_url, a post's thumbnail_url, and a sound's cover_url are file names. Put https://auth.virlo.ai/storage/v1/object/public/ plus avatars/, thumbnails/, or sound-covers/ in front. A value that starts with https:// is already a link. Tracked videos return platform links, and TikTok's expire.


What it costs

  • Start: $0.25, covering the first check. Refunded if Virlo can't find the creator or video.
  • Each later check: $0.25, charged only when it succeeds.
  • Extras: older posts $0.50 to $2.00, and an audience snapshot $0.50.
  • Everything else is free.

Later checks are billed silently (only the start charge appears in the X-Cost header), so watch your balance and Usage page. If your balance can't cover a check, tracking pauses and won't restart when you add funds: resume it yourself.


POST/v1/tracking/creators

Track a creator

Starts tracking a creator and runs the first check right away.

Cost: $0.25. Refunded within about a minute if Virlo can't find the creator.

How long: 1 to 2 minutes for the first report. Poll Get tracked creator.

Each creator can be tracked once per account, across the web app and the API. If it's already tracked, you get a 409 and no charge. The message says where. If it's in this key's workspace, you also get its creator_id. A creator you stopped through the API is restored instead.

Request body

  • Name
    platform
    Type
    string
    Required
    *
    Description

    tiktok, youtube, or instagram, in lowercase.

  • Name
    handle
    Type
    string
    Description

    Such as khaby.lame. Send handle or url. If both, handle wins.

  • Name
    url
    Type
    string
    Description

    The profile link, with https://: https://www.tiktok.com/@username, https://www.instagram.com/username, or https://www.youtube.com/@handle (/channel/, /c/, and /user/ links work too).

  • Name
    scrape_cadence
    Type
    string
    Description

    six_hours, twelve_hours, daily (default), every_other_day, weekly, bi_weekly, or monthly.

  • Name
    collection_depth
    Type
    string
    Description

    Also collect older posts once: standard (50 videos, +$0.50), deep (200, +$1.00) or full (500, +$2.00). Refunded if it finds no posts or fails.

Request

POST
/v1/tracking/creators
curl -X POST https://api.virlo.ai/v1/tracking/creators \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "platform": "tiktok",
    "handle": "khaby.lame",
    "scrape_cadence": "daily"
  }'

Response

{
  "data": {
    "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "status": "active",
    "message": "Creator tracking started. Initial metrics and AI report are being generated.",
    "pending_jobs": [
      {
        "type": "tracking_report",
        "status": "pending",
        "poll_url": "/v1/tracking/creators/a1b2c3d4-e5f6-7890-abcd-ef1234567890",
        "result_path": "data",
        "webhook_event": "tracking.cycle.completed",
        "retry_after_seconds": 15
      }
    ],
    "finalized": false
  }
}

A 402 has two shapes. Both have code and required_credits.


GET/v1/tracking/creators

List tracked creators

Creators your team tracks through the API, newest first, including paused ones. Stopped creators and creators tracked in the web app aren't listed. Each item is a full creator.

Cost: Free.

Query parameters

  • Name
    search
    Type
    string
    Description

    Part of a handle or display name.

  • Name
    platform
    Type
    string
    Description

    tiktok, youtube, or instagram.

  • Name
    page
    Type
    integer
    Description

    Default 1.

  • Name
    limit
    Type
    integer
    Description

    Default 20. Over 100 counts as 100.

Request

GET
/v1/tracking/creators
curl -G https://api.virlo.ai/v1/tracking/creators \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -d search=khaby

Response

{
  "data": [
    {
      "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
      "platform": "tiktok",
      "platform_handle": "khaby.lame",
      "status": "active",
      "scrape_cadence": "daily",
      "enrichment_status": "ready",
      "latest_followers": 162973075,
      "growth_rate": 0.00031009,
      "next_scrape_at": "2026-09-25T14:00:05.120+00:00"
    }
  ],
  "pagination": {
    "page": 1,
    "limit": 20,
    "total": 1,
    "total_pages": 1,
    "has_next_page": false,
    "has_prev_page": false
  }
}

GET/v1/tracking/creators/:id

Get tracked creator

One tracked creator: latest numbers, settings, and check progress.

Cost: Free.

Full field reference

Every field is in the example. Notes:

  • growth_rate: change since the last check, in total views on YouTube and followers elsewhere. It reads 1 after the first check.
  • followers_gained: equals all followers after the first check.
  • latest_total_likes: 0 on YouTube and Instagram.
  • latest_total_views: the channel total on YouTube. Elsewhere, the sum over stored posts.
  • profile_metadata: differs by platform. external_links is an object.
  • next_scrape_at: the check starts within about 5 minutes of this time.

Request

GET
/v1/tracking/creators/:id
curl https://api.virlo.ai/v1/tracking/creators/a1b2c3d4-e5f6-7890-abcd-ef1234567890 \
  -H "Authorization: Bearer YOUR_API_KEY"

Response

{
  "data": {
    "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "platform": "tiktok",
    "platform_handle": "khaby.lame",
    "profile_url": "https://www.tiktok.com/@khaby.lame",
    "display_name": "Khabane lame",
    "avatar_url": "7c2e9f4b1a8d3c6e5f0a9b8c7d6e5f4a3b2c1d0e9f8a7b6c5d4e3f2a1b0c9d8e.jpg",
    "bio": "If u wanna laugh u r in the right place",
    "is_verified": true,
    "status": "active",
    "scrape_cadence": "daily",
    "enrichment_status": "ready",
    "latest_followers": 162973075,
    "latest_following": 81,
    "latest_total_likes": 2681204805,
    "latest_total_views": 463241900,
    "latest_total_videos": 1355,
    "last_scraped_at": "2026-09-24T14:00:05.120+00:00",
    "growth_rate": 0.00031009,
    "followers_gained": 50521,
    "next_scrape_at": "2026-09-25T14:00:05.120+00:00",
    "category": "comedy",
    "content_tags": ["silent-comedy", "life-hacks", "reactions"],
    "profile_metadata": {
      "country": null,
      "website": null,
      "language": "en",
      "external_links": {}
    },
    "created_at": "2026-09-20T10:30:00.000+00:00",
    "updated_at": "2026-09-24T14:00:31.402+00:00",
    "pending_jobs": [],
    "finalized": true
  }
}

GET/v1/tracking/creators/:id/report

Get creator report

The latest AI report on the creator, rewritten each check: what works, breakout videos (viral_content), and how commenters react. report is null until the first check is done.

Cost: Free.

collection_data lists the videos the report used. popular_videos is TikTok only, and YouTube uses shorts.

Request

GET
/v1/tracking/creators/:id/report
curl https://api.virlo.ai/v1/tracking/creators/a1b2c3d4-e5f6-7890-abcd-ef1234567890/report \
  -H "Authorization: Bearer YOUR_API_KEY"

Response

{
  "data": {
    "account": {
      "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
      "platform": "tiktok",
      "platform_handle": "khaby.lame",
      "latest_followers": 162973075
    },
    "report": {
      "id": "c7d8e9f0-a1b2-4c3d-9e4f-5a6b7c8d9e0f",
      "tracking_account_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
      "analysis": {
        "overview": {
          "headline": "163M-follower comedy creator known for silent reactions...",
          "recent_focus": "Everyday mishaps and travel gags...",
          "content_focus": "Language-free comedy that works in any country...",
          "popular_focus": "Silent reactions to overcomplicated life hacks...",
          "growth_insight": "Follower growth is steady but slowing..."
        },
        "key_insight": "Wordless storytelling travels across languages...",
        "what_works": [
          {
            "insight": "Silent, non-verbal storytelling",
            "evidence": "His top videos contain no spoken words...",
            "evidence_video_ids": ["7678009073421405471"]
          }
        ],
        "discoveries": [
          { "text": "Posts at almost the same time of day...", "metric": "3 posts/week", "evidence_video_ids": [] }
        ],
        "viral_content": [
          {
            "title": "They were there yesterday, I promise #learnfromkhaby #comedy",
            "views": 101400000,
            "outlier_ratio": 8.52,
            "why_it_worked": "A relatable mix-up with a silent payoff...",
            "platform_video_id": "7678009073421405471"
          }
        ],
        "content_themes": [
          {
            "name": "Life hack reactions",
            "avg_views": 89400000,
            "description": "Watches an overcomplicated solution, then shows the simple one...",
            "video_count": 8,
            "evidence_video_ids": ["7678009073421405471"]
          }
        ],
        "posting_patterns": {
          "frequency": "2 to 3 videos per week...",
          "best_formats": ["Silent reaction to a life hack"],
          "optimal_duration": "13 to 18 seconds"
        },
        "subjects_covered": ["Life hack reactions", "Travel mishaps"],
        "audience_sentiment": {
          "overall_summary": "Overwhelmingly positive and global...",
          "sentiment_labels": ["highly_positive", "appreciative"],
          "notable_comments": [
            { "likes": 190974, "content": "Learn from Khaby", "insight": "Fans treat him as a teacher of common sense", "video_id": "7678009073421405471" }
          ],
          "key_findings": [
            { "finding": "Comments come in many languages...", "takeaway": "Keep videos wordless to reach every market..." }
          ]
        }
      },
      "collection_data": {
        "latest_videos": [
          {
            "url": "https://www.tiktok.com/@khaby.lame/video/7688400123809893662",
            "title": "Yeah, I'm never taking these glasses off again. #learnfromkhaby",
            "views": 1100000,
            "likes": 64000,
            "comments": 820,
            "shares": 1900,
            "bookmarks": 3100,
            "duration": null,
            "published_at": "2026-09-22T16:39:49.000Z",
            "thumbnail_url": "0174d2837f7635b2bed46a3bff5e03ec16fe26465600f3839f56b68e69911c2a.webp",
            "platform_video_id": "7688400123809893662"
          }
        ],
        "popular_videos": [
          {
            "url": "https://www.tiktok.com/@khaby.lame/video/7678009073421405471",
            "title": "They were there yesterday, I promise #learnfromkhaby #comedy",
            "views": 101400000,
            "likes": 6200000,
            "comments": 31000,
            "shares": 450000,
            "bookmarks": 120000,
            "duration": null,
            "published_at": "2026-08-25T16:36:58.000Z",
            "thumbnail_url": "6893372b005aa6d33df99bfd7c8093090c52ead2dc2a83c346972e8a749f47c1.webp",
            "platform_video_id": "7678009073421405471"
          }
        ],
        "shorts": [],
        "shorts_count": 0,
        "comments_collected": 40,
        "transcripts_collected": 0,
        "top_video_transcripts": {}
      },
      "created_at": "2026-09-24T14:00:31.402+00:00"
    }
  }
}

GET/v1/tracking/creators/:id/signals

Get creator signals

Alerts from the creator's checks, newest first.

Cost: Free.

typeRaised when
outlier_videoA video got over 3 times the median views. Once per video.
follower_spike, follower_declineFollowers moved at least 3% and 250 followers since the last check (critical at 10% or more). payload.pct is a percent.
new_subjectThe new report covers new topics.
creator_unreachable3 failed attempts paused tracking.

weighted_score ranks breakouts: it rises with both outlier_ratio and median views. It's not the agent pages' Virality Score.

read is always false. To spot new alerts, keep the newest created_at you've seen.

Query parameters

  • Name
    limit
    Type
    integer
    Description

    Default 50, at most 100. There's no paging or date filter.

Request

GET
/v1/tracking/creators/:id/signals
curl "https://api.virlo.ai/v1/tracking/creators/a1b2c3d4-e5f6-7890-abcd-ef1234567890/signals?limit=20" \
  -H "Authorization: Bearer YOUR_API_KEY"

Response

{
  "data": [
    {
      "id": "7d8e9f0a-1b2c-4d3e-8f4a-5b6c7d8e9f0a",
      "type": "outlier_video",
      "severity": "notable",
      "title": "Outlier video: They were there yesterday, I promise #learnfromkhaby #comedy",
      "payload": {
        "url": "https://www.tiktok.com/@khaby.lame/video/7678009073421405471",
        "title": "They were there yesterday, I promise #learnfromkhaby #comedy",
        "views": 101400000,
        "platform": "tiktok",
        "median_views": 11900000,
        "outlier_ratio": 8.52,
        "weighted_score": 34.91,
        "platform_handle": "khaby.lame",
        "platform_video_id": "7678009073421405471"
      },
      "created_at": "2026-08-26T14:01:12.431+00:00",
      "read": false
    }
  ]
}

GET/v1/tracking/creators/:id/snapshots

Get creator snapshots

The creator's numbers at each check, one row per check.

Cost: Free.

You get the most recent limit checks in your date range, listed oldest first. There's no paging.

  • delta_* compares with the check before it. The first row's are null only when there's no earlier check.
  • engagement_rate isn't the usual metric: it's total_likes ÷ total_views (null if views are 0). On TikTok that's profile likes over about 20 stored posts' views, so values like 5.79 are common. Use it only as one creator's trend. It's 0 on YouTube and Instagram.
  • subscribers repeats followers on YouTube and is null elsewhere.

Query parameters

  • Name
    start_date
    Type
    string
    Description

    ISO 8601, such as 2026-09-01.

  • Name
    end_date
    Type
    string
    Description

    A date alone means the start of that day, so that day is left out. Add T23:59:59Z to include it.

  • Name
    limit
    Type
    integer
    Description

    How many of the most recent checks to return, 1 to 365. Default 30.

Request

GET
/v1/tracking/creators/:id/snapshots
curl -G https://api.virlo.ai/v1/tracking/creators/a1b2c3d4-e5f6-7890-abcd-ef1234567890/snapshots \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -d start_date=2026-09-17T00:00:00Z

Response

{
  "data": [
    {
      "id": "1b2c3d4e-5f6a-4b7c-9d8e-0f1a2b3c4d5e",
      "tracking_account_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
      "followers": 162973075,
      "following": 81,
      "subscribers": null,
      "total_videos": 1355,
      "total_views": 463241900,
      "total_likes": 2681204805,
      "collected_post_count": 20,
      "snapshot_at": "2026-09-24T14:00:05.120+00:00",
      "engagement_rate": 5.787915,
      "delta_followers": 50521,
      "delta_following": 0,
      "delta_total_videos": 1,
      "delta_total_views": 1253800,
      "delta_total_likes": 1254805,
      "delta_engagement_rate": -0.012992
    }
  ]
}

PATCH/v1/tracking/creators/:id

Update tracked creator

Pause, resume, or change the cadence. Send only what changes. The response is the full creator.

Cost: Free, but resuming triggers a $0.25 check.

  • Resuming (status: "active") runs that check now, or within an hour if the last one was under an hour ago.
  • A new scrape_cadence sets the next check one full interval from now. Send it with status to resume without an immediate check.

Request body

The handle can't change: stop tracking and track the new one.

  • Name
    status
    Type
    string
    Description

    active or paused.

  • Name
    scrape_cadence
    Type
    string
    Description

    As in Track a creator.

Request

PATCH
/v1/tracking/creators/:id
curl -X PATCH https://api.virlo.ai/v1/tracking/creators/a1b2c3d4-e5f6-7890-abcd-ef1234567890 \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "scrape_cadence": "weekly" }'

Response

{
  "data": {
    "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "platform": "tiktok",
    "platform_handle": "khaby.lame",
    "status": "active",
    "scrape_cadence": "weekly",
    "next_scrape_at": "2026-10-01T16:20:41.918+00:00"
  }
}

DELETE/v1/tracking/creators/:id

Stop tracking creator

Stops checks and charges, and hides the creator from your list. To take a break, pause instead.

Cost: Free.

  • Everything stays readable by id, with status: "deleted".
  • To come back, track the handle again (same id and history, $0.25), or update the id to paused or active. If the last check was under an hour ago, the new one can take up to an hour to start.
  • A second DELETE also returns 204.

Request

DELETE
/v1/tracking/creators/:id
curl -X DELETE https://api.virlo.ai/v1/tracking/creators/a1b2c3d4-e5f6-7890-abcd-ef1234567890 \
  -H "Authorization: Bearer YOUR_API_KEY"

Response

(empty response body)

GET/v1/tracking/creators/:id/posts

List creator posts

The creator's stored videos, with their stats.

Cost: Free.

Not the full history. Each check stores TikTok's 10 or so newest and 10 most popular videos, up to Instagram's 90 newest, or YouTube's Shorts only. For older videos, collect posts with deep or full.

Query parameters

  • Name
    sort
    Type
    string
    Description

    publish_date_desc (default), publish_date_asc, or views_desc.

  • Name
    start_date
    Type
    string
    Description

    Published on or after. ISO 8601.

  • Name
    end_date
    Type
    string
    Description

    Published on or before.

  • Name
    page
    Type
    integer
    Description

    Default 1.

  • Name
    limit
    Type
    integer
    Description

    1 to 200. Default 50.

Full field reference

Every field is in the example. Notes:

  • is_outlier: over 3 times the median views on the check that stored it. Otherwise outlier_ratio and outlier_weighted_score are null.
  • shares and bookmarks: TikTok only, 0 elsewhere.
  • duration_seconds: null on TikTok.
  • sound: external_id is the platform's sound ID. YouTube posts get a sound only from deep or full collections. usage_count and owner_handle are often 0 or null.

Request

GET
/v1/tracking/creators/:id/posts
curl -G "https://api.virlo.ai/v1/tracking/creators/a1b2c3d4-e5f6-7890-abcd-ef1234567890/posts" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -d limit=20 \
  -d sort=views_desc

Response

{
  "data": [
    {
      "id": "c3d4e5f6-a7b8-4901-8def-123456789012",
      "tracking_account_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
      "platform": "tiktok",
      "platform_video_id": "7678009073421405471",
      "url": "https://www.tiktok.com/@khaby.lame/video/7678009073421405471",
      "title": "They were there yesterday, I promise #learnfromkhaby #comedy",
      "description": "They were there yesterday, I promise #learnfromkhaby #comedy",
      "thumbnail_url": "6893372b005aa6d33df99bfd7c8093090c52ead2dc2a83c346972e8a749f47c1.webp",
      "publish_date": "2026-08-25T16:36:58+00:00",
      "views": 101400000,
      "likes": 6200000,
      "comments": 31000,
      "shares": 450000,
      "bookmarks": 120000,
      "is_duet": false,
      "is_stitch": false,
      "duration_seconds": null,
      "hashtags": ["learnfromkhaby", "comedy"],
      "collected_at": "2026-09-24T14:00:21.469+00:00",
      "is_outlier": true,
      "outlier_ratio": 8.52,
      "outlier_weighted_score": 34.91,
      "sound": {
        "id": "550e8400-e29b-41d4-a716-446655440000",
        "title": "original sound - khaby.lame",
        "duration": 15,
        "platform": "tiktok",
        "cover_url": "abb4e3bcf8d355936aa54a444b83930591647f6c412843f9fa40dbb71478ce9a.jpg",
        "external_id": "7559312683885201425",
        "is_original": true,
        "usage_count": 0,
        "owner_handle": null,
        "owner_nickname": "Khabane lame",
        "is_commerce_music": false
      }
    }
  ],
  "pagination": {
    "page": 1,
    "limit": 20,
    "total": 20,
    "total_pages": 1,
    "has_next_page": false,
    "has_prev_page": false
  }
}

GET/v1/tracking/creators/:id/posts/:post_id

Get creator post

One stored post, with the same fields as the list.

Cost: Free.

Path parameters

  • Name
    post_id
    Type
    string
    Required
    *
    Description

    The post's id from List creator posts. A platform video ID returns 404.

Request

GET
/v1/tracking/creators/:id/posts/:post_id
curl "https://api.virlo.ai/v1/tracking/creators/a1b2c3d4-e5f6-7890-abcd-ef1234567890/posts/c3d4e5f6-a7b8-4901-8def-123456789012" \
  -H "Authorization: Bearer YOUR_API_KEY"

Response

{
  "data": {
    "id": "c3d4e5f6-a7b8-4901-8def-123456789012",
    "platform_video_id": "7678009073421405471",
    "url": "https://www.tiktok.com/@khaby.lame/video/7678009073421405471",
    "views": 101400000,
    "is_outlier": true,
    "outlier_ratio": 8.52,
    "outlier_weighted_score": 34.91
  }
}

POST/v1/tracking/creators/:id/posts/collect

Collect creator posts

Stores a creator's older videos once, for more history than checks keep.

Cost: set by depth, charged when it starts. Refunded in full if it finds no posts or its last attempt fails.

depthGetsCost
standard (default)Up to 50 videos$0.50
deepUp to 200 videos$1.00
fullUp to 500 videos$2.00

How long: seconds for standard, longer for deep and full. Poll Get collection status.

  • Every depth pages back to its target. On TikTok it also adds the most popular videos: one page at standard, up to 100 at deep and full.
  • Wait for a successful check first (enrichment_status: "ready"). If the last check failed, you get a 409 and no charge.
  • One collection at a time. Another returns a 409.

Request body

  • Name
    depth
    Type
    string
    Description

    standard, deep, or full.

  • Name
    force
    Type
    boolean
    Description

    true skips the failed-check 409. A forced collection that finds nothing is still refunded.

Request

POST
/v1/tracking/creators/:id/posts/collect
curl -X POST "https://api.virlo.ai/v1/tracking/creators/a1b2c3d4-e5f6-7890-abcd-ef1234567890/posts/collect" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "depth": "deep" }'

Response

{
  "data": {
    "collection_id": "e5f6a7b8-c9d0-4e1f-8a2b-3c4d5e6f7a8b",
    "status": "processing",
    "depth": "deep",
    "max_videos": 200,
    "credits_reserved": 100
  }
}

credits_reserved is the charge in credits (100 = $1.00).


GET/v1/tracking/creators/:id/posts/collect/:collection_id

Get collection status

Checks on a post collection. status is processing, completed, or failed (with an error). videos_collected includes videos Virlo already had. If a collection found no posts or failed, credits_refunded shows what was paid back (in credits: 50 = $0.50). It appears only when there was a refund. The status is kept for 24 hours, then returns 404.

Cost: Free.

Request

GET
/v1/tracking/creators/:id/posts/collect/:collection_id
curl "https://api.virlo.ai/v1/tracking/creators/a1b2c3d4-e5f6-7890-abcd-ef1234567890/posts/collect/e5f6a7b8-c9d0-4e1f-8a2b-3c4d5e6f7a8b" \
  -H "Authorization: Bearer YOUR_API_KEY"

Response

{
  "data": {
    "collection_id": "e5f6a7b8-c9d0-4e1f-8a2b-3c4d5e6f7a8b",
    "status": "completed",
    "depth": "deep",
    "videos_collected": 200,
    "max_videos": 200,
    "started_at": "2026-09-24T16:02:10.114Z",
    "completed_at": "2026-09-24T16:03:21.580Z"
  }
}

GET/v1/tracking/creators/:id/posting-cadence

Get posting cadence

How often a creator posts, counted from stored posts only.

Cost: Free.

TikTok checks mix in old hit videos, so a daily poster can show under 1 post a week, and YouTube counts Shorts only. A deep or full collection helps, but the real rate is usually higher.

Weekdays run from "0" (Sunday) to "6", in UTC. Months are 30 days. With no posts, the rates are null.

Request

GET
/v1/tracking/creators/:id/posting-cadence
curl "https://api.virlo.ai/v1/tracking/creators/a1b2c3d4-e5f6-7890-abcd-ef1234567890/posting-cadence" \
  -H "Authorization: Bearer YOUR_API_KEY"

Response

{
  "data": {
    "total_posts": 200,
    "avg_days_between_posts": 2.71,
    "posts_per_week": 2.6,
    "posts_per_month": 11.13,
    "day_of_week_distribution": {
      "0": 24, "1": 31, "2": 30, "3": 29,
      "4": 28, "5": 30, "6": 28
    },
    "earliest_post_date": "2025-04-02T16:30:00.000Z",
    "latest_post_date": "2026-09-23T17:40:45.000Z"
  }
}

Audience data

Find out who engages with a tracked creator: age, gender, language, country, and city. Virlo builds an audience snapshot from the people who comment on recent videos.

  • You ask for it with Refresh audience snapshot. Tracking never makes one, and the creator needs a successful check first.
  • Cost: $0.50 per new snapshot, charged when it starts. Refunded automatically if the job fails or only has the creator's profile. Reading is free.
  • Demographics and geography read the same snapshot.
data_sourceMeaning
commentsCommenters. The usual case.
mixed or followersTikTok, when commenters are few: adds followers, or uses only followers.
comments_extendedInstagram and YouTube: commenters from more posts.
profile_onlyA guess from the profile. Always low confidence, and refunded.

confidence_level is high (usually 200 or more people in sample_size), medium (100 or more), or low. confidence_per_signal scores each field from 0 to 1.


POST/v1/tracking/creators/:id/audience-refresh

Refresh audience snapshot

Gets audience data. A snapshot younger than freshness_days comes back free (source: "cache"). Otherwise Virlo starts a new one (source: "fresh") and returns a job_id. Both return 202.

Cost: $0.50 for a new snapshot. Reusing one is free, but needs a $0.50 balance.

How long: 5 to 12 minutes. Poll the job, or wait for the audience.snapshot.completed webhook.

A 409 means no check has succeeded yet, or the last one failed. Wrong handle? Stop tracking and track the right one. PATCH can't fix it, whatever the hint says.

Request body

  • Name
    freshness_days
    Type
    integer
    Description

    0 to 365. Default 30. 0 always makes a new one.

  • Name
    force
    Type
    boolean
    Description

    true ignores saved snapshots and skips the 409. $0.50 each time, unless a job is already running.

Request

POST
/v1/tracking/creators/:id/audience-refresh
curl -X POST "https://api.virlo.ai/v1/tracking/creators/a1b2c3d4-e5f6-7890-abcd-ef1234567890/audience-refresh" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "freshness_days": 30 }'

Response

{
  "data": {
    "source": "fresh",
    "snapshot": null,
    "job_id": "9f8e7d6c-5b4a-4392-8a1b-0c9d8e7f6a5b",
    "status": "processing",
    "credits_used": 50,
    "creator_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "credit_unit": "cent",
    "pending_jobs": [
      {
        "type": "audience_demographics",
        "status": "processing",
        "job_id": "9f8e7d6c-5b4a-4392-8a1b-0c9d8e7f6a5b",
        "poll_url": "/v1/audience/snapshot/9f8e7d6c-5b4a-4392-8a1b-0c9d8e7f6a5b",
        "result_path": "data.snapshot",
        "webhook_event": "audience.snapshot.completed",
        "retry_after_seconds": 15
      }
    ],
    "finalized": false
  }
}

credits_used is in credits (50 = $0.50).


GET/v1/tracking/creators/:id/audience-refresh/:jobId

Check audience refresh status

Checks on an audience job. status is processing, completed (with the snapshot), or failed.

Cost: Free.

  • A failure has error.code and error.message (INSUFFICIENT_SAMPLE means too few people), and is refunded.
  • The job is kept for 24 hours, then returns 404. The snapshot stays readable.
  • pending_jobs[].poll_url works too. This route has friendlier field names.

Request

GET
/v1/tracking/creators/:id/audience-refresh/:jobId
curl "https://api.virlo.ai/v1/tracking/creators/a1b2c3d4-e5f6-7890-abcd-ef1234567890/audience-refresh/9f8e7d6c-5b4a-4392-8a1b-0c9d8e7f6a5b" \
  -H "Authorization: Bearer YOUR_API_KEY"

Response

{
  "data": {
    "job_id": "9f8e7d6c-5b4a-4392-8a1b-0c9d8e7f6a5b",
    "status": "completed",
    "snapshot": {
      "snapshot_id": "f5a3b2c1-d4e5-4f6a-8b7c-9d0e1f2a3b4c",
      "snapshot_at": "2026-09-24T16:27:09.261+00:00",
      "sample_size": 500,
      "confidence_level": "high",
      "data_source": "comments",
      "model_version": "v1"
    },
    "error": null,
    "pending_jobs": [],
    "finalized": true
  }
}

GET/v1/tracking/creators/:id/audience-demographics

Get audience demographics

Age, gender, and language from the latest audience snapshot. Reading never starts a new one.

Cost: Free.

  • You always get the latest snapshot, however old. freshness_days only sets is_stale: true when it's older. Without it, is_stale is false.
  • snapshot is null if there's none. If the first is still running, pending_jobs[0] has its job_id.
  • Language un means unknown.

Query parameters

  • Name
    freshness_days
    Type
    integer
    Description

    0 to 365. Only sets is_stale.

Request

GET
/v1/tracking/creators/:id/audience-demographics
curl "https://api.virlo.ai/v1/tracking/creators/a1b2c3d4-e5f6-7890-abcd-ef1234567890/audience-demographics?freshness_days=30" \
  -H "Authorization: Bearer YOUR_API_KEY"

Response

{
  "data": {
    "platform": "tiktok",
    "handle": "khaby.lame",
    "snapshot": {
      "snapshot_id": "f5a3b2c1-d4e5-4f6a-8b7c-9d0e1f2a3b4c",
      "snapshot_at": "2026-09-24T16:27:09.261+00:00",
      "sample_size": 500,
      "age_distribution": { "13-17": 0.14, "18-24": 0.46, "25-34": 0.32, "35-44": 0.06, "45+": 0.02 },
      "gender_distribution": { "male": 0.45, "female": 0.55 },
      "language_distribution": { "en": 0.62, "it": 0.18, "pt": 0.09, "es": 0.07, "un": 0.04 },
      "country_distribution": null,
      "city_distribution": null,
      "confidence_per_signal": { "age": 0.72, "city": 0.69, "gender": 0.84, "country": 0.79, "language": 0.92 },
      "confidence_level": "high",
      "data_source": "comments",
      "signal_breakdown": { "comments": 500, "followers": 0 },
      "evidence_summary": "Analysis of 500 commenters across 30 recent posts shows...",
      "model_version": "v1"
    },
    "is_stale": false,
    "pending_jobs": [],
    "finalized": true
  }
}

GET/v1/tracking/creators/:id/audience-geography

Get audience geography

Countries and cities, from the same snapshot as demographics and with the same rules.

Cost: Free.

  • A country's name repeats its code, such as US. Small countries roll up into Other (N countries).
  • A city's name is the city. city_distribution can be null.

Query parameters

  • Name
    freshness_days
    Type
    integer
    Description

    0 to 365. Only sets is_stale.

Request

GET
/v1/tracking/creators/:id/audience-geography
curl "https://api.virlo.ai/v1/tracking/creators/a1b2c3d4-e5f6-7890-abcd-ef1234567890/audience-geography" \
  -H "Authorization: Bearer YOUR_API_KEY"

Response

{
  "data": {
    "platform": "tiktok",
    "handle": "khaby.lame",
    "snapshot": {
      "snapshot_id": "f5a3b2c1-d4e5-4f6a-8b7c-9d0e1f2a3b4c",
      "snapshot_at": "2026-09-24T16:27:09.261+00:00",
      "sample_size": 500,
      "age_distribution": null,
      "gender_distribution": null,
      "language_distribution": null,
      "country_distribution": [
        { "pct": 0.22, "code": "US", "name": "US", "confidence": 0.81 },
        { "pct": 0.19, "code": "IT", "name": "IT", "confidence": 0.84 },
        { "name": "Other (14 countries)", "pct": 0.59 }
      ],
      "city_distribution": [
        { "pct": 0.08, "code": "IT", "name": "Milan", "confidence": 0.71 }
      ],
      "confidence_level": "high",
      "data_source": "comments",
      "model_version": "v1"
    },
    "is_stale": false,
    "pending_jobs": [],
    "finalized": true
  }
}

POST/v1/tracking/videos

Track a video

Starts tracking one video and runs the first check right away.

Cost: $0.25. The link isn't checked first. If the video can't be read, you're refunded about 40 seconds later, and it stays paused with enrichment_status: "failed".

How long: usually under a minute. Poll Get tracked video.

Videos follow the same once-per-account rule, with video_id in the 409. A video you stopped returns 409 too. 400 Failed to create tracked video means the tracking_account_id isn't a creator you track, or you sent two YouTube /shorts/ links seconds apart.

Request body

  • Name
    url
    Type
    string
    Required
    *
    Description

    TikTok: https://www.tiktok.com/@user/video/ID. YouTube: https://www.youtube.com/watch?v=ID or https://youtu.be/ID (best), or /shorts/ID. Instagram: https://www.instagram.com/reel/CODE/ or /p/CODE/.

  • Name
    platform
    Type
    string
    Required
    *
    Description

    tiktok, youtube, or instagram.

  • Name
    scrape_cadence
    Type
    string
    Description

    As in Track a creator.

  • Name
    tracking_account_id
    Type
    string
    Description

    A tracked creator's id, if it's their video, so the report can weigh views against their followers. Can't change later.

Request

POST
/v1/tracking/videos
curl -X POST https://api.virlo.ai/v1/tracking/videos \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://www.tiktok.com/@khaby.lame/video/7678009073421405471",
    "platform": "tiktok",
    "scrape_cadence": "daily"
  }'

Response

{
  "data": {
    "id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
    "status": "active",
    "message": "Video tracking started. Initial metrics and AI report are being generated."
  }
}

GET/v1/tracking/videos

List tracked videos

Works like List tracked creators, with the same platform, page, and limit. Each item includes the whole AI report, so items are large.

Cost: Free.

Query parameters

  • Name
    search
    Type
    string
    Description

    Part of the video's title or link.

Request

GET
/v1/tracking/videos
curl -G https://api.virlo.ai/v1/tracking/videos \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -d limit=20

Response

{
  "data": [
    {
      "id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
      "platform": "tiktok",
      "url": "https://www.tiktok.com/@khaby.lame/video/7678009073421405471",
      "status": "active",
      "enrichment_status": "ready",
      "latest_views": 101400000,
      "growth_rate": 0.0091,
      "next_scrape_at": "2026-09-25T14:00:04.723+00:00"
    }
  ],
  "pagination": {
    "page": 1,
    "limit": 20,
    "total": 1,
    "total_pages": 1,
    "has_next_page": false,
    "has_prev_page": false
  }
}

GET/v1/tracking/videos/:id

Get tracked video

One tracked video: numbers, settings, transcript, and latest AI report.

Cost: Free.

Full field reference

Every field is in the example, and there's no updated_at. Notes:

  • growth_rate: view change since the last check. 0.009 means up 0.9% per check, not per day. It reads 0 after the first check.
  • latest_shares and latest_bookmarks: TikTok only, 0 elsewhere.
  • duration: often null on YouTube.
  • platform_video_id: empty for a YouTube /shorts/ link until the first check.
  • analysis: the report, null until the first check.

Request

GET
/v1/tracking/videos/:id
curl https://api.virlo.ai/v1/tracking/videos/b2c3d4e5-f6a7-8901-bcde-f12345678901 \
  -H "Authorization: Bearer YOUR_API_KEY"

Response

{
  "data": {
    "id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
    "platform": "tiktok",
    "platform_video_id": "7678009073421405471",
    "url": "https://www.tiktok.com/@khaby.lame/video/7678009073421405471",
    "title": "They were there yesterday, I promise #learnfromkhaby #comedy",
    "description": "They were there yesterday, I promise #learnfromkhaby #comedy",
    "thumbnail_url": "https://p16-common-sign.tiktokcdn-us.com/...",
    "duration": 21,
    "published_at": "2026-08-25T16:36:58+00:00",
    "author_handle": "khaby.lame",
    "author_name": "Khabane lame",
    "author_avatar_url": "https://p19-common-sign.tiktokcdn-us.com/...",
    "status": "active",
    "scrape_cadence": "daily",
    "enrichment_status": "ready",
    "latest_views": 101400000,
    "latest_likes": 6200000,
    "latest_comments": 31000,
    "latest_shares": 450000,
    "latest_bookmarks": 120000,
    "latest_transcript": null,
    "growth_rate": 0.0091,
    "analysis": {
      "key_insight": "A relatable mix-up with a silent payoff..."
    },
    "analysis_updated_at": "2026-09-24T14:00:31.026+00:00",
    "tracking_account_id": null,
    "last_scraped_at": "2026-09-24T14:00:04.723+00:00",
    "next_scrape_at": "2026-09-25T14:00:04.723+00:00",
    "created_at": "2026-09-20T10:30:00.000+00:00"
  }
}

GET/v1/tracking/videos/:id/report

Get video report

The latest AI report on the video: how it's doing, why it works, the hook, and how commenters react. analysis is null until the first check.

Cost: Free.

video is the full tracked video, analysis included. overview.engagement_rate is a percent: 7.06 means 7.06%.

Request

GET
/v1/tracking/videos/:id/report
curl https://api.virlo.ai/v1/tracking/videos/b2c3d4e5-f6a7-8901-bcde-f12345678901/report \
  -H "Authorization: Bearer YOUR_API_KEY"

Response

{
  "data": {
    "video": {
      "id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
      "platform": "tiktok",
      "url": "https://www.tiktok.com/@khaby.lame/video/7678009073421405471",
      "latest_views": 101400000
    },
    "analysis": {
      "overview": {
        "summary": "A silent comedy bit about a missing object...",
        "outlier_ratio": null,
        "growth_insight": "Views are still climbing a month after posting...",
        "engagement_rate": 7.06,
        "growth_trajectory": "steady",
        "performance_verdict": "overperforming"
      },
      "key_insight": "A relatable mix-up with a silent payoff...",
      "what_works": [
        { "insight": "The punchline lands without a single word", "evidence": "Top comments come in many languages..." }
      ],
      "discoveries": [
        { "text": "Saves are unusually high for a comedy clip...", "metric": "1.2% save rate" }
      ],
      "hook_analysis": {
        "hook_type": "Pattern interrupt",
        "description": "Opens mid-problem, so viewers stay to see the fix...",
        "effectiveness": "strong"
      },
      "content_breakdown": {
        "topics": ["Everyday mishaps"],
        "format_style": "Silent reaction with one visual punchline...",
        "target_audience": "A global audience of all ages..."
      },
      "audience_sentiment": {
        "overall_summary": "Warm and amused...",
        "sentiment_labels": ["appreciative", "amused"],
        "notable_comments": [
          { "likes": 52000, "content": "This is literally me every morning", "insight": "Viewers see themselves in the joke" }
        ],
        "key_findings": [
          { "finding": "Viewers tag friends who do the same thing...", "takeaway": "Relatable mistakes drive shares..." }
        ]
      }
    },
    "analysis_updated_at": "2026-09-24T14:00:31.026+00:00"
  }
}

GET/v1/tracking/videos/:id/snapshots

Get video snapshots

The video's views, likes, comments, shares, and saves at each check.

Cost: Free.

Works like creator snapshots, with the same start_date, end_date, and limit: the most recent checks, listed oldest first.

shares and bookmarks are 0 on YouTube and Instagram.

Request

GET
/v1/tracking/videos/:id/snapshots
curl -G https://api.virlo.ai/v1/tracking/videos/b2c3d4e5-f6a7-8901-bcde-f12345678901/snapshots \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -d start_date=2026-09-17T00:00:00Z

Response

{
  "data": [
    {
      "id": "8734c9e6-efce-4c49-8262-2e20ac8f204f",
      "tracking_video_id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
      "views": 101400000,
      "likes": 6200000,
      "comments": 31000,
      "shares": 450000,
      "bookmarks": 120000,
      "snapshot_at": "2026-09-24T14:00:04.723+00:00",
      "delta_views": 920000,
      "delta_likes": 20000,
      "delta_comments": 200,
      "delta_shares": 2000,
      "delta_bookmarks": 1000
    }
  ]
}

PATCH/v1/tracking/videos/:id

Update tracked video

Works like Update tracked creator, with the same status and scrape_cadence fields and the same resume rules. The link and tracking_account_id can't change. The response is the full video.

Cost: Free, but resuming triggers a $0.25 check.

Request

PATCH
/v1/tracking/videos/:id
curl -X PATCH https://api.virlo.ai/v1/tracking/videos/b2c3d4e5-f6a7-8901-bcde-f12345678901 \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "status": "paused" }'

Response

{
  "data": {
    "id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
    "platform": "tiktok",
    "url": "https://www.tiktok.com/@khaby.lame/video/7678009073421405471",
    "status": "paused",
    "scrape_cadence": "daily",
    "next_scrape_at": "2026-09-25T14:00:04.723+00:00"
  }
}

DELETE/v1/tracking/videos/:id

Stop tracking video

Stops checks and charges, and hides the video from your list. Its data stays readable by id, with status: "deleted". A second DELETE also returns 204.

Cost: Free.

Request

DELETE
/v1/tracking/videos/:id
curl -X DELETE https://api.virlo.ai/v1/tracking/videos/b2c3d4e5-f6a7-8901-bcde-f12345678901 \
  -H "Authorization: Bearer YOUR_API_KEY"

Response

(empty response body)

Webhook notifications

To get a message on your server instead of polling, subscribe with the Webhooks API (POST /v1/webhooks). Only items tracked through the API send these.

EventSent when
tracking.cycle.completedA check finished, and its report is ready.
tracking.outlier_video.detectedA creator has a new breakout video.
tracking.paused3 attempts failed, or your balance can't cover a check.
audience.snapshot.completedAn audience snapshot is ready. Not sent on failure, so also check the job.

After a low-balance pause, add funds, then set status to active. If Virlo couldn't reach the creator or video, the handle or link can't be edited: stop tracking and track the right one. Message fields: Tracking updates.


Workflow recipes

Vet a creator before a paid deal

  1. Track the creator and wait for ready: $0.25.
  2. Refresh the audience snapshot: $0.50.
  3. Read the report, demographics, and geography, free.
  4. Stop tracking so no more checks are charged.

Total: about $0.75.


Errors

Errors are never charged. For the full list, see Errors.

StatusUsually means
400A missing, misspelled, or unknown field, or a value like TikTok (use lowercase).
402Your balance is too low. Read code and required_credits.
404No item with that ID for your team. Use Virlo's id, not the platform's video ID.
409Already tracked on your account, a post collection is already running, or a post collection or audience refresh is blocked by a failed check.

Was this page helpful?