Public Beta: Intelligent document OCR — built in Finland, EU-hosted. Start free → We'd love your feedback!

eScanX API Reference

Everything you need to integrate our intelligent document OCR API into your applications.

Base URL
https://api.escanx.com
API Version
v1
Content Type
Responses are JSON. File uploads use multipart/form-data.
All API endpoints are prefixed with /api/v1. Example: https://api.escanx.com/api/v1/extract/receipt

Authentication

Authenticate your API requests using the X-API-Key header. Get your API key from the dashboard after signing up.

Get your API key
Sign up for a free account and generate an API key from your dashboard.

Include your API key in every request:

# Sample: list async jobs (any authenticated endpoint works the same way)
curl https://api.escanx.com/api/v1/jobs \
  -H "X-API-Key: YOUR_API_KEY"
Keep your API key secret. Do not expose it in client-side code or public repositories.

API Playground

Try the extraction endpoint live. Pick a document type, paste your API key, upload a file, and run a real request.

This runs a real extraction against your account and consumes credits. Your API key is sent directly to the API over HTTPS and is never stored by this page.
Document type
Document file

Drag & drop a file or click to browse

JPEG, PNG, WebP, HEIC, or PDF · max 5 MB

Document Extraction

Extract structured data from documents (receipts, invoices, etc.) via our intelligent OCR engine.

POST/api/v1/extract/{document_type}
Requires API key authenticationWait window: 90 seconds
Document type

Request

Send one document as multipart/form-data. Optionally send an Idempotency-Key header so a retry collects the same extraction instead of billing twice.

ParameterTypeDescription
document_type*stringDocument type (path parameter): receipt, invoice, bank_statement, purchase_order, sales_order
file*fileDocument file (JPEG, PNG, WebP, HEIC, or PDF). Max 5 MB.
Idempotency-KeyheaderOptional. Up to 256 characters. A retried request resumes or collects the SAME extraction and is never billed twice.

Strict typing: the path names the document type you expect. A document of a different type is rejected with 422 — it is never reshaped. Use async jobs if you want a best-effort reshape instead.

curl -X POST https://api.escanx.com/api/v1/extract/receipt \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -F "file=@receipt.jpg"

Response

The extraction object is nested and document-type-specific — switch the document type above to see each shape.

{
  "success": true,
  "request_id": "req_abc123def456",
  "extraction_id": "ext_9f3c1a7b2d84",
  "processing_time_ms": 1250,
  "document_type": "receipt",
  "extraction": {
    "merchant": {
      "name": "Helsinki Market",
      "address": {
        "city": "Helsinki",
        "country_code": "FI"
      },
      "business_id": "1234567-8"
    },
    "buyer": {
      "name": "Aurora Digital Oy",
      "business_id": "7788990-1"
    },
    "transaction": {
      "date": "2025-01-15",
      "time": "14:30",
      "receipt_number": "A-4471"
    },
    "items": [
      {
        "line_number": 1,
        "description": "Coffee 250g",
        "quantity": 1,
        "unit_price": 4.9,
        "item_total": 4.9
      }
    ],
    "financial": {
      "subtotal": 43.1,
      "tax": [
        {
          "rate": 25.5,
          "amount": 4.75
        }
      ],
      "tax_total": 4.75,
      "total": 47.85,
      "currency": "EUR"
    },
    "payments": [
      {
        "method": "card",
        "card_type": "Visa",
        "card_last4": "4242",
        "amount": 47.85
      }
    ],
    "loyalty": {
      "card_number": "9012345678",
      "points_earned": 12,
      "points_balance": 340
    },
    "metadata": {
      "sub_type": "grocery",
      "confidence": {
        "overall": 0.97
      }
    }
  },
  "error": null,
  "credit_cost": 1,
  "item_count": 1
}
ParameterTypeDescription
successbooleanWhether the extraction succeeded
request_idstringUnique request identifier
processing_time_msnumberProcessing time in milliseconds
document_typestringDocument type that was processed
extraction_idstring | nullHistory ID for this extraction. Fetch the result again at GET /extractions/{extraction_id} for your retention window.
extractionobject | nullExtracted data — a nested object whose shape depends on document_type (e.g. receipt: merchant, transaction, items, financial, metadata; invoice: seller, invoice_details, …)
errorstring | nullError message if extraction failed
credit_costnumber | nullCredits charged for this extraction. Null on failure.
item_countnumber | nullBillable items extracted, per the billing unit for this document type (pages, or receipts in one image). Null on failure.

Errors on this endpoint

400Invalid file type, or a malformed request
402Insufficient credits
413File exceeds the maximum allowed size
422Document rejected — not a valid document, or the wrong type for this path
429Concurrency budget full (SYNC_EXTRACT_CAPACITY) — either your concurrent-extraction limit or the shared sync pool. Retry-After is set; nothing is recorded and nothing is charged.
503The extraction engine queue is full. Retry-After is set; nothing is recorded and nothing is charged.
504The 90-second wait window elapsed while the extraction was still running. The response body and the Location header carry extraction_id — poll GET /extractions/{extraction_id}, or retry with the same Idempotency-Key.

Extraction History

Every synchronous extraction is recorded. Use these endpoints to fetch a result again later, to recover after a 504, and to review what you have run.

Requires API key authentication

List Extractions

GET/api/v1/extractions

List your sync extractions. Same filters and response envelope as GET /jobs.

ParameterTypeDescription
statusstringFilter by status: processing, completed, failed, cancelled
document_typestringFilter by document type (e.g. receipt, invoice)
date_fromstringOnly extractions created on or after this date (ISO 8601)
date_tostringOnly extractions created on or before this date (ISO 8601)
pageintegerPage number (default: 1)
page_sizeintegerItems per page (default: 20, max: 100)
Status Values
processingJob is being processed
completedExtraction successful — results in extraction field
failedExtraction failed — error details in error field
cancelledJob was cancelled

Item fields

ParameterTypeDescription
extraction_idstringHistory ID — pass it to the detail endpoint
statusstringCurrent state of the extraction
document_typestringDocument type that was requested
original_filenamestringName of the uploaded file
created_atstringWhen the request was accepted
completed_atstring | nullWhen the extraction reached a terminal state
confidencenumber | nullOverall extraction confidence, when available
credit_costnumber | nullCredits charged. Null while running or on failure.
item_countnumber | nullBillable items extracted. Null while running or on failure.
processing_time_msnumber | nullProcessing time in milliseconds
expires_atstringWhen the record and the stored file are purged. Past it, this endpoint returns 404.
curl "https://api.escanx.com/api/v1/extractions?status=completed&page_size=20" \
  -H "X-API-Key: YOUR_API_KEY"

Get an Extraction

GET/api/v1/extractions/{extraction_id}

One extraction with its result, retrievable for your whole retention window. This is how you collect a result after a 504 from POST /extract. A still-running extraction is settled opportunistically on read; non-terminal responses carry Retry-After: 3 as a poll-cadence hint.

curl https://api.escanx.com/api/v1/extractions/EXTRACTION_ID \
  -H "X-API-Key: YOUR_API_KEY"

Extraction Statistics

GET/api/v1/extractions/stats

Aggregate counts of your sync extractions. Mirrors GET /jobs/stats.

ParameterTypeDescription
totalnumberTotal number of extractions
by_statusobjectExtraction counts keyed by status

Cancel an Extraction

POST/api/v1/extractions/{extraction_id}/cancel

Cancel only while the extraction is still queued at the engine. Closing the connection is not a cancel — the run completes into history and is billed normally.

200Cancelled. The result is discarded and nothing is charged.
409EXTRACTION_NOT_CANCELLABLE — the engine has already started, or the extraction is already in a terminal state.

Retention

Results and uploaded files are kept for your API retention window, editable in your organization settings and capped by your plan. expires_at is computed when you read it, so changing the setting applies to existing records immediately in both directions. Purged resources answer 404.

Async Jobs

Submit documents for asynchronous extraction. Ideal for large files or batch workflows where you don't need immediate results.

Requires API key authentication

Submit a Job

POST/api/v1/jobs/{document_type}

Submit 1-20 documents for background processing in one request. Returns 202 with one queued job per file.

ParameterTypeDescription
document_type*stringDocument type (path parameter): receipt, invoice, bank_statement, purchase_order, sales_order
files*file[]Document files (JPEG, PNG, WebP, HEIC, or PDF). Repeated multipart field, 1-20 per request; one job is created per file.
webhook_urlstringHTTPS URL to receive completion notifications, applied to every job in the batch. Sent as a query string parameter, not a form field.
Idempotency-KeyheaderOptional. Replays of the same key return the original response instead of creating a second batch.

All-or-nothing: every file is validated and the queue capacity for the whole batch is checked before anything is created. Any violation rejects the entire request and names the file at fault.

Jobs always reshape: document_type is the output shape you asked for, and a differently-typed document is read as what it is and returned in that shape. Strict type matching applies only to synchronous /extract.

curl -X POST https://api.escanx.com/api/v1/jobs/invoice \
  -H "X-API-Key: YOUR_API_KEY" \
  -F "files=@invoice-1.pdf" \
  -F "files=@invoice-2.pdf"

202 response

ParameterTypeDescription
acceptednumberNumber of jobs created - always equal to the number of files sent
itemsobject[]One entry per submitted file, in the order the files were sent
items[].job_idstringPoll this ID for the result
items[].statusstringAlways queued on submit
items[].document_typestringThe output shape this job will produce
items[].estimated_wait_secondsnumber | nullRough queue wait in seconds, when it can be estimated
items[].poll_urlstringReady-made URL for the status endpoint
items[].webhook_secretstring | nullPer-job signing secret, returned only when webhook_url was given. Shown once.

Errors on submit

400Invalid file, or a batch outside the 1-20 range. The response names the file at fault; nothing is created.
413A file exceeds the maximum allowed size
422Invalid webhook URL
429Queue full - TOO_MANY_QUEUED_JOBS (100 queued per user) or JOB_QUEUE_FULL (500 per organization). Both send Retry-After.

Check Job Status

GET/api/v1/jobs/{job_id}

Poll the job status until it reaches a terminal state.

Status Values
queuedJob is queued, waiting for an extraction slot
processingJob is being processed
completedExtraction successful — results in extraction field
failedExtraction failed — error details in error field
cancelledJob was cancelled
curl https://api.escanx.com/api/v1/jobs/JOB_ID \
  -H "X-API-Key: YOUR_API_KEY"

Terminal responses include expires_at. After that timestamp the record and the stored file are purged and this endpoint returns 404. The window is your API retention setting, editable in your organization settings.

Webhooks

Instead of polling, provide a webhook_url when submitting a job. Your endpoint will receive a POST request when the job completes or fails. The response includes a webhook_secret for verifying request signatures.

Requirements
URL must use HTTPS
URL must be publicly accessible (no private IPs)
Endpoint must respond with 2xx within 10 seconds
# Submit with webhook notification (webhook_url is a query parameter,
# applied to every job in the batch)
curl -X POST "https://api.escanx.com/api/v1/jobs/invoice?webhook_url=https://your-app.com/webhooks/escanx" \
  -H "X-API-Key: YOUR_API_KEY" \
  -F "files=@invoice-1.pdf" \
  -F "files=@invoice-2.pdf"

Store the webhook_secret returned in the job submission response. Use it to verify that incoming webhook requests are from eScanX.

List Jobs

GET/api/v1/jobs

List your extraction jobs with optional filters.

ParameterTypeDescription
statusstringFilter by job status: queued, processing, completed, failed, cancelled
document_typestringFilter by document type (e.g. receipt, invoice)
date_fromstringOnly jobs created on or after this date (ISO 8601)
date_tostringOnly jobs created on or before this date (ISO 8601)
pageintegerPage number (default: 1)
page_sizeintegerItems per page (default: 20, max: 100)

Cancel a Job

POST/api/v1/jobs/{job_id}/cancel

Cancel a job while it is still queued. Once processing starts the job runs to completion, and a late cancel returns 400 JOB_CANCELLATION_FAILED.

Retry a Job

POST/api/v1/jobs/{job_id}/retry

Retry a failed job. Bumps the extraction attempt and re-queues it. Wrong status or exhausted attempts returns 400 JOB_RETRY_FAILED.

Job Statistics

GET/api/v1/jobs/stats

Aggregate counts of your jobs and your remaining queued-job capacity.

ParameterTypeDescription
totalnumberTotal number of jobs
by_statusobjectJob counts keyed by status
queued_limitnumberMaximum number of jobs you may have queued at once
queued_remainingnumberRemaining queued-job slots
curl https://api.escanx.com/api/v1/jobs/stats \
  -H "X-API-Key: YOUR_API_KEY"

Usage Statistics

Check your API usage, quota, and remaining calls for the current billing period.

GET/api/v1/usage
Requires API key authentication
ParameterTypeDescription
daysintegerNumber of days to include in the statistics window (default: current billing period)
curl "https://api.escanx.com/api/v1/usage?days=30" \
  -H "X-API-Key: YOUR_API_KEY"

Response

ParameterTypeDescription
api_key_idnumberID of the API key the stats are for
api_key_namestring | nullLabel of the API key (null if unnamed)
period_startstringStart of the reporting period (ISO 8601)
period_endstringEnd of the reporting period (ISO 8601)
total_requestsnumberTotal API requests in the period
successful_requestsnumberNumber of successful extractions
failed_requestsnumberNumber of failed extractions
success_ratenumberSuccess rate as a percentage
avg_processing_time_msnumber | nullAverage processing time in milliseconds (null if no requests)
quota_limitnumber | nullQuota limit for the period, if applicable
quota_usednumber | nullQuota consumed in the current period
quota_remainingnumber | nullQuota remaining in the current period

Health Checks

Monitor the API status with liveness and readiness endpoints. No authentication required.

Liveness Check

GET/health
No authentication required

Returns OK if the API server is running.

curl https://api.escanx.com/health

Readiness Check

GET/health/ready
No authentication required

Returns OK if the API and all dependencies (database) are ready to serve requests.

curl https://api.escanx.com/health/ready

Error Handling

The API uses standard HTTP status codes. Error responses follow the RFC 7807 problem+json format with flat fields.

Error Response Format

{
"type": "about:blank",
"title": "Bad Request",
"status": 400,
"detail": "The uploaded file type is not supported",
"error_code": "INVALID_FILE_TYPE",
"error_id": "err_abc123",
"instance": "/api/v1/extract/receipt",
"timestamp": "2026-01-15T12:00:00Z"
}
ParameterTypeDescription
detailstringHuman-readable error description
error_codestringMachine-readable error code
error_idstringUnique identifier for this error (useful for support)

Validation Errors (422)

Validation failures return a 422 with a detail array — one entry per invalid field, each with its location, message, and type.

{
"detail": [{
"loc": ["body", "file"],
"msg": "field required",
"type": "value_error.missing"
}]
}

HTTP Status Codes

400Bad Request — Invalid parameters or missing required fields
401Unauthorized — Missing or invalid API key
403Forbidden — the email associated with the API key is not verified
402Payment Required — insufficient credits for this extraction
404Not Found — The requested resource does not exist
409Conflict — the resource is no longer in a state that allows this action
413Payload Too Large — File exceeds the maximum allowed size
422Unprocessable Entity — Request body validation failed
429Too Many Requests — Rate limit exceeded. Check Retry-After header
500Internal Server Error — Something went wrong on our end
502Bad Gateway — Extraction service returned an error
503Service Unavailable — Extraction service is temporarily unavailable
504Gateway Timeout — Extraction service did not respond in time

Rate Limits

API requests are rate-limited per API key to ensure fair usage and platform stability.

How it works

Rate limits are applied per API key. When you exceed the limit, requests return HTTP 429.

The response includes a Retry-After header indicating how many seconds to wait before retrying.

Handling Rate Limits

Implement exponential backoff when you receive a 429 response.

async function extractWithRetry(file, documentType, maxRetries = 3) {
  const formData = new FormData()
  formData.append('file', file)

  for (let attempt = 0; attempt < maxRetries; attempt++) {
    const response = await fetch(`https://api.escanx.com/api/v1/extract/${documentType}`, {
      method: 'POST',
      headers: { 'X-API-Key': 'YOUR_API_KEY' },
      body: formData
    })

    if (response.status === 429) {
      const retryAfter = response.headers.get('Retry-After') || '5'
      await new Promise(r => setTimeout(r, parseInt(retryAfter) * 1000))
      continue
    }

    return await response.json()
  }
  throw new Error('Max retries exceeded')
}

SDKs & Libraries

Official SDKs for popular programming languages are coming soon. For now, use our REST API directly with any HTTP client.

Native SDKs for JavaScript and Python are in development and will be available soon.
Complete extraction example
# Upload an invoice and extract structured data
curl -X POST https://api.escanx.com/api/v1/extract/invoice \
  -H "X-API-Key: YOUR_API_KEY" \
  -F "file=@invoice.pdf"

# Check your API usage
curl https://api.escanx.com/api/v1/usage \
  -H "X-API-Key: YOUR_API_KEY"

Ready to Get Started?

Create a free account and start extracting data from documents in minutes.

Get Free API Key

We use cookies to improve your experience on our site. By using our site, you consent to cookies. Learn More