Zerodoc Documentation
Private, zero-retention document processing. Extract structured fields, classify document types, split batches and crop multi-document scans — all with confidence scores, processed in memory in the EU and discarded immediately. Never stored, never used to train a third-party model.
Make your first extraction call in minutes.
See what Zerodoc extracts and how your data is protected.
Upload your own document and watch extraction happen.
Quickstart
Three steps to your first structured response.
- 1Create an API key
Create a free account and generate a key from your dashboard.
- 2Send a document
POST a file (or URL) to
/v1/extractwith your key. - 3Read the response
Get structured
fields, full text and a retention proof block.
curl -X POST "https://api.zerodoc.io/v1/extract" \
-H "X-API-Key: zk_your_api_key" \
-F "file=@invoice.pdf;type=application/pdf"Authentication
Authenticate every request with your API key in the X-API-Key header. Keys are scoped to your account, created and revoked from your dashboard.
X-API-Key: zk_your_api_keyAPI reference
https://api.zerodoc.io/v1/extractExtract text and structured fields from a document. Send a file or a url to fetch (if both are sent, the file wins). Supported types: PDF, PNG, JPEG, TIFF, WebP.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
file | file | Conditional | Multipart file upload. Provide file or url; if both are sent, the file wins. |
url | string | Conditional | Public URL to fetch the document from (SSRF-guarded). Used when no file is attached. |
doc_type | string | Optional | Document type to extract. Defaults to invoice — currently the only extraction type. |
Request example
curl -X POST "https://api.zerodoc.io/v1/extract" \
-H "X-API-Key: zk_your_api_key" \
-F "file=@invoice.pdf;type=application/pdf"Response
A successful call returns 200 with the extraction result. The document is discarded the moment this response is sent.
{
"request_id": "req_3abc...",
"status": "ok",
"processing_time_ms": 412,
"doc_type": "invoice",
"document": { "mime_type": "application/pdf", "page_count": 1, "size_bytes": 24872 },
"text": "ACME Trading Ltd\nInvoice Number: INV-2026-0042\n...",
"blocks": [
{ "text": "Total Due: 1200.00", "confidence": 0.99, "page": 0,
"bbox": { "x": 0.05, "y": 0.82, "width": 0.30, "height": 0.03 } }
],
"fields": {
"invoice_number": { "value": "INV-2026-0042", "confidence": 0.9 },
"total_amount": { "value": 1200.0, "confidence": 0.85 },
"taxes": [ { "amount": 200.0, "rate": 20.0, "base": 1000.0 } ],
"locale": { "country": "GB", "currency": "GBP" }
},
"extraction_warnings": [],
"mean_confidence": 0.97,
"extraction_confidence": 0.88,
"retention": { "stored": false, "policy": "zero-retention" }
}Response fields
| Field | Type | Description |
|---|---|---|
request_id | string | Unique identifier for the request. |
status | string | "ok" on success. |
processing_time_ms | number | Server-side processing time in milliseconds. |
doc_type | string | Document type extracted (echoes the request). |
document | object | mime_type, page_count and size_bytes of the input. |
text | string | Full extracted text. |
blocks | array | Text blocks, each with confidence and a normalized bounding box. |
fields | object | Structured fields (invoice number, total, VAT, …), each with a value and confidence, plus derived composites like taxes[] and locale. |
extraction_warnings | array | Sanity-check notes (e.g. a totals mismatch). Empty when all checks pass. |
mean_confidence | number | How well the page could be read: average OCR text confidence. 1.0 on any born-digital PDF — says nothing about field quality. |
extraction_confidence | number | How confident the extraction is: mean of the per-field confidences, after validation. null when no fields were found. |
retention | object | Always { stored: false, policy: "zero-retention" } — proof nothing was persisted. |
/v1/classify
https://api.zerodoc.io/v1/classifyDetect a document’s type so you can route it automatically. Returns the best match from invoice, credit_note, receipt, purchase_order, bank_statement, delivery_note — or an honest other when the evidence is too weak, rather than a guess.
Takes the same file / url input as /v1/extract, and shares the response envelope (request_id, status, document, retention, …). The response is deliberately lean — no document text or blocks — so it stays cheap to route on.
Request example
curl -X POST "https://api.zerodoc.io/v1/classify" \
-H "X-API-Key: zk_your_api_key" \
-F "file=@document.pdf;type=application/pdf"Response
{
"request_id": "req_9fe2...",
"status": "ok",
"processing_time_ms": 241,
"document": { "mime_type": "application/pdf", "page_count": 1, "size_bytes": 18240 },
"doc_type": "invoice",
"confidence": 0.7,
"candidates": [
{ "doc_type": "invoice", "score": 0.7 },
{ "doc_type": "receipt", "score": 0.3 }
],
"retention": { "stored": false, "policy": "zero-retention" }
}| Field | Type | Description |
|---|---|---|
doc_type | string | Best-matching type, or other when the evidence is too weak to name one. |
confidence | number | Evidence share (0–1) behind the top type. A routing signal, not a calibrated probability. |
candidates | array | Every type with any evidence, best first, each with doc_type and score. |
/v1/split
https://api.zerodoc.io/v1/splitDetect document boundaries in a multi-page upload — one PDF containing several documents comes back as segments, each with its page range and type. Boundaries are found from page markers (“Page 1 of 3”), document numbers changing between pages, and per-page type changes; ties keep pages together rather than cutting a document in half.
Takes the same file / url input as /v1/extract, and shares the response envelope (request_id, status, document, retention, …). Feed each segment’s pages to /v1/extract to get its fields.
Request example
curl -X POST "https://api.zerodoc.io/v1/split" \
-H "X-API-Key: zk_your_api_key" \
-F "file=@batch.pdf;type=application/pdf"Response
{
"request_id": "req_41bd...",
"status": "ok",
"processing_time_ms": 690,
"document": { "mime_type": "application/pdf", "page_count": 5, "size_bytes": 88410 },
"segments": [
{ "segment": 0, "pages": [0, 1], "page_count": 2, "doc_type": "invoice", "confidence": 0.82 },
{ "segment": 1, "pages": [2], "page_count": 1, "doc_type": "receipt", "confidence": 1.0 },
{ "segment": 2, "pages": [3, 4], "page_count": 2, "doc_type": "invoice", "confidence": 0.9 }
],
"retention": { "stored": false, "policy": "zero-retention" }
}| Field | Type | Description |
|---|---|---|
segments | array | One entry per detected document, in page order. |
segments[].pages | array | 0-indexed page numbers belonging to this document (matches blocks[].page on /v1/extract). |
segments[].doc_type | string | Classification of the segment’s own text (same types as /v1/classify, or other). |
segments[].confidence | number | Evidence share behind the segment’s doc_type. |
/v1/crop
https://api.zerodoc.io/v1/cropDigitize several documents scanned on a single page — e.g. three receipts on one flatbed A4. Each detected item is returned as its own image, positioned and classified, ready to process individually.
Takes the same file / url input as /v1/extract, and shares the response envelope (request_id, status, document, retention, …). Accepts images and PDFs only (text/plain is rejected — there are no pixels to crop). Items need readable text and whitespace between them to be detected.
Request example
curl -X POST "https://api.zerodoc.io/v1/crop" \
-H "X-API-Key: zk_your_api_key" \
-F "file=@flatbed-scan.png;type=image/png"Response
{
"request_id": "req_efb3...",
"status": "ok",
"processing_time_ms": 14356,
"document": { "mime_type": "image/png", "page_count": 1, "size_bytes": 402133 },
"items": [
{ "item": 0, "page": 0,
"bbox": { "x": 0.04, "y": 0.03, "width": 0.38, "height": 0.21 },
"doc_type": "receipt", "confidence": 1.0,
"image_mime_type": "image/png", "image_base64": "iVBORw0KGgo..." },
{ "item": 1, "page": 0,
"bbox": { "x": 0.04, "y": 0.67, "width": 0.33, "height": 0.17 },
"doc_type": "invoice", "confidence": 1.0,
"image_mime_type": "image/png", "image_base64": "iVBORw0KGgo..." }
],
"retention": { "stored": false, "policy": "zero-retention" }
}| Field | Type | Description |
|---|---|---|
items | array | One entry per document found, in reading order. |
items[].bbox | object | Where the item sits on its source page (normalized 0–1). |
items[].doc_type | string | Classification of the item’s own text (same types as /v1/classify). |
items[].image_base64 | string | The cropped item as a base64-encoded PNG — returned inline, never stored. |
Errors
Errors use standard HTTP status codes and return a JSON body with a detail field explaining what went wrong. Processing failures (500) include a request_id you can quote to support.
| Status | Meaning |
|---|---|
| 400 | No document provided, or the URL could not be fetched / was rejected. |
| 401 | Missing or invalid API key. |
| 413 | Document exceeds the maximum size (15 MB). |
| 415 | Unsupported media type. |
| 422 | Unsupported doc_type. |
| 429 | Rate limit or monthly page quota exceeded — retry after the Retry-After header. |
| 500 | Processing failed. The body carries a request_id — quote it to support. |
Supported documents
Field extraction currently supports UK & EU invoices; more extraction types are on the roadmap. Classification (/v1/classify) already recognises receipts, purchase orders, bank statements, credit notes and delivery notes for routing.
Zero-retention guarantee
Documents are received only by our EU processing service, held in memory for the duration of the request, and discarded immediately afterwards. No document or extracted content is written to disk or logged. Every response carries a retention block as proof. See the Security page for the full data flow and sub-processors.