Skip to main content
Lesson 4 of 16 · Document AI: OCR and Document Intelligence

Mistral's OCR API

Lesson 4 of 16 Updated November 3, 2026 Developer
On this page
  1. A single entry point
  2. Installation and configuration
  3. Call parameters
  4. Three ways to send a document
  5. Reading the response
  6. Limits and errors
  7. Key takeaways

Updated November 2, 2026 — OCR. OCR 4.0 (mistral-ocr-4-0) was retired on September 30, 2026. Use OCR 4.1 (mistral-ocr-4-1), which has been generally available since August 31 at the same price, or the alias mistral-ocr-latest, which has pointed to it since July 16. OCR 4.1 adds confidence scores at the block level. Addendum November 3, 2026: To send a local PDF, encode it in base64 as {"type": "document_url", "document_url": "data:application/pdf;base64,…"}. The document_base64 type found in older examples does not exist in the documentation or the mistralai SDK, and those examples would have failed. Sources: OCR Processor · Document QnA.

A single entry point

All document extraction goes through a single endpoint: /v1/ocr, or its equivalent in the Python SDK, client.ocr.process(). Whether you are processing a photo of a receipt or an 800-page annual report, the call is the same. This lesson details how to configure it, the three ways to pass a document to it, and what it returns.

Installation and configuration

Installation takes a single command:

pip install mistralai

Next, instantiate the client with your API key:

from mistralai.client import Mistral

client = Mistral(api_key="your-api-key")

This approach works for a quick test in a notebook, but it risks accidentally committing your key. As soon as code leaves your machine, use an environment variable:

export MISTRAL_API_KEY="your-api-key"
import os
from mistralai.client import Mistral

client = Mistral(api_key=os.environ["MISTRAL_API_KEY"])

We will use this second form in all subsequent lessons.

Call parameters

The client.ocr.process() call requires only two parameters: model, always set to "mistral-ocr-latest", and document, which describes the file to process in one of the three forms detailed below.

Other parameters adjust the output to your needs:

  • table_format: Table format (null, "markdown", "html")
  • include_image_base64: True to receive images in base64
  • bbox_annotation_format: Bounding box annotation schema
  • document_annotation_format: Full document annotation schema
  • document_annotation_prompt: High-level instructions for annotation

The first two concern OCR proper, and you will handle them in the next lessons. The last three relate to annotations, covered starting in Lesson 9; for now, just remember that they let you request structured extraction in the same call, without a second round trip.

Three ways to send a document

The first, a public URL, is the most direct when the file is already accessible online—a published report, an attachment stored on a public bucket:

ocr_response = client.ocr.process(
    model="mistral-ocr-latest",
    document={
        "type": "document_url",
        "document_url": "https://example.com/document.pdf"
    }
)

The second is necessary for documents that live on your machine or in private storage. You encode them in base64 and send them in the request body. This is the default method for anything confidential, since no URL needs to be exposed:

import base64

with open("my_document.pdf", "rb") as f:
    document_base64 = base64.b64encode(f.read()).decode("utf-8")

ocr_response = client.ocr.process(
    model="mistral-ocr-latest",
    document={
        "type": "document_url",
        "document_url": f"data:application/pdf;base64,{document_base64}"
    }
)

The third uses the Files API. You upload the file once, retrieve an ID, and then reference it in your calls. The benefit shows up with large documents, or when the same file must be processed multiple times—an OCR extraction today, an annotation with a different schema tomorrow: you do not retransfer 40 MB on every attempt.

# Upload the file
uploaded_file = client.files.upload(
    file=open("my_document.pdf", "rb"),
    purpose="ocr"
)

# Use the uploaded file
ocr_response = client.ocr.process(
    model="mistral-ocr-latest",
    document={
        "type": "file_id",
        "file_id": uploaded_file.id
    }
)

Reading the response

The response exposes a list of pages, and each page carries its content along with its metadata. A minimal loop is enough to verify that extraction went smoothly:

for page in ocr_response.pages:
    print(f"Page {page.index}")
    print(f"Content: {page.markdown[:200]}...")
    print(f"Images: {len(page.images)} found")
    print(f"Header: {page.header}")
    print(f"Footer: {page.footer}")
    print(f"Dimensions: {page.dimensions}")

Make it a habit to run this kind of loop on a sample document before industrializing anything: it tells you in three seconds whether the page count matches, whether images were detected, and whether headers are properly isolated from the body text.

Limits and errors

Three constraints govern usage: 50 MB per file maximum, 1,000 pages per document, and input formats PDF, PNG, JPEG, AVIF, PPTX, and DOCX. On top of that comes rate limiting, with a threshold that depends on your subscription tier—a point to check before massive parallelization, or you risk seeing your calls rejected in a burst.

These limits, like network incidents, manifest as exceptions that you must catch. A batch job that stops at invoice number 300 because one file exceeds the allowed size is an avoidable waste of time:

from mistralai.client import Mistral
from mistralai.client.errors import SDKError

client = Mistral(api_key=os.environ["MISTRAL_API_KEY"])

try:
    ocr_response = client.ocr.process(
        model="mistral-ocr-latest",
        document={
            "type": "document_url",
            "document_url": "https://example.com/document.pdf"
        }
    )
except SDKError as e:
    print(f"API Error: {e.status_code} - {e.message}")
except Exception as e:
    print(f"Unexpected error: {e}")

Distinguishing the two exception levels is useful: SDKError gives you a status code on which to decide whether to retry—a 429 should be retried after a delay, a 400 never—while the generic block catches local problems, such as a missing file or impossible encoding.

Key takeaways

  • The OCR endpoint is client.ocr.process() with the model mistral-ocr-latest
  • Three sending methods: public URL, base64, uploaded file
  • Optional parameters control table format and image inclusion
  • The response is structured page by page with Markdown, images, and metadata
  • Respect the limits of 50 MB and 1,000 pages per document