OCRQueen API
Quickstart
60 seconds from API key to typed JSON.
Quickstart
1. Get a key
Sign in at ocrqueen.com → open API keys → click New key. The plaintext is shown once — copy it.
2. Submit a document
curl https://api.ocrqueen.com/v1/extract \
-H "Authorization: Bearer pk_test_xxx" \
-F "file=@invoice.pdf"You get back a job, queued for extraction:
{
"job_id": "job_a3f9c2",
"status": "queued",
"status_url": "https://api.ocrqueen.com/v1/jobs/job_a3f9c2",
"estimated_seconds": 12
}3. Poll for the result
curl https://api.ocrqueen.com/v1/jobs/job_a3f9c2 \
-H "Authorization: Bearer pk_test_xxx"Once status is completed (1-30s for most docs), the response carries the full document + a markdown render. Drop the markdown straight into an LLM prompt, or walk the typed blocks yourself.
Skip the polling — use ?sync=true
For documents that finish quickly (the common case), add sync=trueand we'll hold the connection open up to 25 seconds. If the worker finishes in time you get the completed result inline — one HTTP round-trip instead of two. If the doc runs long, the response falls back to 202 with a Retry-After header and you poll as usual.
curl https://api.ocrqueen.com/v1/extract \
-H "Authorization: Bearer pk_test_xxx" \
-F "file=@invoice.pdf" \
-F "sync=true"
# → 200 OK with the full { document, markdown, ... } inline.Our SDKs enable sync by default and fall back to polling for long docs automatically — you write client.extract("invoice.pdf") and never think about it. See Python and Node / TypeScript.
That's it. Two endpoints, one optional polling loop. SDKs ship in Python and Node — see the cookbooks for the patterns customers reach for most.
Authentication
Pass your key as a bearer token on every request:
Authorization: Bearer pk_test_xxxTwo prefixes:
pk_test_*— sandboxed. Real extraction runs, no charges, useful for CI.pk_live_*— production. Real billing applies.
Keys are shown exactly once at creation. Lost it? Revoke + reissue— there's no recovery flow.
Response shape
A completed job carries document.pages[].blocks[] — every block has a stable type, bbox (normalized 0..1), and text_source so you can trust the provenance of every character.
{
"job_id": "job_a3f9c2",
"status": "completed",
"document": {
"source": { "kind": "pdf", "filename": "invoice.pdf", "page_count": 2 },
"pages": [
{
"number": 1,
"blocks": [
{
"type": "heading",
"level": 1,
"text": "Invoice #INV-3034",
"bbox": [0.08, 0.04, 0.62, 0.09],
"text_source": "pdf_text_layer"
},
{
"type": "paragraph",
"text": "Vendor: Acme Corp",
"text_source": "pdf_text_layer"
}
]
}
]
},
"markdown": "# Invoice #INV-3034\n\nVendor: Acme Corp\n..."
}text_source tells you where the text came from: pdf_text_layer and pptx_native are byte-perfect; llm_vision_ocr is probabilistic — pair with confidence for high-stakes use.
Idempotency
Send a unique Idempotency-Key header on POST /v1/extract and retrying the same key returns the original job — no re-extraction, no double billing. Critical for at-least-once delivery from job queues and retry wrappers.
curl https://api.ocrqueen.com/v1/extract \
-H "Authorization: Bearer pk_test_xxx" \
-H "Idempotency-Key: invoice-3034-2026-05-14" \
-F "file=@invoice.pdf"On a replay, the response carries Idempotent-Replayed: true so you can distinguish it from a fresh extraction in logs and metrics.
- Keys are scoped to your account. Two customers can use the same key string without colliding.
- The first call wins. If you retry with the same key but a different file, you still get the original job back — the idempotency contract is the key, not the body.
- Use any stable string, 1-255 chars. A UUID, your internal job ID, or
sha256(file)-timestampall work. Don't reuse keys across logically distinct calls.
Both SDKs accept it as a per-call option:
result = client.extract(
"invoice.pdf",
idempotency_key="invoice-3034-2026-05-14",
)const result = await client.extract("invoice.pdf", {
idempotencyKey: "invoice-3034-2026-05-14",
});Errors
Every error uses the same envelope:
{
"error": {
"code": "PDF_PASSWORD_PROTECTED",
"message": "Cannot extract from a password-protected PDF.",
"request_id": "req_xxx"
}
}The codes you're most likely to see:
INVALID_API_KEY— bad/missing/revoked keyUNSUPPORTED_FILE_TYPE— only PDF, PPTX, PPT are acceptedFILE_TOO_LARGE— max 100 MBPDF_PASSWORD_PROTECTED— strip the password firstRATE_LIMITED— back off, retry
Rate limits
Per API key, per minute (fixed window):
- New account (no usage yet, no funds) — 10 req/min
- Any account that has used a free page or topped up — 60 req/min
- Enterprise — custom (talk to us)
Every response carries X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset (seconds until the window resets). A 429 means slow down; need more headroom? Talk to us.
Ready for real workflows? Read the cookbooks — RAG ingest, patent extraction with profile=advanced, and batch processing with webhooks.
Questions? Email support@ocrqueen.com.
