Developer API

A clean API for clean Markdown.

One POST turns a PDF or image into LLM-ready Markdown and a structured JSON payload. Sync for small docs, async with live progress over SSE for big ones - and zero document-retention on every call, EU-hosted.

See API plans

API access is included on every paid plan (from €4.95/mo). New accounts get 100 free pages to try HexRead on the web first.

Sync & async

Small docs return Markdown inline (200). Large ones return a job (202) you poll or stream over SSE.

Built to script

Idempotency keys, stable error codes with a request_id, RateLimit-* headers, and an OpenAPI 3.0.3 contract you can codegen from.

Zero document retention

Your document streams through memory and is deleted right after conversion - no document backups, never training data.

Base URL & authentication

The API is a plain HTTPS + multipart service. Authenticate every request with a bearer API key.

Base URL https://api.hexread.com/v1
Auth header Authorization: Bearer hr_live_…

Mint a key from your account (or POST /v1/keys) - the full secret is shown exactly once, so store it like a password. Keys carry least-privilege scopes (convert, jobs:read, usage:read). API keys require a paid plan; a missing or invalid key returns 401.

Quickstart

Sign in, mint a key, then pick your interface:

curl
# Small docs return Markdown inline (200); large ones return a job (202).
curl -fsS https://api.hexread.com/v1/convert \
  -H "Authorization: Bearer $HEXREAD_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -F file=@report.pdf \
  -F model=auto \
  -F formats=md,json | jq -r .markdown
CLI
curl -fsSL https://hexread.com/install | sh   # cosign-verified
hexread login                                     # device grant → OS keychain
hexread convert report.pdf -o report.md           # sync/async auto; live progress
hexread batch "invoices/*.pdf" -o out/ --concurrency 4

Convert options

  • file The PDF or image to convert (required).
  • model auto (recommended - routes by content), mineru-2.5 (the default when omitted), granite-docling, or paddleocr-vl.
  • formats Comma-separated: md, json. Default md,json.
  • prefer sync or async - force the path regardless of document size.
  • Idempotency-Key Header (≤255 chars) - safe retries; a replay returns Idempotency-Replayed: true.

Convert returns clean Markdown plus a JSON payload of per-page Markdown and conversion metadata (model, page count, checksum, timing) - not a raw layout tree.

Endpoints

Convert

  • POST /v1/convert Upload a PDF or image; get Markdown + JSON inline (small docs) or a job handle (large ones).

Jobs

  • GET /v1/jobs/{id} Async job status - metadata only, never document content.
  • GET /v1/jobs/{id}/events Server-sent progress stream, page by page.
  • GET /v1/jobs/{id}/result Read-once result (also /markdown, /json) - purged the moment you fetch it.
  • DELETE /v1/jobs/{id} Cancel a queued or in-flight job.

Keys

  • GET /v1/keys List your API keys (masked - the secret is never returned again).
  • POST /v1/keys Mint a key - the full secret is shown exactly once.
  • POST /v1/keys/{key_id}/rotate Rotate - old key revoked, new secret shown once.
  • DELETE /v1/keys/{key_id} Revoke a key, effective immediately.

Usage & meta

  • GET /v1/usage Current-period pages + concurrency meters. No document data.
  • GET /v1/version Service version and the selectable model registry.
  • GET /v1/openapi.json This API as a machine-readable OpenAPI 3.0.3 document.

The complete, machine-readable contract - every parameter, response, and error code - is served at GET /v1/openapi.json (OpenAPI 3.0.3). Point your generator at it to build a client in any language.

Errors

Every non-2xx response is the same JSON envelope. Branch on the stable code (never the message), and log the request_id.

Error envelope
{
  "type": "payload_too_large",
  "code": "file_too_large",
  "message": "The uploaded file exceeds the size limit for your plan.",
  "request_id": "req_98fca6c87faace9b"
}

Each code maps to a fixed HTTP status and type - e.g. quota_exceeded (402), rate_limited (429), file_too_large (413), unsupported_media_type (415). The request_id is also returned as the X-Request-Id header for support.

Rate limits, quota & headers

Starter 60 req/min
  • Burst 120
  • 500 pages / mo
  • 2 concurrent conversions
  • Up to 100 MB · 500 pages / doc
Pro 120 req/min
  • Burst 240
  • 3,000 pages / mo
  • 3 concurrent conversions
  • Up to 100 MB · 1,000 pages / doc
Power 240 req/min
  • Burst 480
  • 10,000 pages / mo
  • 5 concurrent conversions
  • Up to 200 MB · 1,500 pages / doc
  • RateLimit-Limit / -Remaining / -Reset Your window: ceiling, calls left, seconds to reset (IETF draft).
  • RateLimit-Policy The active policy, e.g. 120;w=120 (120 requests per 120 s).
  • X-HexRead-Quota-Pages-Remaining Whole pages left in your monthly allowance.
  • Retry-After Seconds to wait before retrying, sent on every 429.
  • X-Request-Id Echoes the envelope request_id - quote it in support requests.

On 429, honour Retry-After and back off exponentially, then retry with the same Idempotency-Key. Need higher limits or a pooled team allowance? Compare plans or talk to us about Custom.

Versioning & clients

Stable /v1

Versioned in the path. Changes are additive-only; anything breaking ships as /v2. Pin to /v1 - there's no dated version header to track.

OpenAPI 3.0.3

Generate a typed client for your stack from /v1/openapi.json - every parameter, response, and error code, straight from the contract.

Command-line

A cosign-verified Go hexread CLI - install with curl -fsSL https://hexread.com/install | sh.

Build with HexRead.

Start free on the web in seconds, then add a paid plan to drop in the API when you're ready to automate.

Compare plans