API Reference
IdScanly Identity Document Scanning API - v1
The IdScanly API accepts one or more identity document images in a session, processes them asynchronously, and exposes partial and final recognition results through polling.
Typical integration flow
- 1Create a session
- 2Upload the first image
- 3Optionally upload additional images to the same session
- 4Poll the session until isProcessing is false
- 5Read merged fields from result.fields
Base URL
https://api.idscanly.com/v1All examples below use:
BASE_URL="https://api.idscanly.com/v1"
API_KEY="idscanly_live_replace_me"Authentication
Send the API key in the x-api-key header on every request.
x-api-key: idscanly_live_replace_mecurl -H "x-api-key: $API_KEY" "$BASE_URL/stats/today"Missing, invalid, disabled, or expired keys return:
HTTP/1.1 401 Unauthorized
Cache-Control: no-store{
"error": "Invalid or expired API key",
"code": "UNAUTHORIZED",
"requestId": "req-123"
}API keys are provisioned by IdScanly. Contact support and provide the partner or application name, technical contact, and intended environment.
Image Requirements
| Property | Description |
|---|---|
| Multipart Content-Type | multipart/form-data |
| Multipart field name | image |
| Base64 Content-Type | application/json |
| Max file size | 15 MiB |
| Max decoded base64 image size | 15 MiB |
| Max images per session | 10 (default) |
| Images per request | One image per upload request |
| Supported formats | JPEG, PNG, WebP, HEIC, HEIF |
The server validates actual image content, not only the MIME header. HEIC/HEIF uploads are decoded server-side before preprocessing and OCR.
Recommended capture
- •Show the entire document - avoid cut-off corners
- •Use sufficient light without glare
- •Keep text reasonably sharp
- •Use the highest practical resolution
- •Upload front and back as separate images in the same session
1. Create a Session
No request body is required. Returns a session ID and upload URL.
curl -X POST \
-H "x-api-key: $API_KEY" \
"$BASE_URL/sessions"Response 201 Created
{
"success": true,
"sessionId": "9c5c6e9c-73b6-4976-a84f-527a98552713",
"isProcessing": false,
"status": "waiting_for_images",
"uploadUrl": "/v1/sessions/9c5c6e9c-73b6-4976-a84f-527a98552713/images",
"resultUrl": "/v1/sessions/9c5c6e9c-73b6-4976-a84f-527a98552713"
}2. Upload an Image
SESSION_ID="9c5c6e9c-73b6-4976-a84f-527a98552713"
curl -X POST \
-H "x-api-key: $API_KEY" \
-F "image=@front.jpg" \
"$BASE_URL/sessions/$SESSION_ID/images"Optional document crop
Enable automatic ID detection and cropping with the crop=true query parameter. Accepted values: true, false, 1, 0. Disabled by default.
curl -X POST \
-H "x-api-key: $API_KEY" \
-F "image=@front.jpg" \
"$BASE_URL/sessions/$SESSION_ID/images?crop=true"Use crop when the ID occupies only part of a larger photograph. Leave disabled when the image is already tightly framed.
Response 202 Accepted
{
"success": true,
"imageId": "5f4fc156-c08d-4cd0-b9f6-5112eb13178e",
"sessionId": "9c5c6e9c-73b6-4976-a84f-527a98552713",
"status": "queued",
"cropEnabled": false,
"resultUrl": "/v1/sessions/9c5c6e9c-73b6-4976-a84f-527a98552713"
}202 means accepted for processing. It does not mean recognition has finished.
Upload a base64 image
Use the dedicated base64 endpoint when the client cannot send multipart form data.
IMAGE_BASE64="$(base64 -w 0 front.jpg)"
curl -X POST \
-H "x-api-key: $API_KEY" \
-H "content-type: application/json" \
-d "{\"imageBase64\":\"$IMAGE_BASE64\",\"filename\":\"front.jpg\",\"mimeType\":\"image/jpeg\"}" \
"$BASE_URL/sessions/$SESSION_ID/images/base64"JSON body
{
"imageBase64": "/9j/4AAQSkZJRgABAQ...",
"filename": "front.jpg",
"mimeType": "image/jpeg"
}imageBase64 may also be a data URL. When a data URL is used, mimeType is optional. The response is the same 202 Accepted payload returned by multipart uploads.
Add more images
Upload each additional image (e.g. back of the document) in a separate request using the same session ID. Do not create a separate session for each side of the same document.
curl -X POST \
-H "x-api-key: $API_KEY" \
-F "image=@back.jpg" \
"$BASE_URL/sessions/$SESSION_ID/images"Upload without explicit session creation
A compatibility endpoint that creates a session automatically:
# Creates a new session automatically
curl -X POST \
-H "x-api-key: $API_KEY" \
-F "image=@front.jpg" \
"$BASE_URL/session-images"
# Append to an existing session
curl -X POST \
-H "x-api-key: $API_KEY" \
-F "image=@back.jpg" \
"$BASE_URL/session-images?sessionId=$SESSION_ID"For new integrations, the explicit POST /sessions → POST /sessions/{sessionId}/images workflow is preferred.
The same compatibility behavior is available for base64 uploads:
curl -X POST \
-H "x-api-key: $API_KEY" \
-H "content-type: application/json" \
-d "{\"imageBase64\":\"$IMAGE_BASE64\",\"filename\":\"front.jpg\",\"mimeType\":\"image/jpeg\"}" \
"$BASE_URL/session-images/base64"3. Poll Results
curl \
-H "x-api-key: $API_KEY" \
"$BASE_URL/sessions/$SESSION_ID?view=summary"isProcessing is false. Partial fields appear while processing is still active.Example response (still processing)
{
"success": true,
"sessionId": "9c5c6e9c-73b6-4976-a84f-527a98552713",
"status": "processing",
"isProcessing": true,
"summary": {
"status": "partial_or_complete",
"isComplete": false,
"imageCount": 1,
"successfulImageCount": 1,
"failedImageCount": 0,
"fields": {
"documentType": "identity_card",
"documentCountry": "HUN",
"documentNumber": "123456AB",
"fullName": "EXAMPLE PERSON"
},
"fieldConfidences": {
"documentNumber": 0.96,
"fullName": 0.93
},
"fieldSources": {
"documentNumber": {
"value": "123456AB",
"source": "spatial_label",
"imageId": "5f4fc156-c08d-4cd0-b9f6-5112eb13178e",
"confidence": 0.96
}
}
},
"result": {},
"images": [
{
"imageId": "5f4fc156-c08d-4cd0-b9f6-5112eb13178e",
"status": "running",
"sourceName": "front.jpg",
"fieldCount": 8,
"avgConfidence": 0.93,
"mrzFound": false,
"error": null
}
],
"createdAt": "2026-06-10T08:00:00.000Z",
"updatedAt": "2026-06-10T08:00:05.000Z",
"completedAt": null
}Full diagnostic view
Omitting ?view=summary returns the full response including per-image OCR blocks, candidates, recognition layer details, preprocessing timings and logs. Use only for controlled diagnostics.
Detailed logs are also available from:
Session Status Values
| Status | Meaning |
|---|---|
| waiting_for_images | Session exists but no image has been uploaded |
| processing | At least one image is queued or running |
| finished | No image is processing; at least one image completed |
| error | All images in the session failed |
isProcessing === false. A finished session may contain individual failed images - check result.failedImageCount and each image status.Image status values
| Status | Meaning |
|---|---|
| queued | Accepted and waiting to start |
| running | Image processing is active |
| done | Processing completed successfully |
| error | Processing failed for this image |
Merged Result
The merged result is available under summary.fields (summary view) or result.fields (full view).
| Property | Description |
|---|---|
| fields | Selected normalized field values |
| fieldConfidences | Final confidence per selected field (0–1) |
| fieldSources | Source layer, image and confidence for each field |
| documents | Per-image document classifications |
| mrz | Valid parsed MRZ results |
| images | Per-image result summary |
| isComplete | Whether every image reached a terminal state |
Available field names
documentType documentCountry documentNumber
documentCode fullName surname
givenNames dateOfBirth placeOfBirth
nationality sex issueDate
dateOfExpiry issuingAuthority motherMaidenName
address categories canfieldConfidences and fieldSources when making automated decisions.Error Responses
Unknown session 404 Not Found
{ "error": "Session not found" }A session created by one API key is not visible to another API key.
Invalid upload 400 Bad Request
{
"error": "Uploaded file is not a valid supported image",
"code": "INVALID_UPLOAD",
"requestId": "req-124"
}File too large 413 Payload Too Large
{
"error": "request file too large",
"code": "PAYLOAD_TOO_LARGE",
"requestId": "req-125"
}Session image limit 400 Bad Request
{
"error": "Session image limit reached (10)",
"code": "INVALID_UPLOAD",
"requestId": "req-126"
}JavaScript Example
const baseUrl = 'https://api.idscanly.com/v1';
const apiKey = process.env.IDSCANLY_API_KEY;
const headers = { 'x-api-key': apiKey };
// 1. Create session
const created = await fetch(`${baseUrl}/sessions`, {
method: 'POST',
headers,
}).then(readJson);
// 2. Upload image
const form = new FormData();
form.append('image', new Blob([imageBuffer], { type: 'image/jpeg' }), 'front.jpg');
await fetch(`${baseUrl}/sessions/${created.sessionId}/images?crop=false`, {
method: 'POST',
headers,
body: form,
}).then(readJson);
// 3. Poll until done
let session;
do {
await new Promise((resolve) => setTimeout(resolve, 2000));
session = await fetch(
`${baseUrl}/sessions/${created.sessionId}?view=summary`,
{ headers }
).then(readJson);
} while (session.isProcessing);
console.log(session.result.fields);
async function readJson(response) {
const payload = await response.json();
if (!response.ok) throw new Error(payload.error || `HTTP ${response.status}`);
return payload;
}For HEIC images, use the original filename and MIME type:
form.append(
'image',
new Blob([heicBuffer], { type: 'image/heic' }),
'front.heic'
);Privacy Notes
Identity document images and extracted fields are sensitive personal data. API responses include Cache-Control: no-store, but clients must also:
- •Avoid browser caching of responses
- •Encrypt data in transit and at rest
- •Avoid placing fields in URLs
- •Avoid logging full response bodies
- •Delete local temporary files promptly
- •Restrict access to diagnostic responses
- •Comply with applicable privacy and retention requirements
Ready to integrate?
Request an API key and start extracting structured data from identity documents.