Mistral
Reads PDFs and images through Mistral AI’s Document AI endpoint (https://api.mistral.ai), returning per-page markdown and — on the same call — structured fields pulled out with a JSON schema you supply. One route is mounted:
Mistral’s API is a whole platform on one shared account key — chat and FIM completions, embeddings, Files, Batch, Fine-tuning, Models, Agents, billing and usage. Only the OCR endpoint above is mounted, as a fixed literal route rather than a wildcard, so every other Mistral path draws the gateway’s own 404 — /mistral/v1/ocr/ (trailing slash) and /mistral/ocr included, as is any method other than POST.
Files, Batch, chat and Document QnA are not exposed. Uploads (/v1/files) and the file_id handle they mint are never reachable: one shared Mistral key backs this route, so a file handle usable by one client would be usable by every client — send a URL instead. Batch, Fine-tuning, Agents, embeddings, and the billing/usage endpoints are unmounted for the same shared-account reason. Document QnA — Mistral’s /v1/chat/completions with a document reference — is out of scope here as well: it bills per token rather than per page. For chat inference, use the Inference API or the individual model routes.
Like Browserless and Steel, this route is policed: a closed body policy runs before Mistral is called, and the request the gateway forwards is rebuilt from the fields that policy admits — nothing can ride along inside a nested object. Two things make it stricter still. pages is required, because cost scales with the document; and the price of the selection is checked against your balance before the upstream call, so an unaffordable job is refused rather than forwarded.
Credential handling
Authorization: Beareris injected server-side with the gateway’s Mistral key. Your gateway token never goes upstream.- The upstream request is built from a fresh two-header envelope —
authorizationandcontent-type, nothing else. Your header bag is dropped wholesale rather than filtered, so no Mistral key, account or project selector, or cost-affecting header of your own reaches the upstream. - Query parameters are rejected outright. Every control travels in the JSON body.
Request policy
The body is a closed allow-set of at most 16,384 bytes. Fields not named below are rejected with a 400 before Mistral is called.
The document reference
Mistral fetches the document itself, from its own network, at the URL you give it. Two of Mistral’s three chunk types are admitted:
Both URLs must be public https — signed, time-limited links are the intended use. Refused: any other scheme (including http and inline data:), an embedded user:password, an IP-literal host in any spelling, and a single-label or .localhost/.local/.internal hostname. document_name is an optional label Mistral echoes back: a plain filename of at most 256 characters, no spaces or path separators.
Selecting pages
pages is required and zero-based — page one of a PDF is index 0. Both of Mistral’s spellings work:
A request may select at most 100 unique pages, each index at most 9999. A repeated or overlapping selection is refused rather than quietly deduplicated, and so is a descending range or an empty selection — a request should cost exactly what it looks like it costs. Mistral’s own default is the entire document, which has no cost ceiling; naming the pages up front is what lets the gateway price the call before running it.
Denied — a 400 before the request reaches Mistral: image_limit and image_min_size (they expand the response with image bytes the gateway neither meters nor buffers), the word value for confidence_scores_granularity, a file_id document chunk, an aliased model such as mistral-ocr-latest, any query parameter, and anything else the policy doesn’t name. include_blocks and include_image_base64 are always forwarded as false; asking for true is rejected rather than silently rewritten.
A rejected call answers 400 with {"error": "Rejected: <reason>."} — Mistral never sees it, and it’s never billed.
OCR a document
Mistral’s answer comes back exactly as it arrived — status, headers, and body bytes:
images[] carries each detected figure and its bounding box; image_base64 is always null, since the gateway forwards include_image_base64: false.
Structured extraction
Add an annotation format and the same pass also returns fields shaped by your JSON schema — document_annotation_format for the document as a whole, bbox_annotation_format for each detected figure. Each format takes Mistral’s own shape (type: "json_schema" plus a json_schema object carrying name, schema, and optionally description and strict) and may be at most 8,192 bytes serialized.
The extracted fields arrive in document_annotation (and per-figure results in each page’s images[].image_annotation), alongside the page markdown. json_schema.name is 1–64 characters of letters, digits, _, or -.
Either annotation format prices the whole call at the Document AI rate below. document_annotation_prompt on its own does not — it steers an annotation pass, it doesn’t request one.
Billing
Billed per page, not per call, on Mistral’s own published prices:
The annotated rate replaces the OCR rate for that call; the two never stack. Before the upstream request, the selected page count times the applicable rate is compared with your remaining balance — a job that doesn’t fit answers 402 naming the price and the page count (Insufficient credits for this request: 12000 µc for 3 selected page(s). Top up or select fewer pages.) and is never forwarded. A rejected or refused call is never charged.
Settlement happens only on a 2xx, from the usage_info.pages_processed Mistral itself reports — so a document whose selected pages turn out to be fewer than requested is charged for what was processed.
There is no retry on this route. Mistral documents no idempotency key for the OCR endpoint, so a silent second attempt could OCR — and bill — the same document twice. Retrying after a failure or timeout is your call, and counts as a new request.
Privacy
OCR input tends to be contracts, invoices, IDs, and medical records, so both log records this route writes keep metadata only — method, path, headers, status, and latency. Request and response bodies are never stored, which means neither the document URL (it travels in the body) nor a line of extracted text lands in a log. Usage records keep the operation, page counts, and rate — never document content.
Field-level request and response detail lives in Mistral’s OCR documentation; the gateway changes neither beyond the request policy described above.