Convert PDFs, scans and handwritten pages to Markdown with an OCR API in the EU or the US. Extract tables and invoice fields, pay for each page, and keep zero data retention under a GDPR DPA.
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 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
The answer has one entry in pages for each page, with the Markdown of the page in markdown. 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
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.
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
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:
# 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.
# 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))export MUNITO_API_KEY="munitoai_..."
python scan_tables.py scan.pdfFor the scanned page above, the script prints:
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.
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
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 gives the complete script.
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 headermunito-region: EUROPE, andusage.inference_regioniseu. - 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.
- The GDPR Data Processing Agreement (DPA) forms part of the Terms of Service. See GDPR and EU 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.
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 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 costs less for each page than standard: send the header munito-service-tier: flex.
Related pages
- PDF to Markdown
- Invoice extraction
- Run OCR, the API reference