The short answer
- What works: a vision-language model (VLM) that reads each page image straight into rows (date, narration, debit, credit, balance). Our own code then checks every row. The best models read 97–99% of rows exactly on real scans. With the checks, almost no wrong row got through unflagged.
- Where: on our server, not on the customer's PC. Open models on a CPU take 38–130 s per page, far over a 10–12 s budget for a whole statement. Our server has 4 CPU cores and no GPU, so it must send pages to an API (or to a rented GPU later), all pages of a statement at the same time.
- Speed (measured through the direct API): with thinking off and every page sent at once, Claude Haiku 5.5 read whole statements in 5.4 s (median) and at most 12.5 s, over all 31 real scans (1 to 23 pages). Thinking off does not lower accuracy. It only removes the hidden thinking tokens.
- Which service: Claude Haiku 5.5 with thinking off (97.0% of rows exact on real scans, 100% on the 31 banks, $1.2 per 1,000 pages, measured). Read every page with Claude Sonnet 5.5 with thinking off at the same time (97.4%, $24 per 1,000 pages), and use Sonnet's page only where Haiku's page fails the checks. Together, 28 of 31 real statements reconciled end to end in at most 13.5 s, for about ₹2 a page. Gemini 3.8 Flash (97.9%) and Opus 5.5 (99.3%) were tested only through their CLIs, so their true speed is not known.
- Open source: not good enough yet. On real scans PaddleOCR-VL-1.6 read 74% of rows exactly and GLM-OCR 77% (cloud models: 97–99%). PP-OCR (Apache-2.0) is still useful: it finds 99.8% of amounts exactly in 2–7 s per page on CPU, so it is our independent second reader and gives the position of each value on the page.
- What does not work: adding OCR text to our current parser. Only 1 of 37 OCR'd statements validated. Tesseract misses 22–69% of amounts.
What I tested on
There were no scanned uploads in production yet (12 statements in total, all text PDFs). So I built three test sets. Two of them have exact answers.
| Set | Where it came from | Pages | Rows | How we know the right answer | Sent to cloud? |
|---|---|---|---|---|---|
| Production | The 6 distinct validated production statements, turned into scan-like images (tilt, blur, noise, grey paper, JPEG) | 22 | 441 | The validated rows saved in production | No, this PC only |
| 31 banks | One text PDF per bank from the Drive samples (SBI, HDFC, ICICI, Axis, BoB, PNB, Kotak, co-operative banks and more), made scan-like in the same way | 31 | 704 | Our parser's validated rows from the original text PDF | Yes |
| Real scans | The 31 scanned PDFs in the Drive samples: phone photos, office scans, passbook pages, handwriting, ticks, punched holes, cut-off edges | 124 (31 scored) | 428 scored | Page 1 of each PDF: where two models from different vendors agreed exactly and the balances reconciled (26 pages), I accepted the rows. I checked the other 5 pages by eye. | Yes |
A row counts as right only if its date, debit, credit and balance all match exactly. Narration is scored separately. Because the real-scan answers came partly from model agreement, they favour models that agree with Gemini and Claude Sonnet. The two other sets have no such bias.
Results
| Engine | Type | Real scans | 31 banks | Production | Seconds per page | USD per 1,000 pages | License / access |
|---|---|---|---|---|---|---|---|
| Direct Anthropic API, measured (usage and time from the API itself) | |||||||
| Claude Haiku 5.5, thinking off, low effort | General VLM | 100% | not sent | 5.1 | 1.2 | 0 thinking tokens; 1,290 output tokens per page | |
| Claude Haiku 5.5, thinking off, high effort | General VLM | 100% | not sent | 5.5 | 1.2 | Same as low effort | |
| Claude Haiku 5.5, thinking on, low effort | General VLM | 98.6% | not sent | 6.0 | 1.3 | Thought only on some pages; slowest page 18.5 s | |
| Claude Sonnet 5.5, thinking off, low effort | General VLM | 100% | not sent | 7.6 | 24 | No date-column shift this time; 28 of 31 whole statements reconcile | |
| Claude Sonnet 5.5, thinking off, high effort | General VLM | 98.6% | not sent | 7.8 | 24 | Swapped debit and credit on one bank page (caught) | |
| Cloud APIs tested through their command-line tools (times include the tool) | |||||||
| Claude Opus 5.5 | General VLM | 100% | not sent | 17* | ~26 | Anthropic API; India processing on Bedrock | |
| GPT-6 Astra | General VLM | 99.9% | not sent | 28* | ~54 | OpenAI API; no India processing | |
| Gemini 3.8 Flash (low thinking) | General VLM | 100% | not sent | 29* | ~4 | Google API; Vertex AI Mumbai (check model list) | |
| Claude Haiku 5.5 | General VLM | 98.6% | not sent | 20* | 1–4 (measured) | Anthropic API | |
| Claude Haiku 5.5 (low effort) | General VLM | 100% | not sent | 15* | ~2 (measured) | Same errors as medium, all caught by the checks; 3 of them were honest "?" for hidden dates | |
| Gemini 3.8 Flash (high thinking) | General VLM | 100% | not sent | 109* | ~4 | One 46-row page came back empty | |
| Claude Sonnet 5.5 | General VLM | 100% | not sent | 15* | ~13 | Slipped a date column by one row on one page | |
| Open models on CPU (8 threads of a Ryzen 9 9950X) | |||||||
| GLM-OCR (official table prompt) | OCR VLM, 0.9B | 100% (2 pages) | — | 112 | 0 | MIT. Stopped after 19 of 31 real-scan pages: too slow | |
| PaddleOCR-VL-1.6 (official pipeline) | Layout + OCR VLM, 0.9B | — | — | 38 | 0 | Apache-2.0. Real scans only; then stopped | |
| Classic OCR on CPU (text only, no rows) | |||||||
| PP-OCRv6 via RapidOCR | Detector + recognizer | — | amounts found exactly: 99.8% | 2–7 (4 cores) | 0 | Apache-2.0; runs on Windows and Linux | |
| Tesseract 5 | Detector + recognizer | — | amounts found: 78% / 31% | 2–4 | 0 | Apache-2.0 | |
| PP-OCR words fed into our current parser | OCR + recipes | — | 58% | 57% | +1 | 0 | Validated 1 of 37 statements |
* Times through a command-line tool include its start-up and file-reading step (10–20 s); the direct API rows do not. Costs are API list prices (October 2026) times tokens per page. The direct API rows use the tokens the API reported: about 5,200 input tokens per page image and 1,300 output tokens. The CLI rows for Gemini, GPT and Opus count only the visible rows and assume fewer input tokens, so treat them as minimums. Through the CLI, Claude thinking cannot be turned off (I tried both settings); the API accepts thinking: {type: "disabled"} for Haiku 5.5 and {type: "between_tools"} for Sonnet 5.5, at effort high or below. "Production" was run only with engines on this PC.
Speed against a 10–12 s budget
The budget is for a whole statement, from upload to rows. Most scanned statements in the samples have 1–4 pages; the longest has 23.
| What I measured | Result | What it means |
|---|---|---|
| Open models on CPU, per page | 38–130 s | Out on any PC or on our server |
| Model writing speed (fitted from 400+ page calls) | 120–140 tokens/s | Haiku, Sonnet and Gemini Flash are alike; GPT-6 Astra is about 54 tokens/s |
| Output per page | 700 median, 1,530 for dense pages (tokens) | Writing alone takes 6–13 s per page |
| CLI start-up with no image (cost of my test method only) | 4.5 s Claude, 7.5 s Codex, 15 s Antigravity | A direct API call does not pay this |
| 10-page real scan, all pages at once, through the CLI | 22 s Haiku, 59 s Gemini | Includes the CLI overhead and a file-reading step for each page |
| Same statement, one line per row instead of JSON | 20% fewer characters | Accuracy unchanged (224–226 of 226 rows agree) |
| Same statement, each page cut into two overlapping halves | Output per call down 42% | All 226 rows recovered after merging the halves |
| Direct Anthropic API, all 31 real statements (124 pages), every page of a statement sent at once | ||
| Haiku 5.5, thinking off | median 5.4 s, 90% within 12.5 s, slowest 16.0 s | 4 of 31 statements over 12 s, each because of one slow reply |
| Haiku 5.5, thinking off, second copy of a page sent after 8 s | median 5.4 s, 90% within 9.4 s, slowest 12.5 s | Meets the budget. 17 of 124 pages needed the second copy. The slowest is a page with 46 dense rows |
| Sonnet 5.5, thinking off, second copy after 10 s | median 8.1 s, slowest 20.5 s | 7 of 31 statements over 12 s |
| Haiku and Sonnet at once, Sonnet's page used only where Haiku's fails the checks | median 6.0 s, slowest 13.5 s | 16 of 124 pages came from Sonnet; 3 statements just over 12 s |
| Pages cut into two halves, through the API | slowest page 9.2 s (was 11.4 s) | No: rows exact on real scans fell from 97.0% to 91.1%. The lower half has no column headings, so the model put some debits in the credit column |
Measured with the direct API: Haiku 5.5 with thinking off writes about 250–300 tokens a second, about twice the rate seen through the CLI. A typical page takes 4–7 s and a dense 46-row page about 11–12 s. The time of a statement is the time of its slowest page, not the number of pages: the 23-page statement took 5.6 s. Gemini was not measured through its API (no Gemini key).
Two more ways to make the wait feel shorter: show rows as they arrive (pages finish one by one), and start reading as soon as the upload ends, before the bank is confirmed.
The mistakes the models made
All errors on the real scans fall into five kinds. These are the cases our checks must handle.
- A digit misread. GPT-6 Astra read a 6 as a 0 in an amount. The balance check flags it, and PP-OCR read the digit correctly.
- A guess where the page hides the value. A punched hole hid the first digit of a date. Gemini wrote a full date; Sonnet and Opus wrote "?". Where the dates on both sides fix the day (a date between 08/05 and 13/05 that ends in 3 must be 13/05), the guess was right. In one case it was not knowable. No automatic check can catch a plausible guess, so the prompt must ask for "?" and the app must show such rows.
- Paise invented on a cut-off page. The scan cut the balance column after the rupees. Sonnet wrote ".00", Gemini worked out the paise by arithmetic, and only GPT wrote "?". A check that PP-OCR also saw the paise digits would catch this.
- Rows shifted by one. On a page covered with handwritten notes, Gemini moved the amount column down one row. Sonnet did the same with the date column on another page. Shifted amounts break the balance on every row, which is easy to catch. Shifted dates do not touch the balance. A row with a date but no amount is the clue.
- A date digit guessed with thinking off. Under the same punched hole ("?5/06/2019", between 04/06 and 28/06), Haiku with thinking off wrote 25/06; with thinking on it wrote "?". Sonnet with thinking off wrote 11/05 for a hidden 13/05. Only dates were guessed this way, never amounts.
- An opening balance read as a transaction. Haiku with thinking off kept a "B/F" row as a credit of 7,231.97. It reconciles, so the balance check cannot see it.
- A whole page missing. Gemini on "high" thinking returned nothing for a 46-row page. The row count and the balance across the page break show this.
What our checks catch
For each page I ran four checks that need no right answer:
- The running balance reconciles on every row (in page order or newest-first).
- Every amount the model read also appears in the PP-OCR reading of the same page.
- The dates never go backwards.
- Every row has a balance and exactly one of debit or credit, and no "?".
| Model | Wrong rows | Wrong rows that passed all checks | Amounts flagged by PP-OCR | Wrong amounts the PP-OCR check caught |
|---|---|---|---|---|
| Claude Opus 5.5 | 3 | 0 | — | — |
| GPT-6 Astra | 5 | 0 | 41 of 839 (4.9%) | 3 of 3 |
| Gemini 3.8 Flash | 9 | 1 (the punched-hole date) | 45 of 847 (5.3%) | 7 of 8 |
| Claude Haiku 5.5 | 12 | 0 | 37 of 847 (4.4%) | — |
| Claude Sonnet 5.5 | 36 | 0 | 38 of 847 (4.5%) | 0 of 8 (the invented paise) |
Two more checks, added after the API tests: (5) every date must also appear in the PP-OCR reading of the page, and (6) no row may be named B/F, brought forward, opening, carried forward or closing balance. With them, no wrong row passed for any Claude run, including the guessed dates and the B/F row above. The cost is more pages to look at: on the 31 banks, 21 instead of 29 pages pass by themselves, because PP-OCR misreads some dates. The date check should mark the one row, not the page.
On whole statements the balance check is even stronger. Gemini 3.8 Flash read all 124 pages of the 31 real scans (2,155 rows). 27 of 31 statements reconciled from the first row to the last, across page breaks. The other 4 showed 41 breaks, all on pages with heavy handwriting or ticks. Those statements would open as "to check", with the exact rows marked, as the app already does for needs_review. Through the direct API with thinking off: Haiku reconciled 22 of 31 statements (64 breaks), Sonnet 28 of 31 (9 breaks), and Haiku with Sonnet's page where Haiku's fails 28 of 31 (7 breaks).
What the last 7 breaks are (checked by eye on the page images):
- 4 breaks, one statement: rows missing from the scan. The scanner cut off the last row or two of pages 1–4. The model read every visible row right; the missing amounts (388, 620, 50 and 396) are not in the PDF at all. No reader can recover them. The user must add them or get a better scan.
- 1 break, one statement: a second printout stapled on. Page 11 is a separate printout from 21/03/2020 that repeats the last two rows of page 10. The model also misread one of them (49,995.00 circled in pen, read as 49,866.00). The app must spot repeated rows after a new "Brought forward".
- 2 breaks, one statement: a real reading error. An HDFC page prints each balance half a line above its row, and a ₹1.00 charge and its ₹1.00 refund are both in the Withdrawals column ("1.00" and "-1.00"). The model swapped the two ₹1 rows.
So of 2,147 rows, the only wrong values were two ₹1 rows and one amount on a repeated row. All three were flagged.
The PP-OCR check is strict: on real scans it questions about 5% of correct amounts, because PP-OCR itself misreads some cells. So it should mark single cells to check, not reject the page.
Confidence per row and the Sonnet fallback
Measured on 18/10/2026 through the direct API, on the 62 pages with exact answers (1,135 rows).
| Question | Result |
|---|---|
| What does a confidence score (0–100) per row cost? | +5% output tokens: Haiku $1.23 → $1.27 per 1,000 pages. No slower when run side by side (median 5.7 s with it, 6.0 s without). Accuracy unchanged. |
| Does a low score mark the wrong rows? | Partly. Haiku: below 80 flags 4.7% of rows and catches 19 of 22 wrong rows; below 90 catches all 22 but flags 25%. Sonnet is better calibrated: below 90 flags 4.4% and catches 10 of 10. |
| Does it add to our own checks? | Yes. Our row checks (balance, PP-OCR, dates, labels, "?") caught 14 of Haiku's 22 wrong rows. The 8 they missed (invented paise, guessed hidden dates) all had a score below 80. Checks + score below 80: 0 wrong rows passed, at the cost of 19 more correct rows flagged. |
| Who decides the flagged rows? | A second model. Of 241 flagged rows, Sonnet read 219 the same way, and none of those were wrong. The other 22 went to the user, and all 22 were really wrong or unreadable. Gemini Flash as the second reader gave the same result. A second Haiku did not: it agreed with 4 wrong rows, because it makes the same mistakes. |
| Re-read only the problem region, or the whole page? | The whole page. Re-reading the whole page fixed all 7 pages it was used on. Re-reading only a region around the break fixed 4 of 9 pages and made 2 worse, because the cut loses the row context. A region call is faster (about 3.5 s against 5–15 s), but less reliable. |
| How many pages need the second reader? | 26 of 57 pages had at least one flagged row, mostly because PP-OCR misreads some dates. Run Sonnet on every page at the same time as Haiku: about $26 per 1,000 pages, slowest statement 13.5 s. Or run it after Haiku only on flagged pages: about $13 per 1,000 pages, but 7 of 31 statements then take 15–22 s. |
The score threshold (80) was chosen on the same pages it was tested on, and there are only 22 wrong rows. Treat it as a starting point. Measure it again on real scanned uploads and track it in tracking_events.
Where to run it
| Option | Speed (measured) | Accuracy | Cost | Verdict |
|---|---|---|---|---|
| Customer's Windows PC, CPU | PaddleOCR-VL-1.6: 32 s/page; GLM-OCR: 128 s/page (4 fast cores). A 10-page statement takes 5–20 minutes. Office PCs are slower. | Small open models only | Free, but a 1–2 GB download in the installer | No for reading rows. PP-OCR (about 2–7 s/page) could run here. |
| Customer's PC, GPU | Fast on a gaming GPU, but most office PCs have none | Same as the open models | Free | No: we cannot count on the hardware |
| Our server (4 vCPU EPYC 7F72, 15 GB, no GPU) | About 1.5–2× slower than my PC's cores: about 60 s/page for a 0.9B model | Same as the open models | Free | Only PP-OCR: a VLM is too slow for the 100 s Cloudflare limit |
| Our server → VLM API | 5–15 s per page; pages run in parallel | 97–99% of rows exact on real scans | $1–26 per 1,000 pages | Yes, start here |
| Our server → rented GPU with an open model | Not measured: the test GPU was busy. Published: about 1 page/s per model on an A100 | 74–77% on real scans for the open models tested (on CPU; same weights) | About $0.5–1 per hour for an L4-class GPU, or per-second serverless | Later: when volume is high or data must stay with us |
At today's volume an API is far cheaper than any GPU. 10,000 scanned pages a month cost about $12 with Haiku alone, or about $250 with Haiku and Sonnet together (measured tokens). A GPU that is always on costs $400–700 a month.
Proposed design
The design keeps the rule that already protects the app: a statement is validated only when its numbers prove themselves, and otherwise it is needs_review with the rows marked.
scanned_pdf instead of failing.validated. Else needs_review with the rows to check, and Alt+O shows the place on the page from the PP-OCR boxes.Steps to build it
- Ask the user (the accountant) for consent to send page images to the chosen provider. Today they consent only to sending extracted text to OpenAI.
- Add a scan path on the server: render pages, call Claude Haiku 5.5 and Sonnet 5.5 per page with thinking off, a fixed JSON schema and amounts as strings.
- Run PP-OCR (RapidOCR, ONNX) on the server for the cross-check and the word boxes.
- Reuse our validation: balance chain, totals,
needs_reviewreasons, plus new reasons such asocr_disagreesandunreadable_value. - Use Sonnet's page where Haiku's page fails the checks, then show the rest to the user with the rows marked.
- Make long statements asynchronous (a job with progress), because 20+ pages can pass Cloudflare's 100 s limit.
- Track every scan outcome in
tracking_events, so we can measure real accuracy.
External services
From web research on 17/10/2026. I tested only the general VLMs above; the rest are from the vendors' pages.
| Service | What it gives | USD per 1,000 pages | Processing in India | Fit for us |
|---|---|---|---|---|
| Gemini 3.8 Flash (Google) | Rows straight from the image (tested) | ~4 (doubles on 01/01/2027) | Vertex AI Mumbai for listed models; check 3.8 | Strong option; API speed not measured |
| Claude Haiku / Opus 5.5 (Anthropic) | Rows straight from the image (tested) | 1.2 Haiku / 24 Sonnet (measured) / ~26 Opus | Amazon Bedrock India profile | First choice (Haiku + Sonnet, thinking off) |
| Nanonets free bank-statement converter (tested, no log-in) | Upload a PDF, get an Excel sheet. Tested on 7 real scans: page 1 rows exact 50.8% (95 of 187) against 99.5% for Haiku with thinking off. Read only page 1 of a 6-page scan (45 of 224 rows, 2 right). Returned nothing for a 4-page IDBI scan. On a punched-hole page every amount was changed but the balances still added up, so a balance check would not catch it. Good on clean office scans (HDFC 10 pages: 225 rows, 1 break) | free | Not stated | No: about half the rows wrong on our scans |
| Nanonets OCR-3 API | Markdown and HTML tables with a box and a confidence score for each element; /extract fills a schema. 35B mixture-of-experts model, API only (not tested here) | 10 | Not stated (US company) | Worth one test: tables still need our row mapping and checks; 49.6 on the "old scans" part of olmOCR-bench |
| Nanonets workflow platform | No-code workflows billed per step: $0.02 simple, $0.10 standard AI, $0.30 extraction. $50 free credits; volume plans by quote | 300–2,000 | Not stated | No: 25–150 times the cost of Haiku with Sonnet |
| Nanonets-OCR2-3B (open weights) | Qwen2.5-VL-3B fine-tune, page to markdown and HTML tables | 0 + GPU | Our own GPU | Later: same class as the open models tested above (74–77%) |
| GPT-6 Astra (OpenAI) | Rows (tested); most honest about unreadable values | ~54 | Storage only, no processing | Too costly |
| Mistral OCR 4 / 4.1 | Markdown and tables, block boxes, word confidence | 4 (2 in batch) | Not stated | Not tested: no API key |
| Google Document AI OCR | Words with positions | ~1.5 | Mumbai | Second reader option |
| AWS Textract (tables) | Words and table cells with positions | 15 | Mumbai | Second reader option |
| Azure Document Intelligence (Layout) | Words and tables with positions | ~10 | Central India | Its bank-statement model reads US statements only |
| Perfios, FinBox, Signzy, HyperVerge, Arya.ai | Bank statement analysis for lenders; accept scans | Quote only | India | No: built for credit reports, not rows for accountants |
| LlamaParse, Reducto, Unstructured, Nanonets, Docsumo | General document parsing | 1–300 | Not stated | No advantage over a VLM plus our checks |
Open-source models
The newest public ranking (OmniDocBench v1.6, September 2026) is led by small open models. None of the benchmarks uses Indian bank statements. I ran PaddleOCR-VL-1.6 and GLM-OCR on our real scans (74% and 77% of rows exact). The others are downloaded and ready for a GPU test.
| Model | Size | License | OmniDocBench v1.6 | Positions of values | Notes |
|---|---|---|---|---|---|
| TeleOCR | 1.2B | Apache-2.0 | 96.9 | Layout blocks | Built for photos of documents |
| OvisOCR2 | 0.8B | Apache-2.0 | 96.5 | Images only | |
| PaddleOCR-VL-1.6 | 0.9B | Apache-2.0 | 96.3 | Text spotting | Best on skew and photos (Real5 benchmark) |
| MinerU2.5-Pro | 1.2B | Apache-2.0 (model) | 95.8 | Layout blocks | Ready CPU install in MinerU 4 |
| GLM-OCR | 0.9B | MIT | 95.2 | No | |
| Chandra 2 | 4B | Paid above $2M revenue | — | Layout blocks | Best on old scans (olmOCR-Bench); not tested |
| Qwen3-VL 4B / 8B | 4–8B | Apache-2.0 | — | On request | General model: follows our JSON prompt |
| PP-OCRv5 / v6 | small | Apache-2.0 | — | Words and characters | Not generative: cannot invent a row |
Privacy
- Production statements never left this PC. Only local engines read them.
- A scan carries more than its text: names, full account numbers, signatures. Sending page images to Google or Anthropic needs its own consent from the accountant.
- To keep processing in India: Claude on Amazon Bedrock's India profile, Gemini on Vertex AI in Mumbai (check that 3.8 Flash is listed), or open models on our own GPU.
- The DPDP Rules on security and transfers abroad take effect around May 2027. Transfers abroad are allowed unless the government blocks a country. This is not legal advice.
Limits of this study
- Only 31 real scanned statements, mostly from Gujarat banks. The answers for real scans came partly from model agreement.
- Cloud models were called through their command-line tools, so the times include tool start-up. Costs are estimates.
- I did not test Mistral OCR, Textract, Azure or Document AI (no keys), or Chandra 2 (license, and too big for 8 GB).
- The local GPU tests did not run: the RTX 5060 was in use the whole time. The open models ran on CPU with the same weights. Their accuracy carries over; their GPU speed does not.
- My table-to-rows conversion for the open document models is simple. Part of their lower score may come from it, not from the reading.
How I ran the tests
All work is in the main checkout's ignored folder .local/ocr-research/ (scripts, sets, results). scripts/build_sets.py makes the sets, run_cli.py runs cloud models, run_vllm.py and run_openai_api.py run local ones, evaluate.py scores rows, gate.py measures the checks, doc_check.py checks whole statements, and probe_docs.py feeds PP-OCR words into the current parser. The earlier experiment in bank-parser/docs/ocr-sample-036-benchmark.md reached the same result for OCR plus the current parser.
Every model got the same prompt: return rows as JSON, copy digits exactly, never calculate, write "?" for unreadable characters, and ignore handwriting and stamps. Document models got their own table prompt, and I turned their tables into rows by their column headers.
Sources
- OmniDocBench leaderboard: github.com/opendatalab/OmniDocBench (updated 11/09/2026)
- PaddleOCR-VL-1.5 paper, Real5-OmniDocBench: arxiv.org/abs/2601.21957
- Gemini prices: ai.google.dev/gemini-api/docs/pricing; Claude prices: claude.com/pricing; OpenAI prices: developers.openai.com/api/docs/pricing (all read 17/10/2026)
- Mistral OCR 4: mistral.ai/news/ocr-4 (23/06/2026)
- Document AI regions: docs.cloud.google.com/document-ai/docs/regions; Textract prices: aws.amazon.com/textract/pricing
- Azure bank statement model (US only): learn.microsoft.com/azure/ai-services/document-intelligence/prebuilt/bank-statement
- Claude on Bedrock in India: aws.amazon.com/blogs/machine-learning/amazon-bedrock-expands-claude-model-availability-to-india-cross-region-inference (29/09/2026)
- Number hallucination in VLMs: aclanthology.org/2026.findings-acl.1237; "Do VLMs Read or Rewrite?": arxiv.org/abs/2607.21617
- OCR first, then VLM, on financial scans: arxiv.org/abs/2604.26462
- DPDP Rules 2025 timeline: scconline.com (26/12/2025)