Deep-OCR/API Reference

API Reference

The Deep-OCR API lets you extract structured data from any document with a single HTTP call. Submit a file with a document type to POST /v1/extractions, then poll the returned extraction id until it completes and read the typed JSON result. No configuration, no model training, no SDKs required.

How it works — three steps

  1. Submit your file to POST /v1/extractions with a document_type. You get 202 Accepted and an extraction id (ext_…) — never the result inline.
  2. Poll GET /v1/extractions/{id} until status is terminal, honouring the Retry-After header.
  3. Read the result from the result field once status is "completed".

Coming from /v1/ocr?

The older synchronous POST /v1/ocr endpoint is deprecated and will be removed on 31 July 2026. It returned the result inline; the current API submits the document and you poll for the result. Point your integration at POST /v1/extractions — the differences are small:

/v1/ocr (deprecated)/v1/extractions
200 + result inline202 + { id }, then poll
?document_type= query param (optional)document_type form field (required)
{ "detail": "…" }{ "error": { "code", "message" } }
REST — any language
Always returns JSON
GDPR
Quickstart — submit an invoice, then poll for the result
# Authenticate every request with your Bearer API key (see Authentication below).

# 1 · Submit — returns 202 Accepted with an extraction id
curl -sS -X POST "https://api.deep-ocr.com/v1/extractions" \
  -F "file=@invoice.pdf" \
  -F "document_type=invoice"
# → { "id": "ext_123", "status": "queued" }

# 2 · Poll until completed (honour the Retry-After header)
curl -sS "https://api.deep-ocr.com/v1/extractions/ext_123"
# → { "status": "completed", "result": { … } }

Authentication

All requests require an API key passed as a Bearer token in the Authorization header. You can create and manage keys in the .

Authorization: Bearer deep_ocr_sk_live_xxxxxxxxxxxxxxxxxxxx

Keep your API keys secret. Never commit them to version control. Use environment variables or a secrets manager in production.

Submit a document POST /v1/extractions

Submit a document for extraction. The endpoint accepts a multipart/form-data body and always replies 202 Accepted with an extraction whose id is ext_…. Poll that id until it reaches a terminal status and read the result field — see Poll for the result below.

Request Parameters

ParameterTypeRequiredDescription
filebinaryrequiredThe document file. Accepted formats: PDF, PNG, JPG, JPEG, WebP, TIF, TIFF. Up to ≈100 MB and ≈350 pages per document.
document_typestringrequiredExtraction schema to use — a required form field. Default to generic for a general schema; pass auto to let the API classify the document first. One of: auto, invoice, receipt, contract, id_document, delivery_note, bank_statement, payslip, purchase_order, handwriting, generic, fulltext. auto and fulltext cost more (see Document Types).

Code Examples

# Authenticate every request with your Bearer API key (see Authentication above).

# 1 · Submit the document — returns 202 Accepted with an extraction id
curl -sS -X POST "https://api.deep-ocr.com/v1/extractions" \
  -F "file=@invoice.pdf" \
  -F "document_type=invoice"
# → { "id": "ext_123", "status": "queued" }

# 2 · Poll until the status is terminal (honour the Retry-After header)
curl -sS "https://api.deep-ocr.com/v1/extractions/ext_123"
# → { "status": "completed", "result": { … } }

You'll get a 202 with an id — but not the data yet. Next: poll for the result →

Poll for the result GET /v1/extractions/{id}

Every document is processed as a background job. The POST /v1/extractions call always replies 202 Accepted with an extraction whose id is ext_… — never the result inline. Poll that id until it reaches a terminal status and read the result field.

Poll until the status is terminal

# Authenticate every request with your Bearer API key (see Authentication above).
# (You already have the id from the 202 response to Submit a document, above.)

curl -sS "https://api.deep-ocr.com/v1/extractions/ext_123"
# → { "status": "processing" }   (+ Retry-After: <seconds>)     # keep polling
# → { "status": "completed", "result": { … } }                  # success → read result
# → { "status": "failed",    "error":  { … } }                  # terminal failure

Polling contract

  • Poll GET /v1/extractions/{id} with the same Authorization header. Non-terminal statuses (queued, processing) mean keep polling — wait the Retry-After header (seconds) the server returns.
  • Success is gated on status === "completed" not on HTTP 200. A failed extraction is returned as HTTP 200 carrying status: "failed", so always check the status before using the body.
  • On a completed job the extracted data is under result — the shape shown under The result you get back below.

Parameters, limits & errors

  • document_type is a required multipart form field — one of the 12: auto, invoice, receipt, contract, id_document, delivery_note, bank_statement, payslip, purchase_order, handwriting, generic, fulltext. Default to generic (cheap general schema); auto runs an extra classification pass and fulltext defaults to ×10 on public plans; overage-enabled custom plans may bill 1:1. Use metadata.pages_billed as the source of truth.
  • Limits:up to ≈100 MB and ≈350 pages per document.
  • A failed extraction carries { "error": { "code", "message" } } where code is one of: payload_too_large, unprocessable, quota_exceeded, too_many_inflight, not_found, too_many_pages, upstream_unavailable, expired, worker_failed.

Retention. A finished extraction stays retrievable (HTTP 200) for about 24 hours after it terminates; once that window elapses the id returns 404 (there is no 410). Re-submit the document if you need it again.

The result you get back

When an extraction completes, the extracted data is carried in its result field. The shape of that result is consistent across all document types:

FieldTypeDescription
document_typestringThe document_type used for extraction — the value you passed in, or the resolved type when you sent 'auto'.
contentobjectThe extracted data — structure depends on document_type.
metadata.pagesnumberNumber of pages processed.
metadata.pages_billednumberPages billed against your monthly quota. Equals pages for structured schema modes. Fulltext defaults to 10 billed pages per document page on public plans; overage-enabled custom plans may bill fulltext 1:1. Treat this field as the source of truth.
metadata.processing_timenumberEnd-to-end processing time in seconds.

Example — Invoice result

{
  "document_type": "invoice",
  "content": {
    "vendor": {
      "name": "ACME Solutions GmbH",
      "address": "123 Example Street, 10115 Berlin",
      "tax_id": "DE123456789",
      "email": "billing@acme.de",
      "phone": null
    },
    "customer": {
      "name": "Your Company Ltd.",
      "address": "42 High Street, 20354 Hamburg",
      "customer_number": "CUS-0042"
    },
    "invoice_number": "RE-2024-0815",
    "invoice_date": "2024-03-01",
    "due_date": "2024-03-31",
    "currency": "EUR",
    "line_items": [
      {
        "description": "Software License Q1/2024",
        "quantity": 1,
        "unit_price": 490.00,
        "total": 490.00,
        "tax_rate": 19
      }
    ],
    "subtotal": 490.00,
    "tax_breakdown": [
      { "rate": 19, "net_amount": 490.00, "tax_amount": 93.10 }
    ],
    "tax_amount": 93.10,
    "total": 583.10,
    "is_reverse_charge": false,
    "payment_terms": "14 days net",
    "bank_details": {
      "iban": "DE89370400440532013000",
      "bic": "COBADEFFXXX",
      "bank_name": "Commerzbank"
    },
    "is_paid": false,
    "notes": null,
    "items_truncated": false
  },
  "metadata": {
    "pages": 1,
    "pages_billed": 1,
    "processing_time": 1.84
  }
}

Fulltext mode — verbatim Markdown

When you want the full readable text of a PDF or scanned document — including tables, headings, and structure — pass document_type=fulltext. The response shape stays the same except that content has exactly one key, markdown, holding the verbatim document text as Markdown. Headings, paragraphs, lists, and tables are preserved. Multi-page documents are joined with a \n\n<!-- page break -->\n\n sentinel so you can split visually if you want.

Quota usage

Fulltext mode consumes more compute than schema modes due to the higher token cost of verbatim transcription. Public plans default to 10 billed pages per document page. Custom overage plans may bill fulltext 1:1; use metadata.pages_billed as the source of truth.

Request

# Authenticate every request with your Bearer API key (see Authentication above).

# 1 · Submit with document_type=fulltext — returns 202 Accepted
curl -sS -X POST "https://api.deep-ocr.com/v1/extractions" \
  -F "file=@contract.pdf" \
  -F "document_type=fulltext"
# → { "id": "ext_123", "status": "queued" }

# 2 · Poll until completed, then read result.content.markdown
curl -sS "https://api.deep-ocr.com/v1/extractions/ext_123"
# → { "status": "completed", "result": { "content": { "markdown": "# …" } } }

Example — Fulltext result

{
  "document_type": "fulltext",
  "content": {
    "markdown": "# Service Agreement\n\n## 1. Scope of Services\n\nThis Agreement governs the cooperation between Party A and Party B for the provision of consulting and implementation services as set out below.\n\n| Item | Description | Quantity |\n|---|---|---|\n| 1 | Consulting services | 10 h |\n| 2 | Implementation | 40 h |\n\n<!-- page break -->\n\n## 2. Fees\n\nThe total fee amounts to EUR 5,000, payable within 14 days of the invoice date."
  },
  "metadata": {
    "pages": 3,
    "pages_billed": 30,
    "processing_time": 7.42
  }
}

Document Types

These are the schemas the result.content from step 3 conforms to — pick one as your document_type when you submit. Each document_type uses a pre-defined extraction schema. The returned content object always matches that schema — every field is present, unknown fields are null.

Auto-detect

Not sure which schema fits? Pass document_type=auto and the API classifies the document with a lightweight first pass, then applies the matching schema automatically. The detected type is returned in the document_type field of the result.
The classification pass costs more than a fixed schema — when you already know the type, name it directly (or default to generic) to skip it.

Invoiceinvoice
Vendor invoices, bills
Receiptreceipt
Purchase receipts, till receipts
Contractcontract
Agreements, service contracts
ID Documentid_document
Passports, ID cards, driving licences
Delivery Notedelivery_note
Packing slips, shipping docs
Bank Statementbank_statement
Account statements, transaction lists
Payslippayslip
Wage slips, salary statements
Purchase Orderpurchase_order
Orders placed with a supplier
Handwritinghandwriting
Handwritten notes, forms
Genericgeneric
Any other document type
Fulltext (Markdown)fulltext
Verbatim text as Markdown — use when you want the full document content, including tables, from a PDF or scanned page. Public plans default to 10 billed pages per document page; custom overage plans may bill 1:1.

Field Reference

Monetary values are returned as numbers (e.g. 583.10) so they can be used directly in calculations — the currency is always available separately in the currency field as an ISO 4217 code (e.g. EUR). Dates are ISO 8601 YYYY-MM-DD. Every field not found in the document is returned as null.

Invoiceinvoice
FieldTypeDescription
vendor.namestringCompany or person issuing the invoice
vendor.addressstringFull address of the vendor
vendor.tax_idstringVAT / tax ID of the vendor (e.g. DE123456789)
vendor.emailstringContact email of the vendor
vendor.phonestringContact phone of the vendor
customer.namestringName of the invoice recipient
customer.addressstringFull address of the recipient
customer.customer_numberstringCustomer number assigned by the vendor
invoice_numberstringUnique invoice identifier
invoice_datedateDate the invoice was issued (ISO 8601)
due_datedatePayment due date
performance_date_fromdateStart of the service/delivery period if stated separately from the invoice date
performance_date_todateEnd of the service/delivery period
purchase_order_numberstringBuyer's PO reference number if quoted on the invoice
currencystringISO 4217 code (e.g. EUR, USD)
line_items[]arrayOne entry per line item.
{ "description": "Software License Q1/2024", "quantity": 1, "unit_price": 490.00, "total": 490.00, "tax_rate": 19 }
subtotalnumberNet total before tax
tax_breakdown[]arrayOne entry per distinct VAT rate on the invoice.
{ "rate": 19, "net_amount": 490.00, "tax_amount": 93.10 }
tax_amountnumberTotal tax (sum of all tax_breakdown entries)
totalnumberGross total including tax
is_reverse_chargebooltrue if the recipient is liable for VAT instead of the vendor — common in cross-border EU B2B invoices ('reverse charge')
payment_termsstringPayment conditions as written on the document (e.g. 'Net 30', '2% 10 Net 30')
bank_details.ibanstringIBAN for bank transfer
bank_details.bicstringBIC / SWIFT code
bank_details.bank_namestringName of the bank
is_paidbooltrue if stamped/marked paid, false if marked unpaid, null if not indicated
items_truncatedbooltrue if the invoice had >50 line items (only first 50 extracted)
Receiptreceipt
FieldTypeDescription
merchant.namestringShop or business name
merchant.addressstringShop address
merchant.phonestringShop phone number
merchant.tax_idstringVAT / tax ID of the merchant
receipt_numberstringReceipt or transaction number
datedateDate of purchase (ISO 8601)
timestringTime of purchase in 24-hour format HH:MM
currencystringISO 4217 code
items[]arrayOne entry per purchased item.
{ "description": "Oat Milk 1L", "quantity": 2, "unit_price": 1.49, "total": 2.98, "tax_rate": 7 }
subtotalnumberNet total before tax
tax_breakdown[]arrayOne entry per distinct VAT rate on the receipt. German receipts often show both 7 % and 19 %.
{ "rate": 7, "net_amount": 2.80, "tax_amount": 0.20 }
tax_amountnumberTotal tax amount
totalnumberAmount paid including tax
payment_methodstringOne of: cash, credit_card, debit_card, mobile_payment, check, gift_card, store_credit, other
card_last_fourstringLast 4 digits of the card used as a string (e.g. '4242'), never asterisks
cashierstringName or ID of the cashier if shown
notesstringAny additional notes or remarks printed on the receipt
Contractcontract
FieldTypeDescription
titlestringTitle or heading of the contract
contract_numberstringContract reference number if present
parties[]arrayOne entry per contracting party.
{ "role": "service_provider", "name": "ACME Ltd.", "address": "123 Main St, London EC1A 1BB", "representative": "Jane Smith" }
parties[].rolestringOne of: buyer, seller, service_provider, service_recipient, lessor, lessee, employer, employee, licensor, licensee, other
parties[].namestringFull legal name of the party
parties[].addressstringAddress of the party
parties[].representativestringName of the person signing on behalf of the party
effective_datedateDate the contract enters into force
expiry_datedateEnd date; null for perpetual or indefinite contracts
termination_notice_daysnumberRequired notice period in days to terminate (longest value if multiple apply)
governing_lawstringISO 3166-1 alpha-3 country code (e.g. DEU) or country name as written
subject_matterstringOne-sentence summary of what the contract is about (max 30 words)
key_obligationsstring[]Up to 5 concrete obligations as short strings (max 15 words each).
["Provide IT consulting services 40 hours per week", "Pay monthly fee of €5,000 by the 15th"]
payment_termsstringPayment schedule or conditions
contract_valuenumberTotal monetary value of the contract
currencystringISO 4217 code
signatures[]arrayOne entry per signature block.
{ "party": "ACME GmbH", "signed_date": "2024-01-15", "signed_by": "Max Müller" }
signatures[].partystringName of the signing party
signatures[].signed_datedateDate of signature
signatures[].signed_bystringName of the individual who signed
notesstringAdditional observations or remarks about the contract
ID Documentid_document
FieldTypeDescription
id_typestringOne of: passport, national_id, id_card, driver_license, residence_permit, travel_document, other
issuing_countrystringISO 3166-1 alpha-3 code (e.g. DEU, AUT, CHE)
issuing_authoritystringAuthority that issued the document
document_numberstringDocument number exactly as printed
surnamestringFamily name
given_namesstringGiven name(s)
date_of_birthdateDate of birth (ISO 8601)
nationalitystringNationality as printed on the document
genderstringM or F; null for X / Divers / non-binary
place_of_birthstringPlace of birth if shown
issue_datedateDate of issue
expiry_datedateExpiry date; null if the document has no expiry
mrz_line1stringFirst machine-readable zone line, copied character-for-character (typically 44 chars)
mrz_line2stringSecond machine-readable zone line
Delivery Notedelivery_note
FieldTypeDescription
delivery_note_numberstringDelivery note reference number
order_numberstringCorresponding purchase or sales order number
delivery_datedateDate of delivery (ISO 8601)
sender.namestringSending company or person
sender.addressstringFull address of the sender
sender.contactstringContact person or department at the sender
recipient.namestringReceiving company or person
recipient.addressstringDelivery address
recipient.contactstringContact person or department at the recipient
carrierstringShipping carrier (e.g. DHL, UPS)
tracking_numberstringShipment tracking number, copied exactly as printed
items[].descriptionstringDescription of the delivered item
items[].article_numberstringArticle / SKU number
items[].quantity_orderednumberOriginally ordered quantity (null if not shown)
items[].quantity_deliverednumberActually delivered quantity
items[].unitstringUnit of measurement as written (e.g. pcs, Stück, kg)
total_packagesnumberTotal number of packages
total_weight_kgnumberTotal shipment weight in kg (converted from lbs if needed)
is_partial_deliverybooltrue if explicitly marked as a partial shipment, false if marked as complete, null if not stated
delivery_sequencestringShipment sequence if shown (e.g. '1/3' for first of three)
notesstringAdditional remarks or instructions on the delivery note
Bank Statementbank_statement
FieldTypeDescription
account_holderstringName of the account holder
account_numberstringAccount number as printed
ibanstringIBAN
bicstringBIC / SWIFT code
bank_namestringName of the bank
statement_datedateDate the statement was generated
period_fromdateStart of the statement period
period_todateEnd of the statement period
currencystringISO 4217 code
opening_balancenumberAccount balance at the start of the period
closing_balancenumberAccount balance at the end of the period
transactions[]arrayOne entry per transaction. Statements with more than 100 transactions: only the first 100 are extracted.
{ "date": "2024-03-15", "value_date": "2024-03-15", "description": "Direct debit rent March", "reference": "INV-2024-03", "amount": -950.00, "type": "debit", "balance": 2340.12 }
transactions[].datedateBooking date
transactions[].value_datedateValue date — the date the amount actually affects the balance
transactions[].descriptionstringFull transaction description / purpose text
transactions[].referencestringTransaction reference or booking text
transactions[].amountnumberPositive for credits (money in), negative for debits (money out)
transactions[].typestring'credit' if amount is positive, 'debit' if negative
transactions[].balancenumberRunning account balance after this transaction, if shown
Payslippayslip
FieldTypeDescription
employer.namestringEmployer company name
employer.addressstringEmployer address
employer.tax_idstringEmployer tax ID
employee.namestringEmployee full name
employee.employee_numberstringInternal employee number
employee.tax_idstringEmployee tax identification number (TIN)
employee.social_security_numberstringSocial security / national insurance number
employee.ibanstringIBAN of the employee's bank account for salary payment
period_fromdateStart of the pay period
period_todateEnd of the pay period
payment_datedateDate the salary was / will be paid
currencystringISO 4217 code
earnings[]arrayEach positive pay component (base salary, bonuses, allowances, overtime, etc.).
{ "description": "Base salary", "amount": 3500.00 }
deductions[]arrayEach amount subtracted (income tax, social security, pension, etc.). Amount is always a positive number.
{ "description": "Income tax", "amount": 650.00 }
gross_salarynumberTotal gross pay before any deductions
net_salarynumberTake-home pay after all deductions
tax_amountnumberIncome tax portion of deductions if shown separately
social_security_amountnumberTotal social security / national insurance contributions
notesstringAdditional remarks on the payslip
Purchase Orderpurchase_order
FieldTypeDescription
po_numberstringPurchase order number
order_datedateDate the order was placed
delivery_date_requesteddateRequested delivery date
buyer.namestringCompany or person placing the order
buyer.addressstringBuyer's address
buyer.contactstringBuyer contact person or department
buyer.tax_idstringBuyer's VAT / tax ID
supplier.namestringCompany or person receiving the order
supplier.addressstringSupplier's address
supplier.contactstringSupplier contact person
supplier.tax_idstringSupplier's VAT / tax ID
delivery_addressstringDelivery address if different from buyer address
currencystringISO 4217 code
line_items[]arrayOne entry per ordered item.
{ "position": 1, "description": "Laptop Dell XPS 15", "article_number": "DEL-XPS-15", "quantity": 2, "unit": "pcs", "unit_price": 1200.00, "total": 2400.00, "tax_rate": 19 }
line_items[].positionnumberLine item position number (1, 2, 3, …)
line_items[].descriptionstringDescription of the ordered item
line_items[].article_numberstringArticle / SKU number
line_items[].quantitynumberOrdered quantity
line_items[].unitstringUnit of measurement as written (e.g. pcs, Stück, kg)
line_items[].unit_pricenumberPrice per unit (net)
line_items[].totalnumberLine total (net)
line_items[].tax_ratenumberVAT rate as a percentage (e.g. 19)
subtotalnumberNet total before tax
tax_amountnumberTotal tax amount
totalnumberGross total including tax
payment_termsstringPayment conditions
notesstringAdditional remarks or terms printed on the purchase order
items_truncatedbooltrue if the PO had >50 line items (only first 50 extracted)
Handwritinghandwriting
FieldTypeDescription
transcriptionstringFull transcribed text. Unclear words marked with [?] (e.g. meetng[?]), single unclear characters as w[?]rd, illegible sections as [illegible – 2 lines]
confidencestringOverall readability: 'high' (>90% clearly readable), 'medium' (50–90%), 'low' (<50%)
languagestringISO 639-1 language code of the primary language (e.g. de, en, fr)
notesstringObservations such as mixed print/cursive, multiple writing styles, or damage
Genericgeneric
FieldTypeDescription
document_typestringModel's best-guess label for the specific document kind (e.g. 'form', 'letter', 'report') — more specific than the top-level 'generic'
titlestringDocument title or heading
datedatePrimary date found on the document
contentobjectAll extracted fields as a nested JSON object — structure adapts to the document. May contain tables (array of row objects), lists (array of strings), and any other relevant fields.
Fulltext (Markdown)fulltext
FieldTypeDescription
markdownstringThe verbatim document text as Markdown. Headings, paragraphs, lists, and tables are preserved. Multi-page documents are joined into a single Markdown stream with `\n\n<!-- page break -->\n\n` between pages so you can split visually if you want to.

Error Codes

There are two kinds of error. Transport errors — an invalid request, a missing key, or an unknown id — come back as the HTTP status codes below. A document that was accepted but could not be processed fails the extraction: the job reaches status: "failed" carrying { "error": { "code", "message" } } with the closed code set listed under Poll for the result (above).

400
Bad Request
File type not supported, MIME mismatch, or file too large.
401
Unauthorized
API key missing, invalid, or revoked.
413
Payload Too Large
File exceeds the maximum allowed size (≈100 MB) or the ≈350-page-per-document limit.
422
Unprocessable Entity
Required parameter missing (e.g. document_type) or an invalid document_type value.
429
Rate Limited
Too many requests. Respect the rate limits for your plan.
404
Not Found
The extraction id is unknown, or its retention window has elapsed (≈24 h after it terminates). Re-submit the document.
500
Internal Server Error
Unexpected server-side error. Retry with exponential back-off.
504
Gateway Timeout
The request timed out at the gateway. Retry the request.

Rate Limits

Rate limits are enforced per API key. Exceeding the limit returns HTTP 429. Limits reset every minute.

PlanRequests / minutePages / month
Free Trial10100
Starter30200
Pro601,000
Business1205,000
Retry strategy: implement exponential back-off starting at 1 s, doubling up to a maximum of 30 s. Add random jitter (±200 ms) to avoid thundering herd on shared plans.

Integrations

Use Deep-OCR with the tools you already know — from no-code automation to AI-assisted development.

n8n

Verified n8n node

Deep-OCR is a verified n8n community node, so you can automate document OCR inside any workflow without writing code. On n8n Cloud, add it directly from the node catalog — search for the Deep-OCR node on the canvas and drag it in. On self-hosted n8n, enable Verified Community Nodes in the Admin Panel (and restart the instance) to add it the same way.

You can also install the package manually:

npm install n8n-nodes-deep-ocr

Available on npmjs.com/package/n8n-nodes-deep-ocr

Postman Collection

Download the official Postman collection to explore and test every endpoint without writing any code. Includes pre-configured auth and example responses for all supported document types.

AI-ready — Vibe-code friendly

Building with Claude Code, Cursor, GitHub Copilot, or another AI assistant? We publish a machine-readable API summary at /llms.txt — a concise Markdown file containing everything your AI assistant needs to write a correct integration from scratch.

Paste into your AI assistant to get started instantly:

"Read https://deep-ocr.com/llms.txt and integrate Deep-OCR into my project"

The /llms.txt file follows the emerging llms.txt convention — a lightweight standard for making APIs and documentation easy for AI tools to consume.

Ready to start building?

Create a free account, grab your API key, and make your first request in under 5 minutes.

100 pages free · No credit card required