---
title: "Poll creator lookup status"
method: GET
path: "/v1/satellite/creator/status/{job_id}"
tags: ["Satellite"]
---

# Poll creator lookup status

`GET /v1/satellite/creator/status/{job_id}`

## Request

- Base URL: `https://api.virlo.ai`
- URL: `https://api.virlo.ai/v1/satellite/creator/status/{job_id}`
- Auth: HTTP bearer

Returns the current status of a creator lookup job. Poll until status is "completed" or "failed". Results expire after 24 hours.

## Path parameters

- `job_id` string, required

## Response `200`

Job status retrieved. Shape varies by status: processing, completed (with result), or failed (with error).

- CreatorStatusResponseDto
  - `data` union, required: Job state — shape varies by status field (processing | completed | failed)
    - CreatorStatusProcessingDto
      - `status` string, required
      - `finalized` boolean: Always false while the main lookup is still processing. Included for shape uniformity with the completed variant.
      - `pending_jobs` PendingJobDto[]: Always an empty array while the main lookup is processing.
        - `type` 'viral_analysis' | 'audience_demographics' | 'audience_geography' | 'tracking_report', required: Stable identifier for the kind of secondary job. Use this to switch on the field where the result will appear (see `result_path`).
        - `status` 'pending' | 'processing', required: Current non-terminal state of the job. Once the job reaches a terminal state (ready / failed / skipped) the entry is removed from `pending_jobs[]`.
        - `job_id` string: Optional opaque identifier for the underlying job, when one exists. Useful with `poll_url` to fetch the result directly.
        - `poll_url` string: Path (relative to api.virlo.ai) you can GET to fetch the result of this job once it is ready. Free to call.
        - `result_path` string: Dot-path inside the parent resource's response body where the completed result will appear. Useful for clients that re-fetch the resource instead of using `poll_url`.
        - `webhook_event` string: Name of an event from the existing `SUPPORTED_EVENTS` list that fires when this job reaches a terminal state. Subscribe to it via POST /v1/webhooks to avoid polling. Guaranteed to be one of the already-supported event names so existing webhook subscriptions keep working — no new event types were introduced.
        - `started_at` string: When the job entered its current state (ISO 8601 UTC).
        - `retry_after_seconds` number: Suggested minimum delay (seconds) before polling again. Respect this to avoid hitting rate limits.
    - CreatorStatusCompletedDto
      - `status` string, required
      - `result` CreatorLookupResultDto, required
        - `username` string, required
        - `platform` 'youtube' | 'tiktok' | 'instagram', required
        - `profile` CreatorProfileDto
          - `followers` number, required
          - `following` number, required
          - `avatar_url` string, nullable
          - `url` string, required
          - `description` string, nullable
          - `is_verified` boolean, nullable
          - `total_videos` number, nullable
          - `total_likes` number, nullable
          - `total_views` number, nullable
          - `total_posts` number, nullable
          - `category` string, nullable: Content category slug from the Virlo category registry (e.g. "comedy", "fitness", "basketball"). AI-classified.
        - `stats` CreatorStatsDto, required
          - `videos_analyzed` number, required
          - `avg_views` number, required
          - `median_views` number, required
          - `avg_likes` number, required
          - `avg_comments` number, required
          - `engagement_rate` number, required
          - `top_performing_views` number, required
          - `lowest_performing_views` number, required
          - `posting_frequency_days` number: Only present when >= 2 videos have dates
        - `hashtags` CreatorHashtagDto[], required
          - `hashtag` string, required
          - `total_views` number, required
          - `avg_views` number, required
          - `avg_likes` number, required
          - `avg_comments` number, required
          - `used_count` number, required
        - `content_tags` string[]: AI-generated content tags describing the creator's specific niche (e.g. ["asmr", "mukbang", "budget-meals"])
        - `videos` CreatorVideoDto[]: Only present when include=videos
          - `id` string, required
          - `title` string, required
          - `description` string, required
          - `url` string, required
          - `views` number, required
          - `likes` number, required
          - `comments` number, required
          - `shares` number, nullable
          - `bookmarks` number, nullable
          - `is_duet` boolean, required: Whether this video is a TikTok duet. Always false for YouTube/Instagram.
          - `is_stitch` boolean, required: Whether this video is a TikTok stitch. Always false for YouTube/Instagram.
          - `publish_date` string, nullable: ISO publish timestamp.
          - `publishDate` string, nullable: Deprecated camelCase alias for `publish_date`. Removed Aug 3, 2026.
          - `hashtags` string[], required
          - `thumbnail_url` string, nullable: Video thumbnail URL. Null when the platform did not supply one.
          - `duration` number, nullable: Video length in seconds. Null when unavailable.
          - `is_pinned` boolean: Whether the video is pinned to the top of the creator profile. TikTok only; absent/false elsewhere.
          - `isPinned` boolean: Deprecated camelCase alias for `is_pinned`. Removed Aug 3, 2026.
          - `sound` V1SoundDto
            - `id` string, required: Virlo's internal sound ID (UUID). Use this for the /v1/sounds/:sound_id family (details, videos, usage-history, resolve). For the satellite sound lookup prefer `external_id`, though passing this UUID also works — it is auto-resolved to the sound's platform + external_id.
            - `external_id` string, nullable: Platform-native sound identifier (TikTok music/clip id, or Instagram audio id). This is the `music_id` accepted by the satellite sound lookup — call GET /v1/satellite/sounds/{platform}/{external_id} for TikTok or Instagram sounds. YouTube sounds are not supported (you'll get a 400). Null for sounds without a platform id.
            - `title` string, required
            - `platform` 'tiktok' | 'youtube' | 'instagram', required
            - `duration` number, nullable: Duration in seconds. Available on TikTok only.
            - `cover_url` string, nullable: Sound cover image URL. Available on TikTok and YouTube. Always an absolute URL (or null). Images Virlo stores are served from https://auth.virlo.ai/storage/v1/object/public/<bucket>/; platform CDN links are passed through. Some files are HEIC, which most non-Safari browsers cannot render, so convert before display if you need to.
            - `owner_handle` string, nullable: Sound owner handle. Partially available on TikTok (~12%).
            - `owner_nickname` string, nullable: Sound owner display name. Available on TikTok, YouTube, Instagram.
            - `is_original` boolean, nullable: Whether the sound is an original creation. TikTok only.
            - `is_commerce_music` boolean, nullable: Whether the sound is cleared for commercial use. TikTok only.
            - `usage_count` number, nullable: Platform-wide usage count. TikTok only.
        - `outliers` CreatorOutliersDto
          - `threshold_multiplier` number, required
          - `outlier_videos` CreatorOutlierVideoDto[], required
            - `id` string, required
            - `title` string, required
            - `description` string, required
            - `url` string, required
            - `views` number, required
            - `likes` number, required
            - `comments` number, required
            - `shares` number, nullable
            - `bookmarks` number, nullable
            - `is_duet` boolean, required: Whether this video is a TikTok duet. Always false for YouTube/Instagram.
            - `is_stitch` boolean, required: Whether this video is a TikTok stitch. Always false for YouTube/Instagram.
            - `publish_date` string, nullable: ISO publish timestamp.
            - `publishDate` string, nullable: Deprecated camelCase alias for `publish_date`. Removed Aug 3, 2026.
            - `hashtags` string[], required
            - `thumbnail_url` string, nullable: Video thumbnail URL. Null when the platform did not supply one.
            - `duration` number, nullable: Video length in seconds. Null when unavailable.
            - `is_pinned` boolean: Whether the video is pinned to the top of the creator profile. TikTok only; absent/false elsewhere.
            - `isPinned` boolean: Deprecated camelCase alias for `is_pinned`. Removed Aug 3, 2026.
            - `sound` V1SoundDto
              - …
            - `outlier_ratio` number, required
            - `weighted_score` number: Size-weighted outlier score (Virality Score)
        - `cross_links` CrossLinksResultDto
          - `discovered` CrossPlatformLinkDto[], required: Discovered cross-platform profiles. Empty if none found.
            - `platform` 'youtube' | 'tiktok' | 'instagram' | 'twitter' | 'spotify', required
            - `username` string, required
            - `url` string, required
            - `source` 'bio_link' | 'bio_text' | 'linktree' | 'spotify_api' | 'ai_search', required
          - `bio_link_url` string, nullable: Link-in-bio URL detected in creator profile (e.g. Linktree).
          - `sources_checked` string[], required: Discovery methods attempted.
        - `audience_demographics` union: Only present when audience_demographics=true. Inferred audience age + gender distributions. Shape is AudienceSnapshotDto on cache hit, or AudienceProcessingPointerDto ({ status: "processing", job_id }) when a fresh job was dispatched.
          - AudienceSnapshotDto
            - `snapshot_id` string, required
            - `snapshot_at` string, required
            - `sample_size` number, required: Number of commenter samples resolved for this snapshot
            - `age_distribution` object, nullable: Age band → share of resolved sample (0–1). Keys: "13-17", "18-24", "25-34", "35-44", "45+". Null when confidence is below internal floor.
            - `gender_distribution` object, nullable: Gender → share of resolved sample (0–1). Keys: "female", "male", "unknown".
            - `country_distribution` AudienceGeoBucketDto[], nullable: Top countries, ordered by share descending. Low-volume tail entries (those backed by fewer than 2 underlying commenters) are consolidated into a single trailing `{ "name": "Other (N countries)", "pct": <sum> }` bucket so the distribution head stays readable on small samples.
              - …
            - `city_distribution` AudienceGeoBucketDto[], nullable: Top cities, ordered by share descending. Long-tail consolidation matches `country_distribution` — singletons are rolled into a trailing `{ "name": "Other (N cities)", "pct": <sum> }` bucket. May be null when city signal is too sparse.
              - …
            - `language_distribution` object, nullable: Language code → share of resolved sample (0–1). ISO 639-1 lowercase codes.
            - `confidence_per_signal` AudienceConfidenceDto, required
              - …
            - `confidence_level` 'low' | 'medium' | 'high', required: Coarse confidence band. "high" = ≥200 samples with strong signals; "medium" = ≥100 or "high" downgraded (weak signals OR followers-only sample); "low" = ≥30 samples. Quick-take on snapshot reliability before reading `confidence_per_signal`.
            - `data_source` 'comments' | 'mixed' | 'followers' | 'comments_extended', required: How the sample was assembled. "comments" — engagement-only sample (default); "mixed" — comments + follower list blend (TikTok Tier 1 fallback when comments are sparse); "followers" — follower-list only (TikTok Tier 2 fallback, indicates audience composition without active engagement); "comments_extended" — comments re-collected over an extended post window (IG/YT fallback). Use to weight signals: comment-derived samples reflect active engagement; follower-derived samples reflect who chose to subscribe.
            - `signal_breakdown` object, required: Per-source contribution to `sample_size`. Always sums to `sample_size`. For `data_source = "followers"` snapshots, `comments` is 0; for `data_source = "comments"` snapshots, `followers` is 0; for `mixed` both are non-zero.
              - …
            - `evidence_summary` string, nullable: Human-readable summary of how the inferences were derived. Calls out when the sample is follower-sourced rather than commenter-sourced.
            - `model_version` string, required: Audience inference model version.
          - AudienceProcessingPointerDto
            - `status` string, required
            - `job_id` string, required: Job ID for the dispatched audience snapshot — poll via the audience endpoints.
        - `audience_geography` union: Only present when audience_geography=true. Inferred country + city distributions and language mix. Shape is AudienceSnapshotDto on cache hit, or AudienceProcessingPointerDto ({ status: "processing", job_id }) when a fresh job was dispatched.
          - AudienceSnapshotDto
            - `snapshot_id` string, required
            - `snapshot_at` string, required
            - `sample_size` number, required: Number of commenter samples resolved for this snapshot
            - `age_distribution` object, nullable: Age band → share of resolved sample (0–1). Keys: "13-17", "18-24", "25-34", "35-44", "45+". Null when confidence is below internal floor.
            - `gender_distribution` object, nullable: Gender → share of resolved sample (0–1). Keys: "female", "male", "unknown".
            - `country_distribution` AudienceGeoBucketDto[], nullable: Top countries, ordered by share descending. Low-volume tail entries (those backed by fewer than 2 underlying commenters) are consolidated into a single trailing `{ "name": "Other (N countries)", "pct": <sum> }` bucket so the distribution head stays readable on small samples.
              - …
            - `city_distribution` AudienceGeoBucketDto[], nullable: Top cities, ordered by share descending. Long-tail consolidation matches `country_distribution` — singletons are rolled into a trailing `{ "name": "Other (N cities)", "pct": <sum> }` bucket. May be null when city signal is too sparse.
              - …
            - `language_distribution` object, nullable: Language code → share of resolved sample (0–1). ISO 639-1 lowercase codes.
            - `confidence_per_signal` AudienceConfidenceDto, required
              - …
            - `confidence_level` 'low' | 'medium' | 'high', required: Coarse confidence band. "high" = ≥200 samples with strong signals; "medium" = ≥100 or "high" downgraded (weak signals OR followers-only sample); "low" = ≥30 samples. Quick-take on snapshot reliability before reading `confidence_per_signal`.
            - `data_source` 'comments' | 'mixed' | 'followers' | 'comments_extended', required: How the sample was assembled. "comments" — engagement-only sample (default); "mixed" — comments + follower list blend (TikTok Tier 1 fallback when comments are sparse); "followers" — follower-list only (TikTok Tier 2 fallback, indicates audience composition without active engagement); "comments_extended" — comments re-collected over an extended post window (IG/YT fallback). Use to weight signals: comment-derived samples reflect active engagement; follower-derived samples reflect who chose to subscribe.
            - `signal_breakdown` object, required: Per-source contribution to `sample_size`. Always sums to `sample_size`. For `data_source = "followers"` snapshots, `comments` is 0; for `data_source = "comments"` snapshots, `followers` is 0; for `mixed` both are non-zero.
              - …
            - `evidence_summary` string, nullable: Human-readable summary of how the inferences were derived. Calls out when the sample is follower-sourced rather than commenter-sourced.
            - `model_version` string, required: Audience inference model version.
          - AudienceProcessingPointerDto
            - `status` string, required
            - `job_id` string, required: Job ID for the dispatched audience snapshot — poll via the audience endpoints.
        - `trends` CreatorTrendsDto
          - `analyzed` boolean, required: True iff trend_analysis was set on the request.
          - `summary` string, required
          - `trends` CreatorTrendItemDto[], required
            - `name` string, required
            - `description` string, required
            - `tactics` string[], required
            - `confidence` number, required
            - `video_count` number, required
            - `evidence_video_ids` string[], required
            - `time_windows` CreatorTrendsTimeWindowDto[], required
              - …
            - `resurged` boolean, required: True iff this trend has >=2 time_windows (i.e. an era this creator returned to).
            - `momentum` 'stronger' | 'weaker' | 'similar', nullable: Latest vs prior window avg_views. Null for single-window trends. +/-15% deadband.
          - `model_used` string, nullable
          - `cost_usd` number, nullable
          - `tokens_used` number, nullable
          - `status` 'ok' | 'insufficient_corpus' | 'skipped', required
        - `run_id` string, uuid: Durable satellite_run id for this lookup. Use it with GET /v1/satellite/runs/:run_id to re-read the full result for free at any time. Always present on completed responses.
        - `credits_charged` number: Credits charged for this run (base + trend surcharge + audience surcharge). Informational; the credit is also reflected on your account ledger and in `public_api_usage`.
        - `credits_refunded` number: Credits paid back because part of the run was not delivered: the trend_analysis surcharge when no trends came out, the audience surcharge when no new snapshot was needed. Present only when a refund was made.
      - `finalized` boolean: True when this lookup will not change anymore: the main lookup is in a terminal state AND `pending_jobs` is empty. Optional for backwards compatibility.
      - `pending_jobs` PendingJobDto[]: Secondary jobs (e.g. audience demographics / geography snapshots) still running after the main lookup completed. Always an array, never null. Empty when nothing is pending.
        - `type` 'viral_analysis' | 'audience_demographics' | 'audience_geography' | 'tracking_report', required: Stable identifier for the kind of secondary job. Use this to switch on the field where the result will appear (see `result_path`).
        - `status` 'pending' | 'processing', required: Current non-terminal state of the job. Once the job reaches a terminal state (ready / failed / skipped) the entry is removed from `pending_jobs[]`.
        - `job_id` string: Optional opaque identifier for the underlying job, when one exists. Useful with `poll_url` to fetch the result directly.
        - `poll_url` string: Path (relative to api.virlo.ai) you can GET to fetch the result of this job once it is ready. Free to call.
        - `result_path` string: Dot-path inside the parent resource's response body where the completed result will appear. Useful for clients that re-fetch the resource instead of using `poll_url`.
        - `webhook_event` string: Name of an event from the existing `SUPPORTED_EVENTS` list that fires when this job reaches a terminal state. Subscribe to it via POST /v1/webhooks to avoid polling. Guaranteed to be one of the already-supported event names so existing webhook subscriptions keep working — no new event types were introduced.
        - `started_at` string: When the job entered its current state (ISO 8601 UTC).
        - `retry_after_seconds` number: Suggested minimum delay (seconds) before polling again. Respect this to avoid hitting rate limits.
    - CreatorStatusFailedDto
      - `status` string, required
      - `error` string, required: Human-readable reason. Free to change: branch on error_code.
      - `error_code` 'creator_not_found' | 'no_public_posts' | 'creator_unavailable' | 'provider_unavailable' | 'lookup_failed': Why the lookup failed, as a stable code. creator_not_found: no account under this handle, fix the handle. no_public_posts: the account exists but has no posts to analyze. creator_unavailable: the profile could not be read (private, age-restricted or region-blocked). provider_unavailable: the profile shows posts but they could not be loaded right now; retry in a few minutes. lookup_failed: any other error. Every failed lookup is refunded. New codes may be added.
      - `credits_refunded` number: Credits paid back because the lookup failed (for example a handle with no public posts). Present only when a refund was made.
      - `finalized` boolean: Always true when the lookup failed: no further work will happen. Included for shape uniformity with the completed variant.
      - `pending_jobs` PendingJobDto[]: Always an empty array on failure.
        - `type` 'viral_analysis' | 'audience_demographics' | 'audience_geography' | 'tracking_report', required: Stable identifier for the kind of secondary job. Use this to switch on the field where the result will appear (see `result_path`).
        - `status` 'pending' | 'processing', required: Current non-terminal state of the job. Once the job reaches a terminal state (ready / failed / skipped) the entry is removed from `pending_jobs[]`.
        - `job_id` string: Optional opaque identifier for the underlying job, when one exists. Useful with `poll_url` to fetch the result directly.
        - `poll_url` string: Path (relative to api.virlo.ai) you can GET to fetch the result of this job once it is ready. Free to call.
        - `result_path` string: Dot-path inside the parent resource's response body where the completed result will appear. Useful for clients that re-fetch the resource instead of using `poll_url`.
        - `webhook_event` string: Name of an event from the existing `SUPPORTED_EVENTS` list that fires when this job reaches a terminal state. Subscribe to it via POST /v1/webhooks to avoid polling. Guaranteed to be one of the already-supported event names so existing webhook subscriptions keep working — no new event types were introduced.
        - `started_at` string: When the job entered its current state (ISO 8601 UTC).
        - `retry_after_seconds` number: Suggested minimum delay (seconds) before polling again. Respect this to avoid hitting rate limits.

## Other responses

- `401`: Unauthorized - missing or invalid API key
- `404`: Job not found or expired
- `429`: Rate limit exceeded (`rate_limit_exceeded`); see the Retry-After header

---

[API](https://skmtc.dev/virlo/apis/virlo-public-api.md) · [All operations](https://skmtc.dev/virlo/apis/virlo-public-api/llms.txt) · [OpenAPI document](https://skmtc.dev/virlo/apis/virlo-public-api/revisions/3b69c6145087?raw)
