An invoice data extraction API takes an invoice as a PDF or image and returns structured JSON: header fields such as invoice number, vendor, dates and totals, plus the line items, each value with a confidence score and, in the better ones, the place on the page it was read from. With anyformat you describe the fields once as a schema, run a workflow, and read the result. This guide shows the full flow with code, what comes back, what it costs, and how it compares with the other ways of doing it. Last evaluated: October 2026.
What an invoice extraction API should return
For accounts payable the useful output is not text, it is a record you can post to an ERP or accounting system:
- Header fields: invoice number, vendor name, issue date, due date, currency, subtotal, tax amount, total amount.
- Line items: description, quantity, unit price and amount for each row.
- A confidence score per value, so you can decide what is posted automatically and what a person checks.
- Evidence: the text and page each value came from, so a reviewer can verify it in seconds instead of rereading the invoice.
- No invented values: when a field is not on the document, the right answer is empty, not a guess.
The last two are what separate an extraction API from OCR with a script on top. See how to extract structured data from a PDF with an LLM for the general case.
The flow: three calls
The API has three steps. Create a workflow with a schema, upload an invoice against it, and fetch the result.
1. Create the workflow (a parse node and an extract node, connected):
curl -X POST 'https://api.anyformat.ai/v3/workflows/' \
-H "Authorization: Bearer $ANYFORMAT_API_KEY" \
-H 'Content-Type: application/json' \
-d '{
"name": "Invoice extractor",
"nodes": [
{ "id": "parse_1", "type": "parse" },
{
"id": "extract_1",
"type": "extract",
"extraction_schema": {
"fields": [
{ "name": "invoice_number", "description": "Invoice ID printed on the document", "data_type": "string" },
{ "name": "vendor_name", "description": "Company that issued the invoice", "data_type": "string" },
{ "name": "issue_date", "description": "Date the invoice was issued", "data_type": "date" },
{ "name": "due_date", "description": "Payment due date", "data_type": "date" },
{ "name": "subtotal", "description": "Total before tax", "data_type": "float" },
{ "name": "tax_amount", "description": "Total tax", "data_type": "float" },
{ "name": "total_amount", "description": "Grand total to pay", "data_type": "float" },
{ "name": "currency", "description": "Currency of the amounts", "data_type": "enum",
"enum_options": [
{ "name": "USD", "description": "US dollar" },
{ "name": "EUR", "description": "Euro" },
{ "name": "GBP", "description": "Pound sterling" }
] },
{
"name": "line_items",
"description": "One row per line on the invoice",
"data_type": "object",
"nested_fields": [
{ "name": "description", "data_type": "string" },
{ "name": "quantity", "data_type": "integer" },
{ "name": "unit_price", "data_type": "float" },
{ "name": "amount", "data_type": "float" }
]
}
]
}
}
],
"edges": [ { "source": "parse_1", "target": "extract_1" } ]
}'
# returns { "id": "<workflow_id>", ... }2. Upload an invoice and run it:
curl -X POST "https://api.anyformat.ai/v3/workflows/<workflow_id>/upload/run/" \
-H "Authorization: Bearer $ANYFORMAT_API_KEY" \
-F "files=@invoice.pdf"
# returns 202 { "run_id": "<run_id>", "document_packet_id": "...", "workflow_id": "...", "status": "queued" }3. Fetch the result. The endpoint always answers 200; the status field tells you where the run is (queued, in_progress, processed, error or cancelled), and the results appear once it is processed:
curl -H "Authorization: Bearer $ANYFORMAT_API_KEY" \
"https://api.anyformat.ai/v3/runs/<run_id>/"The same flow as a Python function, with polling and backoff (install the library first with pip install requests):
import os
import time
import requests
API = "https://api.anyformat.ai"
HEADERS = {"Authorization": f"Bearer {os.environ['ANYFORMAT_API_KEY']}"}
def extract_invoice(path: str, workflow_id: str) -> dict:
with open(path, "rb") as f:
run = requests.post(
f"{API}/v3/workflows/{workflow_id}/upload/run/",
headers=HEADERS,
files={"files": f},
timeout=60,
)
run.raise_for_status()
run_id = run.json()["run_id"]
url = f"{API}/v3/runs/{run_id}/"
delay = 4
for _ in range(60):
res = requests.get(url, headers=HEADERS, timeout=30)
res.raise_for_status()
body = res.json()
status = body["status"]
if status == "processed":
return body["results"]["extractions"][0]["fields"]
if status in ("error", "cancelled"):
raise RuntimeError(f"Run {run_id} ended with status {status}")
time.sleep(delay) # queued or in_progress
delay = min(delay * 2, 30)
raise TimeoutError("Invoice still processing after 60 attempts")
fields = extract_invoice("invoice.pdf", workflow_id="<workflow_id>")
print(fields["invoice_number"]["value"], fields["total_amount"]["value"])Submissions are limited to 60 requests per minute and the other endpoints to 600. Send an Idempotency-Key header on POST requests so a retry does not duplicate work. See the API reference.
What comes back
Every field in results.extractions[0].fields returns its value, a confidence score, a verification status and the evidence it was read from. Real output for a fictional invoice, trimmed to two header fields and the first line item:
{
"invoice_number": {
"value": "INV-2026-0417",
"value_override": null,
"verification_status": "not_verified",
"confidence": 91.0,
"evidence": [
{
"text": "Invoice number: INV-2026-0417\nIssue date: 2026-09-15\nDue date: 2026-10-15",
"page_number": 1
}
]
},
"total_amount": {
"value": "2141.7",
"value_override": null,
"verification_status": "not_verified",
"confidence": 92.0,
"evidence": [
{
"text": "Total due (EUR) 2,141.70",
"page_number": 1
}
]
},
"line_items": [
{
"description": {
"value": "Cloud hosting, September",
"value_override": null,
"verification_status": "not_verified",
"confidence": 98.0,
"evidence": [
{
"text": "Cloud hosting, September | 1 | 1,200.00 | 1,200.00",
"page_number": 1
}
]
},
"amount": {
"value": "1200.0",
"value_override": null,
"verification_status": "not_verified",
"confidence": 98.0,
"evidence": [
{
"text": "Cloud hosting, September | 1 | 1,200.00 | 1,200.00",
"page_number": 1
}
]
}
}
]
}Three details that save debugging time:
- Values arrive as strings, even for numbers and dates. Cast them with the data type you declared.
nullmeans not found. The model does not invent a value when the document does not contain it; use the confidence score to decide what to review.- Line items come back as a list of rows.
fields["line_items"]is a list with one entry per row of the invoice, and each entry maps a column name (description,quantity,unit_price,amount) to a cell with the same shape as any other field:value,confidenceandevidence.
A confidence score is only useful if you act on it: set a threshold, post the invoices above it automatically, and send the rest to a person who sees the evidence next to the value.
Ways to extract invoice data, compared
| Approach | Invoice fields | Line items | Custom fields | Confidence and evidence | Price (list, date checked) |
|---|---|---|---|---|---|
| OCR plus regex or templates | Per vendor layout | Fragile | New template each time | No | Your engineering time |
| AWS Textract AnalyzeExpense | Prebuilt | Yes | Limited | Per-field confidence | $10 per 1,000 pages, $0.01 per page up to 1 million pages, US West (Oregon) (AWS pricing page, read 2026-10-07, our Textract comparison) |
| Google Document AI invoice parser | Prebuilt | Yes | Custom extractor | Per-field confidence | $0.10 per document of up to 10 pages, so $0.10 for a one-page invoice (Google Cloud pricing page, read 2026-10-07, our Google comparison) |
| Azure Document Intelligence prebuilt invoice | Prebuilt | Yes | Needs labeled training data | Per-field confidence | $10 per 1,000 pages for all prebuilt models, invoices included (Azure pricing page, read 2026-10-07, our Azure comparison) |
| Schema-based extraction (anyformat) | Any schema you define | Object field with nested columns | A schema change, no retraining | Per value, with evidence | Credits per page and per operator (below) |
AWS Textract and Azure list their prebuilt invoice models at $10 per 1,000 pages, which is cheaper per page than a schema-based API, and they are a good fit when your invoices match their model and you stay on one cloud. Google bills by document, $0.10 for up to 10 pages, so one-page invoices cost more per page there than anywhere else in this table. The trade-off for all three is custom fields: a field they did not anticipate means training or a second tool.
What it costs
Pricing is credit-based, per page and per operator. At list rates, Standard Parse is 25 credits and Standard Extract 35 credits per page, so one invoice page is 60 credits. A credit costs €0.001 on Business and €0.0015 on Free and pay-as-you-go: that is €0.06 per page on Business, or €60 per 1,000 pages, and €0.09 per page on pay-as-you-go. Agentic mode, for dense or complex invoices, costs 100 credits for Parse and 150 for Extract. The Free tier includes 50,000 one-time credits with no credit card, which covers about 800 single-page invoices at Standard rates.
That is more per page than the AWS and Azure prebuilt invoice models at $10 per 1,000 pages. The difference buys a schema you control, evidence for every value, and the same API for other document types. Run the numbers on your own volume before choosing.
Limits, stated plainly
- Scans and photos: accuracy drops with skew, noise and handwriting. Use a confidence threshold and a review step for those.
- Multi-page invoices: test one long invoice from each supplier you handle; dense tables across pages are what Agentic mode is for, and Agentic mode does not support multi-file documents yet.
- Several invoices in one PDF: add a split step so each invoice is extracted separately, and read
extractions, which lists one entry per detected document. - Invoices are business and sometimes personal data. Zero data retention is an Enterprise option that has to be enabled for your organization, so on the Free and Business plans use fictional invoices for tests and read our GDPR guide.
- Accuracy depends on your documents. Before you rely on any vendor's headline figure, ours included, test fifty of your own invoices and measure field-level accuracy and the share that pass with no human touch.
How to choose an invoice extraction API
If your invoices match a prebuilt model and you live in one cloud, start with that cloud's invoice parser. If you need custom fields, several suppliers with different layouts, or evidence a reviewer can check, a schema-based API is the better fit. Ask every vendor the same question: when a value is wrong, what does my team see? For the broader process, see invoice processing with anyformat and the accounts payable page.
Frequently asked questions
What is an invoice data extraction API?
An API that receives an invoice (PDF or image) and returns structured data, usually JSON with header fields, line items and a confidence score per value.
How do I extract line items from an invoice?
Define a line_items field of type object with nested fields (description, quantity, unit price, amount). The API returns one row per line, each cell with its own confidence and evidence.
Does it work with scanned invoices?
Yes, with OCR before extraction. Scans and photos are the weak spot of every approach, so set a confidence threshold and review what falls below it.
What format does the API return?
JSON. Values arrive as strings and you cast them using the data type you declared; a value that is not on the invoice comes back as null.
How is it priced?
Per page and per operator in credits. At Standard rates one invoice page is 60 credits, or €0.06 on Business.
Can I process many invoices at once?
Yes: run the workflow once per file and poll each run. Submissions are limited to 60 requests per minute.
Start with your hardest documents. Upload the invoices that break your current pipeline, define the fields you need, and see the values with their confidence and their evidence on the page. The Free tier includes 50,000 credits with no credit card.
Disclosure: anyformat sells document extraction; the example is fictional and the comparison criteria and dates are stated.







