Invoice & Receipt OCR
Turn a photo, scan, or PDF of an invoice or receipt into structured JSON: vendor, line items, tax, and total.
The problem
Accounting and bookkeeping tools, expense-management apps, and accounts-payable automation all face the same manual step: someone re-typing numbers off a receipt or invoice image into a system of record.
Who it's for
Expense-management apps, bookkeeping and accounts-payable tools, and any workflow that currently has a person manually transcribing receipt or invoice data.
Use cases
- Auto-filling an expense report from a photographed receipt
- Extracting vendor and line-item data from a scanned invoice into an accounts-payable system
- Batch-processing a folder of receipt images instead of manual data entry
What it does
Send an image (by public URL or base64) or a PDF (base64 only — a URL ending in `.pdf` is rejected) and get back structured JSON: vendor name, date, a line-item array (description, quantity, unit price, line total), subtotal, tax, grand total, and currency. Two fields say how far to trust it: `warnings` — present only when something needs a look — which flags amounts that do not reconcile (`totals_mismatch`: line items against the subtotal or total, and subtotal plus tax against the total — often items that were never read, a misread amount, or a discount, shipping or tip this result has no field for), numbers and dates that could not be read or traced to the document (`unreadable_number`, returned as null rather than guessed), and line items that could not be read at all (`line_items_not_a_list`); and `confidence` (0–1), the model's own self-assessment — not a calibrated accuracy figure, and 0 when the model gives none.
Example
Request
POST /extract
{ "file_url": "https://aerviklabs.com/examples/invoice-ocr/sample-receipt-coffee-co.png" }
Response
Captured live 2026-09-15 from the request above on our hosted sample receipt, identical on two calls, values unedited.
{
"vendor": "COFFEE CO.",
"date": null,
"line_items": [
{
"description": "Latte",
"quantity": 2,
"unit_price": 4.5,
"total": 9
}
],
"subtotal": 9,
"tax": 0.72,
"total": 9.72,
"currency": "USD",
"confidence": 0.95,
"warnings": [
{
"code": "unreadable_number",
"message": "\"date\" is printed on the document with a day and month that could be read either way round, so it is returned as null rather than as a guess at which is which."
}
]
}
Calling it directly — real, working requests you can paste and run:
cURL
curl -X POST "https://invoice-receipt-ocr-structured-line-item-extraction-api.p.rapidapi.com/extract" \
-H "content-type: application/json" \
-H "X-RapidAPI-Key: $RAPIDAPI_KEY" \
-H "X-RapidAPI-Host: invoice-receipt-ocr-structured-line-item-extraction-api.p.rapidapi.com" \
-d '{"file_url": "https://aerviklabs.com/examples/invoice-ocr/sample-receipt-coffee-co.png"}'
Python
import os
import requests
RAPIDAPI_HOST = "invoice-receipt-ocr-structured-line-item-extraction-api.p.rapidapi.com"
resp = requests.post(
f"https://{RAPIDAPI_HOST}/extract",
json={"file_url": "https://aerviklabs.com/examples/invoice-ocr/sample-receipt-coffee-co.png"},
headers={
"X-RapidAPI-Key": os.environ["RAPIDAPI_KEY"],
"X-RapidAPI-Host": RAPIDAPI_HOST,
},
)
print(resp.json())
Node.js
const RAPIDAPI_HOST = 'invoice-receipt-ocr-structured-line-item-extraction-api.p.rapidapi.com';
const res = await fetch(`https://${RAPIDAPI_HOST}/extract`, {
method: 'POST',
headers: {
'content-type': 'application/json',
'X-RapidAPI-Key': process.env.RAPIDAPI_KEY,
'X-RapidAPI-Host': RAPIDAPI_HOST,
},
body: JSON.stringify({ file_url: 'https://aerviklabs.com/examples/invoice-ocr/sample-receipt-coffee-co.png' }),
});
console.log(await res.json());
Machine-readable: the OpenAPI 3.1 document for this API, generated from its code — import it into Postman or a client generator. More on the developer page.
A ready-made Postman collection is also available: import it and the shared environment in Postman (Import, then select the file), then set RAPIDAPI_KEY to your RapidAPI key.
Pricing
A free tier of 20 requests a month covers light testing, no card required. Paid plans start at $49/month for 500 extractions and are hard-capped — calls stop at the limit rather than billing you. Only the top plan (10,000 extractions) runs on a soft limit, with overage at $0.03 per additional extraction beyond it.
Check warnings and confidence before trusting a result. A high confidence next to a
totals_mismatch warning is exactly the case the warning exists for. OCR and vision extraction
are never perfect — skewed photos, faded thermal receipts, handwriting, and unusual invoice
layouts can all produce partially wrong or incomplete output. Treat a low-confidence response, or
any field returned as null where a value was expected, as needing human review, not ground
truth. This API does not guarantee accuracy.
How the checks work. Each amount and the date must be traced to the characters the model says
it read on the document, and those characters must give the same value: 9.72 does not support
97.2, and -9.72 does not support 9.72. A date printed only in numbers whose day and month
could be read either way round, such as 03/10/2026, is returned as null with a warning rather
than as a guess at which is the month. The line items are added up and compared with the subtotal
(or with the total when no subtotal is read), and the subtotal plus tax with the total, allowing
two cents plus one cent per line item for rounding. vendor and currency are not checked against
the document: currency is the model’s reading, and a receipt that prints only “$” does not say
which dollar.
Number and date formats. Amounts are returned as JSON numbers, checked against the characters
the model quotes for each one. An amount quoted with a decimal comma, such as 9,72 or 1.234,56,
is read as 9.72 or 1234.56. An amount such as 1,234 reads as 1234 or 1.234, so it is returned
as null with a warning unless the document’s other quoted amounts show which mark is the decimal.
A negative amount, as on a credit note, has to be quoted with its sign (a minus sign, parentheses, a
trailing minus or CR), and an amount quoted with a negative sign is not returned as positive.
Whether the model reports a credit note’s amounts as negative has not been measured. date is
returned as YYYY-MM-DD: a date quoted with an English month name, such as 10 March 2026, is
read; a date quoted with no year is returned as null with a warning; and a two-digit year is
checked against the last two digits of the year returned.
What it does not return. Invoice number, due date, purchase order number, vendor address or
tax ID, bill-to details, payment terms, tip, discount, shipping, balance due, tax split by rate,
document type, or the page a line item came from. One document per request, and a multi-page PDF
is one request. A document with more content than fits in one response is refused with a 422
rather than returned in part; send its pages one at a time. A base64 file can be up to 15,000,000
characters and a file_url up to 2,048; a PDF sent as base64 is recognized from its content even
without mime_type. It does not check whether a document is genuine: an altered or fake invoice is
extracted like any other.
422 or 502. A 422 extraction_failed means the document could not be processed as sent: the
model service refused the file, the model did not read it as an invoice or receipt, or the document
has more content than fits in one response. Do not retry the same file unchanged; send a readable
image or PDF, or a long document’s pages one at a time. A 502 extraction_failed means the model
service failed, timed out or was rate limited, or its reply could not be used. A retry later may
succeed.
Data handling. Aervik Labs does not store the documents you send or the data extracted from
them. Extraction runs on an AI model, so each document is sent to our model provider, Anthropic,
which processes it under its own
API data-retention policy.
Nothing on the document is redacted: card digits or a cardholder name printed on a receipt can come
back in line_items or another text field.