DocsAPI reference

OpenAPI specification

Model IDs and options in examples are illustrative. Select an available model from GET /v1/models and check its capabilities and current prices.

POST /v1/ocr

Extracts text from a document as markdown. Billed per page processed rather than per token, so no token count applies.

Accepted: PDF, images (jpg, png, heic, webp, gif, avif, tiff) and office documents (pptx, docx, xlsx). An image is read as one page, and a multi page tiff as one page each. Anything else is refused with unsupported_format rather than returning an empty result.

Example

curl -X POST https://relayon.app/v1/ocr \
  -H "Authorization: Bearer ro_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "ro-ocr-latest",
    "document": {"type": "document_url", "url": "https://example.com/report.pdf"}
  }'

Three ways to send a document

document.type is document_url for a PDF orimage_url for a png, jpg, webp or gif. The document itself can arrive as a public URL, as base64 in the same field, or as the id of something you uploaded to /v1/files first.

"document": {"type": "document_url", "url": "https://example.com/report.pdf"}

"document": {"type": "document_url", "url": "data:application/pdf;base64,JVBERi0x..."}

"document": {"type": "file_id", "file_id": "file_8f96360be4d2a854b97d06e6b3c8142e"}

Inline base64 is capped at 20MB; anything larger belongs in an upload.

Reading part of a document

pages takes zero indexed page numbers and reads only those, for PDFs. You are billed for the pages you select, not for the document they came from, so pulling three pages out of a 248 page textbook costs three pages. A page number past the end fails rather than being quietly skipped.

{
  "model": "ro-ocr-latest",
  "document": {"type": "file_id", "file_id": "file_59e7b8060cbcb8ded18eb3a9035db5ba"},
  "pages": [0, 1, 2]
}

table_format and include_images are optional and not every engine can honour them; one that cannot rejects the request rather than ignoring it silently and billing you for a result you did not ask for.

Response

{
  "model": "ro-ocr-latest",
  "pages": [
    {"index": 0, "markdown": "# Quarterly Report\n\n..."}
  ],
  "usage": {"pages_processed": 29}
}

usage.pages_processed is what you are billed for. Thepages array is what the engine could return, and the two can differ: an engine that reads a whole document at once returns a single entry carrying all of it while still reporting the true page count. A page selection comes back the same way, as one entry holding the pages you asked for rather than one entry each.

Error codes

Detailed provider failures are available to admins in Diagnostics. The API returns RelayOn error codes and messages.

ParameterTypeDefaultDescription
encrypted_pdf422—Encrypted PDFs are unsupported. Upload an unencrypted PDF.
no_text_extracted500—OCR could not find readable text in the document
internal_error500—RelayOn could not complete the request; provider details are masked
upstream_unavailable503—The generation service is temporarily unavailable
upstream_connection_error502—The connection to the generation service failed
upstream_timeout504—The generation service did not respond before the deadline
no_capacity503—No OCR account slot is available right now; honour Retry-After
state_unavailable503—Request capacity is temporarily unavailable; honour Retry-After
unsupported_format / unprocessable_document422—Upload a supported, valid PDF or image
too_many_pages422—Split the document into smaller files
invalid_document400—The document input is malformed
document_too_large413 / 422—Over the size limit
insufficient_credits402—Balance too small for the estimate
guardrail_violation403—Account or workspace inference policy blocks the effective request
invalid_controls400—Invalid guardrail or routing settings
budget_exceeded402—Account, workspace or API key spending limit cannot cover the estimate

Encrypted PDFs are rejected before document upload to the OCR engine, including PDFs that open without a password but still use permission encryption. Uploading a file successfully only stores it; OCR validation happens when it is processed. This rejection is not retried, billed, or counted against service uptime.

{
  "error": {
    "message": "Encrypted PDFs are not supported. Please upload an unencrypted PDF.",
    "type": "invalid_request_error",
    "param": "document",
    "code": "encrypted_pdf"
  }
}

Limits

A document may be up to 512MB and 1,000 pages, however it arrives. A file over the size limit is refused with 413 before anything is sent for processing; one over the page limit is refused once the page count is known, and is not billed either way. Check the selected model’s metadata for any additional limits and supported extraction options.

Large documents: submit a job

A long document can take longer than an HTTP request should be held open, so pass async: true and the request returns 202 with a job to poll. Nothing about the document changes: all three shapes work either way.

curl -X POST https://relayon.app/v1/ocr \
  -H "Authorization: Bearer ro_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "ro-ocr-latest",
    "async": true,
    "document": {"type": "file_id", "file_id": "file_8f96360be4d2a854b97d06e6b3c8142e"}
  }'

# 202 Accepted
{"id": "ocrjob_4964f4174e0e248a5cc043e7f3b21ac5", "object": "ocr.job", "status": "queued", ...}

curl https://relayon.app/v1/ocr/jobs/ocrjob_4964f4174e0e248a5cc043e7f3b21ac5 \
  -H "Authorization: Bearer ro_live_YOUR_KEY"

status moves through queued, running and then succeeded or failed. A succeeded job carries the same result body the synchronous call returns; a failed one carries an error with the code, including encrypted_pdf, and is not billed. Polling a failed job returns HTTP 200; check status and error. A result is kept for 7 days.

Billing

Credits are held against an estimate from the document size before the request runs, then settled at the exact page count. A balance too small for the estimate returns 402 and nothing is sent for processing.