---
title: "Get Scan"
method: GET
path: "/api/v1/scans/{scan_id}"
tags: ["scans"]
---

# Get Scan

`GET /api/v1/scans/{scan_id}`

## Request

- Base URL: `https://app.kaminari.ad`
- URL: `https://app.kaminari.ad/api/v1/scans/{scan_id}`
- Auth: HTTP bearer

Get a scan by ID.

## Path parameters

- `scan_id` string, uuid, required

## Response `200`

Successful Response

- ScanResponse: Full scan detail returned by GET /scans/:id.
  - `id` string, uuid, required
  - `url` string, required
  - `country_code` string, required
  - `emulator_id` string, required
  - `status` 'pending' | 'running' | 'crawled' | 'checking' | 'checking_async' | 'rechecking' | 'completed' | 'partial' | 'failed' | 'cancelled', required: Status of a scan through its lifecycle. Ordered: pending(0) → running(1) → crawled(2) → checking(3) → checking_async(4) → terminal(5). Forward-only transitions enforced by advance_status() in the DB (see status_order() Postgres function). ``CHECKING_ASYNC`` means all inline parts (regexp + domain checks) have been applied and we are waiting only on async parts (LLM batch today, SafeBrowsing / VirusTotal tomorrow). UI can show partial tags + alerts at this stage; only classification is still pending. ``RECHECKING`` is a **transient** state used **only** by the recheck flow. The recheck use case atomically moves a scan from a terminal state into ``RECHECKING``, wipes prior tags (CH tombstones), wipes scan_part_state, then atomically transitions to ``CHECKING`` with fresh ``expected_parts``. Both transitions bypass the forward-only ``advance_status()`` guard via dedicated repository methods. Why RECHECKING exists: stale ``check.parts.*`` messages for a now- rechecking scan must NOT write tags (those would survive the wipe without tombstones, corrupting the post-recheck tag set). Consumers check ``WRITE_BLOCKED_STATUSES`` and skip the message when the scan is in ``RECHECKING``. Defence in depth — the TagWriterAdapter's UoW also re-reads the scan status before save.
  - `parent_scan_id` string, uuid, nullable
  - `ad_discovery` boolean
  - `max_discovered_ads` integer, nullable
  - `ad_formats` AdKind[], nullable
  - `slot_index` integer, nullable
  - `ad_kind` string, nullable
  - `network` string
  - `network_host` string
  - `offer_url` string, required
  - `redirect_chain` RedirectHopResponse[], required
    - `url` string, required
    - `status_code` integer, required
    - `content_type` string, required
    - `body_size` integer, required
    - `timestamp_ms` integer, required
    - `redirected_from` string, required
    - `sub_requests` SubRequestResponse[], required
      - `url` string, required
      - `resource_type` string, required
      - `status_code` integer, required
      - `content_type` string, required
      - `body_size` integer, required
      - `timestamp_ms` integer, required
      - `children` SubRequestResponse[]
  - `screenshot_url` string
  - `report_url` string
  - `public_report_url` string
  - `ad_tag` string, nullable
  - `vast_tag` string, nullable
  - `referrer` string, nullable
  - `ignore_first_n_domains` integer
  - `creative_kind` 'banner' | 'video'
  - `creative_screenshot_url` string
  - `creative_video_url` string
  - `vast_xml_url` string
  - `creative_html_url` string
  - `creative_width` integer
  - `creative_height` integer
  - `creative_redirect_chain` RedirectHopResponse[]
    - `url` string, required
    - `status_code` integer, required
    - `content_type` string, required
    - `body_size` integer, required
    - `timestamp_ms` integer, required
    - `redirected_from` string, required
    - `sub_requests` SubRequestResponse[], required
      - `url` string, required
      - `resource_type` string, required
      - `status_code` integer, required
      - `content_type` string, required
      - `body_size` integer, required
      - `timestamp_ms` integer, required
      - `children` SubRequestResponse[]
  - `video` VideoMetaResponse: Slim VAST video metadata surfaced on the scan report. Present only for VAST scans. The rendered video frame is served via the existing creative-screenshot URL; this block carries the video-specific facts the report shows alongside it.
    - `duration_ms` integer
    - `mediafile_url` string
    - `vast_version` string
    - `ad_system` string
    - `is_vpaid` boolean
    - `wrapper_depth` integer
    - `click_through` string
  - `proxy` ProxyTargetResponse: Proxy targeting info in a scan response.
    - `proxy_type` string, required
    - `region` string
    - `city` string
    - `isp` string
  - `page_title` string, required
  - `elapsed_ms` integer, required
  - `error` string, required
  - `labels` object
  - `classification` ScanClassificationResponse: Classification result for a scan — AI category + IAB V3 + brand + custom taxonomies.
    - `brand` string, nullable
    - `ai_category` AiCategoryResponse: Freeform AI category tiers (legacy ``iab_v3`` semantics).
      - `tier1` string, required
      - `tier2` string, nullable
      - `tier3` string, nullable
      - `tier4` string, nullable
    - `iab_v3` IabV3CategoryResponse: Canonical IAB Content Taxonomy V3 category tiers (validated).
      - `tier1` string, required
      - `tier2` string, nullable
      - `tier3` string, nullable
      - `tier4` string, nullable
    - `custom_taxonomies` ScanTaxonomyClassificationResponse[]
      - `taxonomy_id` string, uuid, required
      - `taxonomy_name` string, required
      - `taxonomy_slug` string, required
      - `taxonomy_version` integer, required
      - `leaf_node_id` string, uuid, nullable, required
      - `tier1` string, nullable, required
      - `tier2` string, nullable
      - `tier3` string, nullable
      - `tier4` string, nullable
      - `used_default` boolean
  - `campaign_id` string, uuid, nullable
  - `campaign_name` string, nullable
  - `campaign_group_id` string, uuid, nullable
  - `campaign_group_name` string, nullable
  - `created_at` string, date-time, required
  - `completed_at` string, date-time, nullable, required
  - `landings` LandingResponse[]
    - `ord` integer, required
    - `opener_url` string
    - `final_url` string
    - `offer_url` string
    - `page_title` string
    - `screenshot_url` string
    - `redirect_chain` RedirectHopResponse[]
      - `url` string, required
      - `status_code` integer, required
      - `content_type` string, required
      - `body_size` integer, required
      - `timestamp_ms` integer, required
      - `redirected_from` string, required
      - `sub_requests` SubRequestResponse[], required
        - `url` string, required
        - `resource_type` string, required
        - `status_code` integer, required
        - `content_type` string, required
        - `body_size` integer, required
        - `timestamp_ms` integer, required
        - `children` SubRequestResponse[]
    - `elapsed_ms` integer
    - `created_at` string, date-time, required
  - `repeat_index` integer
  - `repeat_total` integer
  - `repeat_session_id` string, uuid, nullable
  - `repeat_scan_ids` string[]: The sibling scans this request also created, excluding this one. Populated only by the create endpoints — list and detail responses leave it empty.
  - `retry_attempt` integer
  - `retry_max_attempts` integer

## Other responses

- `422`: Validation Error

---

[API](https://skmtc.dev/kaminari/apis/kaminari-ad-api.md) · [All operations](https://skmtc.dev/kaminari/apis/kaminari-ad-api/llms.txt) · [OpenAPI document](https://skmtc.dev/kaminari/apis/kaminari-ad-api/revisions/013f86e2fd32?raw)
