---
title: "List the snapshots of a report"
method: POST
path: "/snapshots/list"
tags: ["Snapshots"]
---

# List the snapshots of a report

`POST /snapshots/list`

## Request

- Base URL: `https://api.reportingninja.com/v1`
- URL: `https://api.reportingninja.com/v1/snapshots/list`
- Auth: HTTP bearer

Returns the snapshots of a report, newest first: what each one is, whether it has a file and what that file is called.

EVERY SNAPSHOT THAT HAS A FILE COMES WITH A SIGNED LINK. `download_url` opens the file with no Authorization header at all, which is what lets an assistant hand it to a person, and it stops working 900 seconds after this call (`download_url_expires_at`). It is null when the snapshot has no file, and a client that finds it null for one that does falls back to `snapshot_key` with the Bearer. A link is good for that one snapshot and cannot be revoked before it expires; list again for a fresh one. The file itself is served by GET /v1/snapshots/download.

The size of a file is NOT published: measuring it would mean loading it, and a listing of thirteen files is the one place that must not. The download answers Content-Length.

## Request body

- SnapshotsListRequest
  - `report_key` string, required: The report.

## Response `200`

The call succeeded. This endpoint reports its errors with a real HTTP status, so a 200 always carries status=ok.

- SnapshotsListResponse
  - `status` 'ok', required
  - `data` SnapshotsListData, required
    - `report_key` string, required
    - `snapshots` Snapshot[], required: Newest first. A report keeps its 13 newest snapshots.
      - `snapshot_key` string, required
      - `report_key` string, required
      - `type` 'manual' | 'scheduled', required: manual was asked for with /v1/reports/take-snapshot or from the application; scheduled was filed by a scheduled delivery.
      - `status` 'success' | 'error', required: A snapshot of an execution that failed carries no file.
      - `filename` string, nullable: The name the file is downloaded under.
      - `created_at` string, nullable: When the snapshot was filed, ISO 8601 UTC.
      - `download_url` string, nullable: A signed link to the file, good for 900 seconds and for this snapshot alone. It needs no Authorization header, and it is the whole URL, ready to open. Null when the snapshot has no file, and null as well in the rare case of a node that does not know the public base of this API, where the file is still reachable by `snapshot_key` with the Bearer of the account.
      - `download_url_expires_at` string, nullable: When that link stops working, ISO 8601 UTC. List again for a fresh one.
    - `count` integer, required
  - `meta` Meta, required
    - `request_id` string, required: Id of this request, useful when reporting an incident.
    - `timestamp` string, date-time, required: When the answer was built, in UTC.

## Other responses

- `400`: The request was refused before anything was written. INVALID_REQUEST covers the framework refusals of every endpoint (the Content-Type is not application/json, the body could not be read, the body is not valid JSON). The report endpoints add their own: VALIDATION_FAILED with details.problems, UNKNOWN_PARAM with the path of the field the endpoint does not read, and the single-field refusals MISSING_REQUIRED_FIELD, INVALID_VALUE and APPEND_NOT_ALLOWED. The description of each operation lists the ones it emits. Everything else a body can get wrong arrives as an entry of details.problems rather than as the code of the envelope.
- `401`: Invalid or missing Bearer token.
- `404`: A key of the request does not name anything this account owns. NOT_FOUND covers reports, sections, widgets, schedules, portal views, brand templates, email templates and language files; TEMPLATE_NOT_FOUND covers a catalog slug, version or section position that stopped being published between the validation and the write. An entity of another account is reported the same way as one that does not exist.
- `409`: The request is well formed and the current state does not allow it. Every write can answer IDEMPOTENCY_CONFLICT (the same idempotency_key with a different body) and IDEMPOTENCY_IN_PROGRESS (an identical call is running, with a Retry-After). The endpoints that ask for a confirmation add CONFIRMATION_EXPIRED and CONFIRMATION_MISMATCH; the ones that never confirm cannot answer either. Each operation lists the domain conflicts it adds on top of those.
- `413`: The request body is over 1 MB. The cap is applied before authentication, so this answer is refused without the key being read, and it carries the usual error envelope.
- `429`: A budget was spent. RATE_LIMITED is the per-key read budget (300/minute, 5000/day) or the per-account write budget (60/minute, 1000/day). EMAIL_QUOTA_REACHED is the separate budget of 50 calls per account per day that send email, and its Retry-After points at the next UTC midnight. The Retry-After header gives the seconds until the more restrictive window resets. The X-RateLimit-* headers report the read budget, except on the 429 of a write, where they report the write budget.
- `500`: Internal server error.

---

[API](https://skmtc.dev/reportingninja/apis/reporting-ninja-api.md) · [All operations](https://skmtc.dev/reportingninja/apis/reporting-ninja-api/llms.txt) · [OpenAPI document](https://skmtc.dev/reportingninja/apis/reporting-ninja-api/revisions/13876c71be34?raw)
