
Choosing a receipt extraction API: six questions that separate tax-aware from just-OCR
- ACJ Labs
- Engineering,Tax
- 26 Jul, 2026
Here’s the honest version of the pitch first: if all you need is vendor, date, and total off a receipt, you should probably build it yourself. A single vision-model call — GPT-4o-mini, Gemini Flash — with a “return JSON” prompt gets you most of the way there in an afternoon, for a fraction of a cent per image. We’d rather tell you that than sell you a service you don’t need.
The reason a dedicated tool earns its place is the part that quietly isn’t fine — the production invoices where the tax treatment has to hold up at audit, where a silent schema break kills your pipeline, and where “confidently wrong” costs you at audit time. So here are six questions worth asking of anything you evaluate, ours included.
1. Is tax treatment applied per line item, or is it just OCR?
“Find the total” is an optical problem. Deciding whether the GST on a $180 client dinner is claimable is a
rules problem — and the receipt looks identical to an in-flight meal on a business trip that’s treated
differently. A generic model will confidently guess from keywords like “meal” or “bar.” Ask: does the
tool assign a tax_code per line (a taxable / gst-free / entertainment-ITC-denied-style value) with
a rationale naming the actual rule? If it just extracts text and totals, you’ve moved the hard part into
your own business logic. The tradeoff: rules tagging adds inference complexity, and for a stream where
every line is plainly taxable, raw extraction is enough.
2. Does it validate the ABN checksum, or just transcribe what it sees?
Every Australian Business Number carries a modulus-89 checksum baked into its digits. A smudged 3 read as
an 8 becomes a mathematically invalid ABN — but only if something actually runs the check (we added a
fixture specifically to catch that). Ask: does the
API validate the checksum and flag invalids (without silently “fixing” them)? A raw model call hands back
whatever it read, and will often fabricate a plausible digit to make the sequence look complete rather than
surface that the image is unreadable there. The tradeoff is negligible latency; if your documents don’t
carry ABNs, you can skip it entirely.
3. Does it transcribe verbatim, or auto-correct to “plausible”?
This is the silent data-rot risk. A model told to “extract the vendor” might turn McDonalds #402 into
McDonald's Restaurant Group because it knows the entity better than the receipt does. Ask: are vendor
strings and line items verbatim copies of the source? If the tool “helpfully” normalizes, you lose the
audit trail linking the digital record back to the paper — the thing an auditor actually wants. The
tradeoff: verbatim output is messier, and if you’re only indexing receipts for search, light normalization
may save you downstream cleaning.
4. Is the schema stable and versioned, or does it drift with model upgrades?
The afternoon prototype works until you bump the model version and the JSON shape shifts — a field goes missing, an enum gains a value, a nested object flattens. Ask: is there a typed contract with semantic versioning? If every model update makes your parser a moving target, the “cheap” API is expensive in maintenance. The tradeoff: stable contracts require discipline, and while you’re still changing fields daily in a prototype, raw JSON is more flexible.
5. Is the confidence calibrated — and will it return an honest null?
A number between 0 and 1 next to a wrong-but-confident answer is worse than no number at all. What you want is a low score, or an explicit null, when the input genuinely doesn’t determine a field — a thermal print with a torn-off header has no date to read. Ask: feed it a deliberately degraded receipt and watch what it does. If it fabricates a date to satisfy the schema, the confidence value is meaningless. The tradeoff: honest nulls lower raw recall, and for low-stakes personal expense tracking, forcing a best-guess is fine.
6. Can it explain why a claim is treated the way it is?
The ATO doesn’t care that your JSON says a line is non-claimable; a human eventually has to know why. Entertainment expenses, for instance, have their input tax credit denied by default under AU GST rules — and nothing printed on the receipt tells you that. Ask: does each tax decision carry a one-line rationale referencing the actual rule? Without it, someone on your finance side re-justifies every flagged line by hand, which defeats the automation. The tradeoff is output size and token cost; if your receipt stream never touches meals or entertainment, that reasoning is dead weight.
The honest tradeoff
Per-line tax tagging, checksum validation, schema guarantees, and calibrated nulls aren’t free — they add a verification layer and a little latency over a bare model call, and they cost more than a fraction-of-a-cent inference. For a small volume of simple expenses you check by hand, that overhead isn’t worth it: build it yourself.
For a growing feed where the tax treatment has to stand up at audit, where a stable typed contract across model upgrades matters to code you’d rather not babysit, and where “confidently wrong” is the expensive failure mode, these features are the whole point. That’s the bar we hold our own Receipt Extraction API to — per-line AU tax treatment with a rule rationale, verbatim fidelity with ABN checksum validation, honest nulls, and a schema that won’t drift on you. It’s new, and we’d rather you test it against these six questions than take our word for it.
Run your hardest receipts through whatever you’re considering — including us — and see which ones admit what they don’t know.
GST/ABN handling reflects publicly documented Australian tax law and is provided as software output, not tax advice.


