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.
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.
https://api.hexread.com/v1Authorization: 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:
# 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 .markdowncurl -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 4Convert options
fileThe PDF or image to convert (required).modelauto (recommended - routes by content), mineru-2.5 (the default when omitted), granite-docling, or paddleocr-vl.formatsComma-separated: md, json. Default md,json.prefersync or async - force the path regardless of document size.Idempotency-KeyHeader (≤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/convertUpload 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}/eventsServer-sent progress stream, page by page. - GET
/v1/jobs/{id}/resultRead-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/keysList your API keys (masked - the secret is never returned again). - POST
/v1/keysMint a key - the full secret is shown exactly once. - POST
/v1/keys/{key_id}/rotateRotate - old key revoked, new secret shown once. - DELETE
/v1/keys/{key_id}Revoke a key, effective immediately.
Usage & meta
- GET
/v1/usageCurrent-period pages + concurrency meters. No document data. - GET
/v1/versionService version and the selectable model registry. - GET
/v1/openapi.jsonThis 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.
{
"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
- Burst 120
- 500 pages / mo
- 2 concurrent conversions
- Up to 100 MB · 500 pages / doc
- Burst 240
- 3,000 pages / mo
- 3 concurrent conversions
- Up to 100 MB · 1,000 pages / doc
- Burst 480
- 10,000 pages / mo
- 5 concurrent conversions
- Up to 200 MB · 1,500 pages / doc
RateLimit-Limit / -Remaining / -ResetYour window: ceiling, calls left, seconds to reset (IETF draft).RateLimit-PolicyThe active policy, e.g. 120;w=120 (120 requests per 120 s).X-HexRead-Quota-Pages-RemainingWhole pages left in your monthly allowance.Retry-AfterSeconds to wait before retrying, sent on every 429.X-Request-IdEchoes 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.