# Document OCR API hosted in Europe: PDFs, scans and handwriting to Markdown
URL: https://docs.munito.ai/document-ai

The Munito AI OCR API reads PDFs and images, such as scans and handwritten pages, and returns the Markdown of each page. It runs in the EU or the US, and it bills each page. Munito AI deletes the result when it delivers it.

Send the document to [`POST /ocr`](https://docs.munito.ai/api/createOcr) by URL or as base64. The base URL is `https://api.munito.ai/inference/v1`. To keep the work in the EU, use `https://api.eu.munito.ai/inference/v1`.

## Convert a PDF to Markdown [#convert-a-pdf-to-markdown]

The answer has one entry in `pages` for each page, with the Markdown of the page in `markdown`. [PDF to Markdown](https://docs.munito.ai/pdf-to-markdown) gives a complete script, with page selection and long documents.

The model sets how Munito AI reads each page:

| Model                          | How it reads a page                                                                                     | What `markdown` holds                                                                  |
| ------------------------------ | ------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- |
| `lightonai/lightonocr-2-1b`    | With the OCR model LightOnOCR-2-1B                                                                      | Markdown with headings, paragraphs and tables                                          |
| `munito/ocr-auto`, the default | From the text layer when the PDF page is mostly text. With an OCR model for other pages, such as scans. | Plain text for a page from its text layer, Markdown for a page that an OCR model reads |
| `munito/ocr-fast`              | From the text layer of a PDF only                                                                       | Plain text with no headings and no tables. A scan gives empty text and costs nothing.  |

## Read scans and handwriting [#read-scans-and-handwriting]

A page with no text layer, such as a scan or a handwritten page, needs an OCR model. `lightonai/lightonocr-2-1b` and `munito/ocr-auto` send such a page to an OCR model, and `munito/ocr-fast` returns empty text for it. Send an image, such as a PNG, in the same way as a PDF. See [formats and limits](https://docs.munito.ai/pdf-to-markdown#formats-and-limits).

LightOnOCR-2-1B reads printed text and handwriting, also cursive. Its accuracy depends on how clear the writing is, so check the Markdown of a page that is hard to read.

## Extract tables from scanned PDFs [#extract-tables-from-scanned-pdfs]

LightOnOCR-2-1B writes each table as an HTML table in the Markdown of its page: one `<tr>` for each row, and one `<th>` or `<td>` for each cell. This is the Markdown of a scanned page with a title, a sentence and a table:

```markdown
# Regional sales report, Q3 2026

This report gives the units sold and the revenue of each region in the third quarter.

<table>
  <tr>
    <th>Region</th>
    <th>Units</th>
    <th>Revenue (EUR)</th>
    <th>Change</th>
  </tr>
  <tr>
    <td>France</td>
    <td>1,240</td>
    <td>86,800</td>
    <td>+12%</td>
  </tr>
  <tr>
    <td>Germany</td>
    <td>980</td>
    <td>68,600</td>
    <td>+4%</td>
  </tr>
  <tr>
    <td>Spain</td>
    <td>415</td>
    <td>29,050</td>
    <td>-3%</td>
  </tr>
  <tr>
    <td>Italy</td>
    <td>530</td>
    <td>37,100</td>
    <td>+8%</td>
  </tr>
</table>

The total revenue is 221,550 EUR.
```

This script sends a scanned PDF and prints each table. `pandas.read_html` reads the HTML tables of the Markdown into data frames.

```python
# pip install requests pandas lxml
import base64
import os
import sys
from io import StringIO

import pandas as pd
import requests

with open(sys.argv[1], "rb") as f:
    data = base64.b64encode(f.read()).decode()
response = requests.post(
    "https://api.munito.ai/inference/v1/ocr",
    headers={"Authorization": f"Bearer {os.environ['MUNITO_API_KEY']}"},
    json={"model": "lightonai/lightonocr-2-1b", "document": {"type": "base64", "data": data}},
    timeout=300,
)
response.raise_for_status()
for page in response.json()["pages"]:
    if "<table" in page["markdown"]:
        for table in pd.read_html(StringIO(page["markdown"])):
            print(f"Page {page['index']}:")
            print(table.to_string(index=False))
```

```bash
export MUNITO_API_KEY="munitoai_..."
python scan_tables.py scan.pdf
```

For the scanned page above, the script prints:

```text
Page 0:
 Region  Units  Revenue (EUR) Change
 France   1240          86800   +12%
Germany    980          68600    +4%
  Spain    415          29050    -3%
  Italy    530          37100    +8%
```

The script expects the full result in the answer, as for a short document. A long document answers `202` with a `location` to poll instead. See [Long documents](https://docs.munito.ai/pdf-to-markdown#long-documents-202-and-polling).

A page that Munito AI reads from its text layer gives each cell of a table on its own line, with no table markup. This applies to `munito/ocr-fast`, and to `munito/ocr-auto` on a page that is mostly text. To keep the tables of a PDF that software made, use `lightonai/lightonocr-2-1b`.

## Extract invoices to structured data [#extract-invoices-to-structured-data]

Read the invoice with `POST /ocr`. Then send its Markdown to a chat model, such as `qwen/qwen3.8-27b`, with a JSON schema in `response_format`. The answer is JSON that matches the schema, for example the invoice number, the seller, the buyer, the total and each line.

Both requests use the same key and the same host. Send both to the EU host to keep the invoice in the EU. [Invoice extraction](https://docs.munito.ai/invoice-extraction) gives the complete script.

## OCR in the EU, for GDPR [#ocr-in-the-eu-for-gdpr]

- Send the request to the EU host, `https://api.eu.munito.ai/inference/v1`. The OCR model then reads the pages in the EU. The response has the header `munito-region: EUROPE`, and `usage.inference_region` is `eu`.
- Munito AI deletes the result when it delivers it. A second request for the same result gets `410`.
- Zero data retention is the default, and Munito AI does not train models on your documents. See [Data privacy](https://docs.munito.ai/data-privacy).
- The GDPR Data Processing Agreement (DPA) forms part of the Terms of Service. See [GDPR and EU data residency](https://docs.munito.ai/gdpr-data-residency).

An OCR request keeps data while it runs, so it must run in the data region of your workspace. Send it to the global host or to the host of that region. The other regional host answers `421`. See [Regions](https://docs.munito.ai/regions).

## Price per page [#price-per-page]

Munito AI bills OCR for each page, at a posted price for each 1,000 pages. The model, the region and the service tier set the price of a page. [Models and pricing](https://docs.munito.ai/models#ocr-models) lists each price.

Each answer shows what the request costs:

| Field                      | Meaning                                                        |
| -------------------------- | -------------------------------------------------------------- |
| `pages[].price_usd`        | The price of the page                                          |
| `usage.pages_processed`    | The number of pages that Munito AI reads and bills             |
| `usage.pages_failed`       | The number of pages that failed. Munito AI does not bill them. |
| `usage.cost_usd`           | The price of the request: the sum of the page prices           |
| `usage.price_per_page_usd` | The average price of one page                                  |

To pay for fewer pages, send `pages` with the numbers of the pages to read. The `flex` [service tier](https://docs.munito.ai/service-tiers) costs less for each page than `standard`: send the header `munito-service-tier: flex`.

## Related pages [#related-pages]

- [PDF to Markdown](https://docs.munito.ai/pdf-to-markdown)
- [Invoice extraction](https://docs.munito.ai/invoice-extraction)
- [Run OCR](https://docs.munito.ai/api/createOcr), the API reference
