Blog

Engineering notes, benchmarks and changelogs from the unstructred team.

benchmarks · 2026-02-10 · 11 min read

Benchmarking document parsers in 2026: we ran 120,000 invoices through everyone (including ourselves)

We evaluated seven document parsing platforms on a private dataset of 120,000 real invoices — scanned, born-digital, faxed and photographed under office lighting. We measured field-level F1, layout recall for tables, and — the metric nobody publishes — variance across document quality tiers.

Short version: everyone looks good on born-digital PDFs. Separate the scans from the born-digital and the field opens up fast. Our parser won on scanned docs (F1 0.991 vs 0.955 for the next-best) but honestly lost on one category we care about: multi-column German financial statements, where a competitor's table reconstruction beat ours by 2.4 points. We're shipping a fix.

Full methodology, per-category results and the anonymized subset we can legally share are in the post.

read the full write-up →

#benchmarks#ocr#invoice-parsing

changelog · 2026-01-22 · 4 min read

v2.3.0: table cells you can trust, and pipeline retries

This release is mostly about the ugly parts of document pipelines: partial failures and tables with merged cells. Highlights:

Table extraction now returns cell-level provenance (bounding box + source page) and handles row spans and column spans that previously split into ghost rows. Pipelines gained automatic per-page retries: if OCR times out on page 34 of a 212-page contract, you get pages 1–33 plus a structured retry record, instead of one monolithic failure.

Also in this release: heredoc schemas in YAML, a unstructred diff CLI for comparing pipeline outputs between versions, and a 30% latency cut for TIFF-heavy workloads.

see the upgrade notes →

#changelog#v2.3.0#tables

engineering · 2025-12-05 · 9 min read

Why we moved document routing off Kafka and onto Postgres (and lived)

Everyone told us we were wrong. Six months later, our p99 pipeline latency dropped from 11 seconds to 2.4, our monthly infra bill fell 68%, and on-call got quieter. This post walks through the trade-offs: Kafka was overkill for our access pattern (document-shaped, bursty, fan-out of one), and queue semantics were actually hurting us — a document that fails page-level parsing needed to pause and resume, not retry from the top.

We cover the schema, how we handle bursts with SELECT ... FOR UPDATE SKIP LOCKED, why we still use object storage for blobs, and when we'd tell you to keep Kafka anyway (spoiler: if you're fanning out to more than ~3 consumers per document).

read the architecture post →

#postgres#architecture#infra

subscribe via RSS: unstructred.io/feed.xml · or just email hello@unstructred.io