Deep-OCR/API Reference

API Reference

Deep-OCR extracts structured JSON from supported document types or full text as Markdown. Processing is asynchronous: submit a file with a document type to POST /v1/extractions, then poll for its status and retrieve the result when processing completes. You need an API key and an HTTP client; no SDK is 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_…) — processing continues in the background.
  2. Poll GET /v1/extractions/{id} until status is completed or failed. Wait between requests as specified by the Retry-After header.
  3. Read the result from the result field once status is "completed".
REST — any language
JSON responses
Asynchronous processing
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. Accepted requests return 202 Accepted with an extraction ID beginning with ext_…. Poll that ID until processing completes or fails. For completed extractions, 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, DOCX, DOC, ODT, RTF. The upload limit is approximately 100 MB and the document limit is approximately 350 pages. Word-processing files over ≈50 MB are rejected asynchronously by the converter. Word-processing files are queued immediately and converted to PDF by the async worker; their page count appears after conversion and uses the converted PDF.
document_typestringrequiredRequired extraction type. Use 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 processing completes or fails (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 returns, when the request is accepted, 202 Accepted with an extraction ID beginning with ext_… — processing continues in the background. Poll that ID until processing completes or fails. For completed extractions, read the result field.

Poll until processing completes or fails

# 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

Check the processing status

Poll GET /v1/extractions/{id} with the same API key. While the status is queued or processing, wait the number of seconds specified by Retry-After before requesting an update.

When the status is completed, read the extracted data from result. If it is failed, read error.code and error.message. Both states return HTTP 200, so check the processing status before using the result.

Choose an extraction type

The document_type form field is required. Choose a predefined document type, use auto for classification, or select generic for an adaptive structured result.

Use fulltext for Markdown. This mode counts as 10 billed pages per document page. Read metadata.pages_billed for the billed page count.

File limits

Uploads can contain approximately 350 pages and be up to 100 MB. Word-processing files (DOCX, DOC, ODT, RTF) have a separate conversion limit of approximately 50 MB, checked during background processing.

Retrieve results before they expire

Completed and failed extractions remain available for about one hour. After that, the ID returns HTTP 404. Save the result in your system during this window; otherwise, you will need to submit the document 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 costs 10 billed pages per document page. 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. Fulltext costs 10 billed pages per document page. 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 selects the extraction format. Predefined structured types use a fixed schema; generic adapts to the document and fulltext returns Markdown. For predefined types, the returned content object follows the schema, with unavailable values represented as 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.
If you already know the document type, specify it directly to skip classification.

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. Fulltext costs 10 billed pages per document page.

Field Reference

Monetary values are returned as numbers (e.g. 583.10). When identified, the currency is provided separately in the currency field as an ISO 4217 code (e.g. EUR). Dates are ISO 8601 YYYY-MM-DD. For predefined structured types, unavailable field values are 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 a machine-readable code and a message describing the failure.

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, ≈50 MB for word-processing files) 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 (≈1 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

Document submission limits apply per API key and plan. Exceeding a limit returns HTTP 429. Polling has a separate allowance; follow Retry-After between polls. Additional protective limits may also apply.

PlanSubmissions / minute
Free Trial10
Starter30
Pro60
Business120
Retry strategy: wait at least as long as Retry-After specifies. If it is absent, use exponential back-off starting at 1 s, doubling up to a maximum of 30 s. Add random jitter (±200 ms) to avoid sending retries at the same time as other clients.

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 in your workflows without writing integration 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

Import the Postman collection and environment to try the API requests. Set your API key in the environment before sending a request.

Integrate with an AI coding assistant

Building with Claude Code, Cursor, GitHub Copilot, or another AI assistant? We publish a machine-readable API summary at /llms.txt — a Markdown reference with endpoints, authentication details, and examples. Use it as context for generated code, then review and test the integration.

Example prompt for your coding assistant:

"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 convention for providing documentation in a format AI tools can read.

Ready to start building?

Create an account, generate an API key in the Developer Center, and follow the quickstart example to submit your first document.

See current pricing for plans and included usage.