---
title: "Retrieve a scan"
method: GET
path: "/v1/scans/{scan_id}"
tags: ["Sources"]
---

# Retrieve a scan

`GET /v1/scans/{scan_id}`

⚠️ **Beta endpoint**: behavior and schemas are subject to change.

Retrieve a historical scan's progress: element and byte counters, plus
the scan's audit summary once one becomes available.

Poll it while the scan runs, watching `report_status` rather than
`scan_status`: a scan reaching a terminal status does not mean the audit
summary is ready. Stop polling once `report_status` is `done` — that
response carries the final numbers (`duration_seconds`,
`pending_seconds`, `outcome_by_reason`) — or `unavailable`, meaning no
report will ever come for this scan.

Counters are reported the same way for every source, but not every source
tracks every counter: `data_downloaded_bytes` and `data_scanned_bytes` are
`null` for sources that do not measure bytes (VCS repositories among them),
which is distinct from a real `0`.

`elements_total` is provisional while `listing_finished` is `false` — it
only reflects elements listed so far, not the final total.

## Path parameters

- `scan_id` string, uuid, required

## Response `200`

Scan progress

- object — ⚠️ **Beta schema**: subject to change. Progress of a historical scan, merged with its audit summary once one is available. Poll this while a scan is running instead of the scan detail endpoint.
  - `scan_id` string, uuid, required — ID of the scan this progress belongs to.
  - `source` object, nullable, required — ⚠️ **Beta schema**: subject to change. Snapshot of the scanned source, frozen when the scan was launched. It is not refreshed afterwards, so a source renamed or resized mid-scan keeps the values it had at launch — except for scans that carry no snapshot, which report the source's current values.
    - `id` integer, required — ID of the source.
    - `visibility` 'public' | 'private' | 'internal', required — Visibility of the source on the provider.
    - `display_name` string, required — Human-readable name of the source.
    - `type` string, required — Type of the source.
    - `size` integer, nullable, required — Size of the source in bytes. `null` for source types that do not report a size.
  - `report_status` 'pending' | 'in_progress' | 'summarizing' | 'done' | 'unavailable', required — How far the scan's audit report has got. Poll on this, not on `scan_status`: the scan reaching a terminal status does not mean the report's final numbers (`duration_seconds`, `pending_seconds`, `outcome_by_reason`) are ready yet. Stop polling once `report_status` is `done` (the numbers are ready) or `unavailable` (no report will ever come).
  - `scan_status` 'launched' | 'pending' | 'running' | 'finished' | 'failed' | 'canceled' | 'too_large' | 'timeout' | 'skipped' | 'pending_timeout' | 'running_failed' | 'running_cancelled', required — Status of the scan itself.
  - `scan_status_reason` string, nullable, required — Reason for the scan's current status, e.g. why it failed or was skipped. `null` when not applicable.
  - `elements_total` integer, required — Number of elements listed so far. Provisional (still growing) until `listing_finished` is `true`, after which this is the scan's final count.
  - `elements_scanned` integer, required — Number of elements scanned so far. Final once `scan_status` is terminal.
  - `elements_skipped` integer, required — Number of elements skipped so far. Final once `scan_status` is terminal. Includes elements filtered out before listing (e.g. unsupported file types), which are not counted in `elements_total`.
  - `elements_failed` integer, required — Number of elements that failed to scan so far. Final once `scan_status` is terminal.
  - `listing_finished` boolean, required — Whether the scan has finished listing all its elements, i.e. whether `elements_total` reflects the true final count.
  - `data_downloaded_bytes` integer, nullable, required — Amount of data downloaded so far, in bytes. `null` for sources that do not track byte counters, which is distinct from a real `0`.
  - `data_scanned_bytes` integer, nullable, required — Amount of data scanned so far, in bytes. `null` for sources that do not track byte counters, which is distinct from a real `0`.
  - `started_at` string, date-time, nullable, required — Date and time the scan started. `null` while the scan is still queued.
  - `ended_at` string, date-time, nullable, required — Date and time the scan ended. `null` while it is running.
  - `duration_seconds` integer, nullable, required — Duration of the scan, in seconds, from the audit summary. `null` until `report_status` is `done`.
  - `pending_seconds` integer, nullable, required — Time the scan spent pending before it started, in seconds, from the audit summary. `null` until `report_status` is `done`.
  - `outcome_by_reason` object, nullable, required — Element outcome counts broken down by reason, from the audit summary, under the `skipped` and `failed` categories. `null` until `report_status` is `done`.
  - `elements_by_type` object, required — Per-element-type breakdown of the counts above, for sources that track more than one kind of element. Only populated for VCS repository scans, where it reports `commit`, `branch` and `patch` counters; an empty object otherwise. Note `patch` carries skip counters only.

## Other responses

- `400` — Invalid data
- `401` — Invalid API key
- `403` — Forbidden Call
- `404` — Scan not found
- `503` — API under maintenance

## Changes

- **2026-09-28** `4bc35232f994` — 1 info
  - endpoint added

[Change history](https://skmtc.dev/gitguardian/apis/gitguardian-api/changes/v1/scans/:scan_id/get.md)

---

[API](https://skmtc.dev/gitguardian/apis/gitguardian-api.md) · [All operations](https://skmtc.dev/gitguardian/apis/gitguardian-api/llms.txt) · [OpenAPI document](https://skmtc.dev/gitguardian/apis/gitguardian-api/revisions/488d081f7d86?raw)
