GET /v1/scans/{id}¶
Fetch a previous scan
Returns a scan made earlier with a live key and kept under a non-zero retention window, while that window lasts; anything else is 404. The image crops are never stored, so every image slot comes back null, and neither is the engine's own output, so quality reads not_checked.
Reads back a result the service still holds. It never re-runs recognition and is never billed.
A scan is readable only while its retention window is open: the retain_hours the creating request asked for, or the account's own history-retention setting when it asked for none. A scan created with retain_hours: 0 was never written down.
Image crops are not stored. A result read back here carries images: null whatever the creating response returned.
Request¶
| Method | GET |
| Path | /v1/scans/{id} |
| Authentication | Authorization: Bearer <api key> |
Path parameters¶
| Name | Type | Required | Description |
|---|---|---|---|
id |
string | yes | Scan identifier: a UUID version 7 (RFC 9562), canonical lower-case 8-4-4-4-12. Its leading 48 bits are the millisecond the scan was made, so ids sort in the order the scans happened — but treat the value as opaque: nothing else about it is part of the contract. |
Responses¶
200¶
The scan as it was returned when it was created.
Body: Scan.
| Field | Type | Description |
|---|---|---|
meta |
ScanMeta | |
document |
ScanDocument | null | |
holder |
ScanHolder | null | |
fields |
ScanField[] | Every field the engine extracted off the printed document, re-keyed to our vocabulary — the open set. Always present; empty when nothing was extracted. A field read in more than one language appears once per language, so name repeats and only id is unique. |
mrz |
ScanMrz | |
images |
ScanImages | Image crops, returned in the recognition response only. Each is scaled down by height, proportionally and never upwards, to at most 250 px for document_crop and 100 px for every other crop, then re-encoded with every metadata block dropped. |
quality |
ScanQuality | |
authenticity |
ScanAuthenticity |
{
"meta": {
"schema_version": "1.0",
"id": "01a0af18-cd8d-7a61-9f2d-4c7b8e105da3",
"status": "recognized",
"billed": true,
"confidence": "high",
"timing": {
"upload_ms": 198,
"processing_ms": 812,
"total_ms": 1024
},
"created_at": "2026-09-17T10:15:00Z",
"reference": "order-1042"
},
"document": {
"kind": "passport",
"country": "GRC",
"country_name": "Greece",
"issuing_state": "GRC",
"type_name": "Greece - Passport",
"type_confidence": "high",
"is_expired": false,
"days_remaining": 2001
},
"holder": {
"given_names": "ELENI SOFIA",
"surname": "PARADEIGMA",
"full_name": "PARADEIGMA ELENI SOFIA",
"birth_date": "1994-03-08",
"sex": "F",
"nationality": "GRC"
},
"fields": [
{
"id": "surname@0",
"name": "surname",
"label": "Surname",
"category": "identity",
"value": "PARADEIGMA",
"language": null,
"confidence": "high"
},
{
"id": "surname@1032",
"name": "surname",
"label": "Surname",
"category": "identity",
"value": "ΠΑΡΑΔΕΙΓΜΑ",
"language": "Greek",
"confidence": "high"
},
{
"id": "document_number@0",
"name": "document_number",
"label": "Document number",
"category": "document",
"value": "AM7304518",
"language": null,
"confidence": "high"
},
{
"id": "days_to_expire@0",
"name": "days_to_expire",
"label": "Days to expire",
"category": "dates",
"value": "2001",
"language": null,
"confidence": "high"
},
{
"id": "mrz@0",
"name": "mrz",
"label": "MRZ",
"category": "document",
"value": "P<GRCPARADEIGMA<<ELENI<SOFIA<<<<<<<<<<<<<<<<AM73045184GRC9403084F3203101PN48291630<<<<72",
"language": null,
"confidence": "high"
}
],
"mrz": {
"status": "passed",
"reason": null,
"lines": [
"P<GRCPARADEIGMA<<ELENI<SOFIA<<<<<<<<<<<<<<<<",
"AM73045184GRC9403084F3203101PN48291630<<<<72"
],
"text": "P<GRCPARADEIGMA<<ELENI<SOFIA<<<<<<<<<<<<<<<<AM73045184GRC9403084F3203101PN48291630<<<<72"
},
"images": {
"document_crop": "data:image/jpeg;base64,/9j/4AAQSkZJRg==",
"rear": null,
"main_photo": "data:image/jpeg;base64,/9j/4AAQSkZJRg==",
"signature": "data:image/jpeg;base64,/9j/4AAQSkZJRg==",
"watermark_face": "data:image/jpeg;base64,/9j/4AAQSkZJRg==",
"barcode": null,
"chip": null
},
"quality": {
"overall": "pass"
},
"authenticity": {
"overall": "not_checked",
"checks": []
}
}401¶
Missing or malformed Authorization: Bearer <api key> header, or an unknown key.
Body: ErrorResponse.
{
"error": {
"code": "unauthorized",
"message": "Send your API key as `Authorization: Bearer <key>`.",
"docs_url": "https://doc.cheap/docs/errors/unauthorized",
"request_id": "req_9e6b1f7c-2d4a-4b83-9c51-7f0ad3e8b642",
"event_id": null
}
}404¶
No scan with this id exists, or its retention window has passed.
Body: ErrorResponse.
{
"error": {
"code": "not_found",
"message": "No scan with id 01a0af18-cd8d-7a61-9f2d-4c7b8e105da3 exists or it has expired.",
"docs_url": "https://doc.cheap/docs/errors/not_found",
"request_id": "req_9e6b1f7c-2d4a-4b83-9c51-7f0ad3e8b642",
"event_id": null
}
}429¶
Rate limit exceeded for this key or IP.
Body: ErrorResponse.
{
"error": {
"code": "rate_limited",
"message": "Sandbox limit of 10 requests per hour per IP reached.",
"docs_url": "https://doc.cheap/docs/errors/rate_limited",
"request_id": "req_9e6b1f7c-2d4a-4b83-9c51-7f0ad3e8b642",
"event_id": null
}
}500¶
Unexpected failure on the server; the scan was not charged.
Body: ErrorResponse.
{
"error": {
"code": "internal_error",
"message": "Unexpected error.",
"docs_url": "https://doc.cheap/docs/errors/internal_error",
"request_id": "req_9e6b1f7c-2d4a-4b83-9c51-7f0ad3e8b642",
"event_id": null
}
}503¶
Temporarily unable to serve the request: a store this service depends on is unreachable. Nothing was charged and it may be retried, after the Retry-After seconds where that header is present.
Body: ErrorResponse.
{
"error": {
"code": "service_unavailable",
"message": "The service is temporarily unable to handle this request; nothing was charged. Retry shortly.",
"docs_url": "https://doc.cheap/docs/errors/service_unavailable",
"request_id": "req_9e6b1f7c-2d4a-4b83-9c51-7f0ad3e8b642",
"event_id": null
}
}Error codes reachable here¶
| Code | When | Page |
|---|---|---|
internal_error |
The service failed in a way it does not model. | internal_error |
not_found |
No scan with that id was ever stored — it was created with retain_hours: 0, or it belongs to another account — or it was stored and its retention window has since passed. The two cases are not distinguished. |
not_found |
rate_limited |
The caller is over the rate limit for its key kind. | rate_limited |
service_unavailable |
A store this endpoint reads from is unreachable, so the answer could not be produced. Nothing was charged; retry after the Retry-After seconds. |
service_unavailable |
unauthorized |
The Authorization header is missing, malformed or names an unknown key. |
unauthorized |
Every error body carries the same shape; the full list is on Errors.