Independent comparison of 6 OCR models on real invoice and receipt processing. We measure what matters: line-item extraction, totals verification, and structure preservation.
Azure Document Intelligence leads on line-item extraction (94.2%) but costs 7× more than Mistral OCR 3. For simple receipts, Mistral at $2/1000 pages is sufficient. For complex invoices requiring total verification, specialized extractors are worth the cost.
General OCR measures character accuracy. Invoice OCR requires semantic understanding: recognizing line items, verifying math, detecting vendor information.
Parse tabular data into structured rows. Each row needs: description, quantity, unit price, total.
Challenge · Variable column layouts, merged cells, implicit headers
Extract and validate: subtotal, tax rate, tax amount, and total. Math should check out.
Challenge · Multiple tax rates, discounts, currency symbols
Identify vendor name, address, tax ID, and bank details. Crucial for accounting integration.
Challenge · Logos, letterheads, varying formats
Tested on 500 real invoices and receipts across 12 industries and 8 languages.
| Model | Type | Line Items | Totals | Vendor | Structure | Cost / 1000 |
|---|---|---|---|---|---|---|
| Azure Document Intelligence | Specialized | 94.2% | 98.1% | 96.5% | 95.8% | $15 |
| Google Document AI | Specialized | 93.8% | 97.5% | 95.2% | 94.6% | $15 |
| Claude Sonnet 4 | VLM | 91.5% | 96.2% | 94.8% | 92.1% | $60 |
| GPT-4V | VLM | 90.8% | 95.8% | 93.5% | 91.4% | $75 |
| Mistral OCR 3 | Expert OCR | 88.6% | 92.4% | 89.2% | 93.5% | $2 |
| Docling | Open Source | 82.5% | 88.2% | 85.4% | 86.8% | $0 |
Line Items = correct description, qty, price, total extracted. Totals = subtotal + tax + total correctly parsed and verified. Vendor = name, address, tax ID correctly identified. Structure = table formatting preserved.
A line item is only correct if ALL fields match: description, quantity, unit price, and line total.
Beyond raw text extraction — how well does the model preserve headers, footers, table structures, and the logical organization of the document?
| Model | Headers | Tables | Tax Calc | Output Format |
|---|---|---|---|---|
| Azure Document Intelligence | Excellent | Excellent | Verified | Structured JSON |
| Google Document AI | Excellent | Excellent | Verified | Structured JSON |
| Claude Sonnet 4 | Excellent | Good | Extracted | Prompted JSON |
| GPT-4V | Excellent | Good | Extracted | JSON Mode |
| Mistral OCR 3 | Good | Excellent | None | Markdown + HTML |
| Docling | Good | Good | None | Markdown/JSON |
Tax Calc: "Verified" = model checks math. "Extracted" = values extracted but not validated. "None" = pure OCR, no semantic understanding.
from mistralai import Mistral
import base64
import json
client = Mistral(api_key="your-api-key")
def extract_invoice(image_path):
with open(image_path, "rb") as f:
image_data = base64.b64encode(f.read()).decode()
response = client.ocr.process(
model="mistral-ocr-2512",
document={"type": "image", "data": image_data}
)
# Parse markdown output for invoice fields
return response.contentimport anthropic
import base64
import json
client = anthropic.Anthropic()
def extract_invoice(image_path):
with open(image_path, "rb") as f:
image_data = base64.b64encode(f.read()).decode()
message = client.messages.create(
model="claude-sonnet-4-20250514",
max_tokens=4096,
messages=[{
"role": "user",
"content": [
{"type": "image", "source": {
"type": "base64",
"media_type": "image/png",
"data": image_data
}},
{"type": "text", "text": """Extract invoice data as JSON:
{
"invoice_number": "",
"date": "",
"vendor": {"name": "", "address": "", "tax_id": ""},
"buyer": {"name": "", "address": "", "tax_id": ""},
"line_items": [{"description": "", "qty": 0, "unit_price": 0, "total": 0}],
"subtotal": 0,
"tax_rate": "",
"tax_amount": 0,
"total": 0
}"""}
]
}]
)
return json.loads(message.content[0].text)from openai import OpenAI
import base64
import json
client = OpenAI()
def extract_invoice(image_path):
with open(image_path, "rb") as f:
image_data = base64.b64encode(f.read()).decode()
response = client.chat.completions.create(
model="gpt-4o",
response_format={"type": "json_object"},
messages=[{
"role": "user",
"content": [
{"type": "image_url", "image_url": {
"url": f"data:image/png;base64,{image_data}"
}},
{"type": "text", "text": """Extract all invoice data.
Return JSON with: invoice_number, date, vendor, buyer,
line_items (description, qty, unit_price, total),
subtotal, tax_rate, tax_amount, total"""}
]
}]
)
return json.loads(response.choices[0].message.content)from azure.ai.formrecognizer import DocumentAnalysisClient
from azure.core.credentials import AzureKeyCredential
client = DocumentAnalysisClient(
endpoint="your-endpoint",
credential=AzureKeyCredential("your-key")
)
def extract_invoice(file_path):
with open(file_path, "rb") as f:
poller = client.begin_analyze_document(
"prebuilt-invoice", f
)
result = poller.result()
invoice = result.documents[0]
return {
"invoice_number": invoice.fields.get("InvoiceId").value,
"vendor": invoice.fields.get("VendorName").value,
"total": invoice.fields.get("InvoiceTotal").value.amount,
"line_items": [
{
"description": item.value.get("Description").value,
"quantity": item.value.get("Quantity").value,
"amount": item.value.get("Amount").value.amount
}
for item in invoice.fields.get("Items").value
]
}from docling.document_converter import DocumentConverter
from docling.datamodel.base_models import InputFormat, DocumentFormat
converter = DocumentConverter()
def extract_invoice(file_path):
result = converter.convert(file_path)
doc = result.document
# Docling returns structured document with tables
tables = []
for table in doc.tables:
tables.append({
"headers": table.columns,
"rows": [
{col: row.cells[i].text for i, col in enumerate(table.columns)}
for row in table.body
]
})
return {
"text": doc.export_to_markdown(),
"tables": tables
}| Model | API Cost | Infra Cost | Total | Notes |
|---|---|---|---|---|
| Docling (self-hosted) | $0 | ~$5 | $5 | GPU compute only |
| Mistral OCR 3 | $2 | $0 | $2 | Batch API: $1 |
| Azure / Google | $15 | $0 | $15 | Pre-built extractors |
| Claude Sonnet 4 | $60 | $0 | $60 | ~800 tokens/invoice |
| GPT-4V | $75 | $0 | $75 | ~1000 tokens/invoice |
Retail receipts, point-of-sale printouts
B2B invoices with line items, tax, totals
Multi-page invoices, complex tables
Non-English, various formats (EU VAT, etc.)
Receipts with handwritten additions
Healthcare, legal, financial documents
Use Mistral OCR 3. $2/1000 pages. Good table extraction. Parse markdown output with your own post-processing.
Use Azure Document Intelligence or Google Document AI. Pre-built invoice extractors. SLAs. Audit trails. $15/1000 pages.
Use Docling self-hosted. Apache 2.0 license. No data leaves your infrastructure. 82.5% line-item accuracy.
Use Claude Sonnet 4. Lowest hallucination rate (0.09%). Best for documents where inventing data is unacceptable.
Use GPT-4V with JSON mode. Best handwriting recognition among VLMs. Direct structured output.