---
title: "Get a content research agent"
method: GET
path: "/v1/agents/{id}"
tags: ["Content Research Agents"]
---

# Get a content research agent

`GET /v1/agents/{id}`

## Request

- Base URL: `https://api.virlo.ai`
- URL: `https://api.virlo.ai/v1/agents/{id}`
- Auth: HTTP bearer

Returns the agent config, autonomy state, latest run, and async status. `finalized: true` means the latest run (scrape + AI analysis) is fully settled.

## Path parameters

- `id` string, required

## Response `200`

Agent

- AgentDetailResponseDto
  - `data` AgentDetailDto, required
    - `id` string, uuid, required
    - `name` string, nullable
    - `is_recurring` boolean, required: false = one-shot (formerly Orbit); true = recurring + cadence (formerly Comet)
    - `active` boolean, required
    - `team_id` string, nullable
    - `source` string, nullable
    - `keywords` string[], required
    - `platforms` string[], required
    - `exclude_keywords` string[], required
    - `exclude_keywords_strict` boolean, required
    - `meta_ads_enabled` boolean, required
    - `data_intelligence_enabled` boolean, required
    - `english_only` boolean, required
    - `intent` string, nullable
    - `intent_keywords` string[], nullable: The expanded keyword set the agent actually searched, derived from your `intent` + `keywords`. Null until the first run resolves it. Useful for seeing how the agent interpreted your intent.
    - `autopilot` boolean, required: Whether autopilot is on. On by default: after each run a recurring agent rewords its searches, adds keywords and follows breaking stories so it keeps finding new videos. It never removes a keyword or exclude term you set. Turn it off with `autopilot: false` on `PUT /v1/agents/:id`.
    - `pinned_keywords` string[], nullable: The keywords you set. Autopilot always keeps these and only adds around them, so `keywords` holds these plus any autopilot added. Replaced whenever you send `keywords`. Null for agents created in the Virlo app.
    - `autonomy_level` 'suggest' | 'autopilot', required: Older two-field spelling of `autopilot`: "autopilot" = on, "suggest" = off. Read `autopilot` instead.
    - `autopilot_unlocked` boolean, required: Legacy field from when autopilot had to be unlocked first; it no longer gates anything. Read `autopilot` instead.
    - `cognition_enabled` boolean, required
    - `cadence` string, nullable: Cron cadence. Null for one-shot agents.
    - `next_run_at` string, nullable
    - `last_run_at` string, nullable
    - `is_processing` boolean, required
    - `created_at` string, required
    - `updated_at` string, required
    - `latest_run` AgentRunDto
      - `id` string, uuid, required
      - `agent_id` string, uuid, required
      - `status` 'pending' | 'processing' | 'completed' | 'partial_failure' | 'failed', required: `pending` and `processing` are in flight. `completed`, `partial_failure` and `failed` are final. `partial_failure` means one platform or keyword failed and the rest of the data is usable.
      - `total_videos_inserted` number, required
      - `total_videos_updated` number, required
      - `total_videos_failed` number, required
      - `videos_linked` number, required
      - `meta_ads_linked` number, required
      - `slideshows_linked` number, required
      - `youtube_count` number, required
      - `tiktok_count` number, required
      - `instagram_count` number, required
      - `exclude_keywords_filtered` number, required
      - `intent_filtered` number, required
      - `language_filtered_count` number, required
      - `duplicates_dropped` number, required
      - `outliers_identified` number, required
      - `trends_detected` string[], nullable
      - `keyword_breakdown` object[], nullable: Per-keyword collection breakdown. One entry per keyword with `keyword`, `videos_inserted`, `videos_updated`, and per-platform counts (`tiktok_count`, `youtube_count`, `instagram_count`). The fastest way to see which keywords are actually producing content.
      - `execution_time_ms` number, required
      - `created_at` string, required
      - `started_at` string, nullable
      - `completed_at` string, nullable
    - `analysis` string, nullable: Short human-readable highlight from the latest AI analysis. null if none yet.
    - `analysis_data` object, nullable: Full structured AI analysis object, or null if none yet.
    - `analysis_batch_start` string, nullable
    - `analysis_batch_end` string, nullable
    - `finalized` boolean: True when the latest run (scrape + AI analysis) is fully settled.
    - `pending_jobs` PendingJobDto[]: Secondary jobs still running after the latest scrape. 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.
    - `progress_pct` number, nullable: Coarse completion percentage (0–100) of the latest run, derived from `stage`. 100 once `finalized`. Null when it cannot be determined.
    - `stage` 'collecting' | 'analyzing' | 'finalizing' | 'completed', nullable: Lifecycle stage of the latest run: `collecting` (scrape in flight), `analyzing` (AI viral-analysis running), `finalizing` (settling), `completed` (`finalized: true`).
    - `eta_seconds` number, nullable: Best-effort seconds until the latest run finalizes. 0 once `finalized`; a bounded estimate while `analyzing`; null when not estimable.

## Other responses

- `401`: Missing or invalid API key (`missing_api_key` / `invalid_api_key`)
- `404`: Agent not found
- `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)
