Receipts Are the Worst Documents to Parse
Every expense management tool needs to process receipts. And every team that’s built a receipt scanner has hit the same wall: receipts are small, crumpled, faded, and formatted differently by every point-of-sale system on the planet.
The merchant name might be at the top or buried in the middle. The total might say “Total,” “TOTAL DUE,” “Amount,” or “Summe.” Line items might be neatly tabulated or crammed onto a single line. The paper might be thermal-printed and half-faded by the time someone takes a photo.
Traditional OCR gets you raw text. You still need to figure out which text is the merchant, which is the total, and which is a line item. That’s where the real work is — and it’s the work the Document Extraction API handles for you.
One Schema, Any Receipt
Define the fields you want. Send the receipt photo. Get structured JSON with confidence scores.
import { IterationLayer } from "iterationlayer";
const client = new IterationLayer({ apiKey: "YOUR_API_KEY" });
const { data } = await client.extract({
files: [
{ type: "base64", name: "receipt.jpg", base64: receiptImageBase64 }
],
schema: {
fields: [
{
name: "merchant_name",
type: "TEXT",
description: "Name of the store or business",
is_required: true,
},
{
name: "transaction_date",
type: "DATE",
description: "Date of the purchase",
is_required: true,
},
{
name: "transaction_time",
type: "TIME",
description: "Time of the purchase",
},
{
name: "items",
type: "ARRAY",
description: "Purchased items",
item_schema: {
fields: [
{ name: "description", type: "TEXT", description: "Item name or description" },
{ name: "quantity", type: "INTEGER", description: "Number of items" },
{ name: "price", type: "CURRENCY_AMOUNT", description: "Price for this item" },
],
},
},
{
name: "subtotal",
type: "CURRENCY_AMOUNT",
description: "Total before tax",
},
{
name: "tax_amount",
type: "CURRENCY_AMOUNT",
description: "Tax charged",
},
{
name: "total",
type: "CURRENCY_AMOUNT",
description: "Total amount paid",
is_required: true,
},
{
name: "currency",
type: "CURRENCY_CODE",
description: "Currency used for the transaction",
},
{
name: "payment_method",
type: "ENUM",
description: "How the purchase was paid",
values: ["cash", "credit_card", "debit_card", "mobile_payment", "other"],
},
],
},
});
No OCR library to install. No image pre-processing pipeline. No bounding box coordinates. The parser handles OCR internally, reads the receipt, and maps the content to your schema.
What the Response Looks Like
{
"success": true,
"data": {
"merchantName": {
"type": "TEXT",
"value": "Whole Foods Market",
"confidence": 0.94
},
"transactionDate": {
"type": "DATE",
"value": "2026-02-14",
"confidence": 0.96
},
"transactionTime": {
"type": "TIME",
"value": "14:32",
"confidence": 0.91
},
"items": {
"type": "ARRAY",
"value": [
[
{ "value": "Organic Bananas", "confidence": 0.92 },
{ "value": 1, "confidence": 0.95 },
{ "value": 2.49, "confidence": 0.93 }
],
[
{ "value": "Almond Milk 1L", "confidence": 0.90 },
{ "value": 2, "confidence": 0.94 },
{ "value": 7.98, "confidence": 0.92 }
],
[
{ "value": "Sourdough Bread", "confidence": 0.91 },
{ "value": 1, "confidence": 0.96 },
{ "value": 5.99, "confidence": 0.93 }
]
],
"confidence": 0.92
},
"subtotal": {
"type": "CURRENCY_AMOUNT",
"value": 16.46,
"confidence": 0.94
},
"taxAmount": {
"type": "CURRENCY_AMOUNT",
"value": 1.32,
"confidence": 0.91
},
"total": {
"type": "CURRENCY_AMOUNT",
"value": 17.78,
"confidence": 0.96
},
"currency": {
"type": "CURRENCY_CODE",
"value": "USD",
"confidence": 0.95
},
"paymentMethod": {
"type": "ENUM",
"value": ["credit_card"],
"confidence": 0.88
}
}
}
Built-In OCR, No Setup
The parser includes OCR for image inputs. Send a JPEG, PNG, GIF, or WEBP photo of a receipt and the parser reads the text, understands the layout, and extracts your fields.
This is not a separate OCR step that dumps raw text for you to parse. The OCR is integrated into the extraction pipeline — the parser sees the full image, recognizes text regions, and maps them directly to your schema fields.
Financial Field Types
Receipt parsing needs more than text extraction. The parser includes purpose-built field types for financial data:
- CURRENCY_AMOUNT — understands decimal separators, thousand groupings, and currency symbols across locales. “$1,234.56” and “1.234,56 €” both parse correctly.
- CURRENCY_CODE — returns validated ISO 4217 codes (USD, EUR, GBP).
- DECIMAL — for non-currency numeric values (tax rates, quantities with fractions).
These field types handle the formatting differences that break regex parsers — the comma-vs-period decimal separator problem, the dollar sign that might be before or after the number, the thousand separators that vary by country.
Confidence Scores for Expense Workflows
Receipts are messy by nature. Faded thermal paper, crumpled edges, bad lighting, partial occlusion. The confidence scores tell you how much to trust each extraction.
Build your expense workflow around thresholds:
- Above 0.90 — auto-approve the expense line
- 0.70 to 0.90 — show the extracted values to the submitter for confirmation
- Below 0.70 — require manual entry
This keeps the automation running for clean receipts while flagging problematic ones for human attention. No silent failures, no garbage data in your accounting system.
Different Receipt Formats, Same Schema
Receipts vary more than almost any other document type. A gas station receipt has a pump number and fuel grade. A restaurant receipt has a tip line and itemized dishes. A hardware store receipt lists SKUs. Despite these differences, the same core schema — merchant, date, items, total — works across all of them.
For domain-specific fields, add them to the schema. Restaurant receipts might include a tip amount. Gas station receipts might include a fuel type. The parser extracts these fields when they’re present and omits the field from the response when they’re not. Your code checks the confidence score and handles each case accordingly.
The ENUM field type is particularly useful for categorizing receipts automatically. Add a field like:
{
name: "receipt_category",
type: "ENUM",
description: "Type of purchase",
values: ["grocery", "restaurant", "fuel", "office_supplies", "travel", "other"],
}
The parser reads the receipt content and assigns the best matching category. This saves a manual categorization step in your expense workflow.
Integration Patterns
Receipt parsing fits into several common workflows:
Mobile expense apps. A user photographs a receipt. Your app sends the image to the parser via base64, gets structured data back, and pre-fills an expense form. The user confirms or corrects, then submits. Confidence scores above 0.90 could skip the confirmation step entirely for known-good merchants.
Email receipt forwarding. Users forward email receipts to a dedicated inbox. A background worker picks up attachments, sends them to the parser, and creates expense entries. PDF and image attachments go through the same pipeline — the parser handles both.
Accounting system sync. After extraction, map the structured fields to your accounting software’s API. The currency field (ISO 4217) maps directly to most accounting systems. The transactionDate comes back as a standard date string. Line items map to expense categories. Confidence scores determine whether entries post automatically or sit in a review queue.
Batch Processing for Expense Reports
An employee returns from a trip with 15 receipts. Send all of them in a single API call — up to 20 files per request, 200 MB total. The parser extracts the same schema from each receipt and returns structured results for every file. Each receipt gets its own confidence scores, so you can auto-approve the clean ones and flag the blurry or faded ones independently.
What’s Next
Extracted receipt data routes directly into Document Generation for formatted expense reports — same auth, same credit pool.
Get Started
Check out the docs for the full API reference. The TypeScript and Python SDKs make integration straightforward — install the package, pass your API key, and start parsing receipts.
Sign up for a free account — no credit card required. Take a photo of a receipt, send it to the API, and see structured JSON come back.