This release is mostly about the ugly parts of document pipelines: partial failures and tables with merged cells. The fun features can wait.
v2.3.0 is live on api.unstructred.io and on PyPI. Upgrade with pip install -U unstructred. Python 3.9 through 3.13 supported; the SDK no longer supports 3.8, which had a good run and is now someone else's problem.
Table extraction now returns cell-level provenance: every cell carries its bounding box and source page, and row spans / column spans that previously split into ghost rows are preserved as real structure.
Why it matters, concretely: one customer's audit workflow required proving which pixels a given total came from. Before v2.3.0 we could prove the page; now we can prove the cell. That's the difference between "trust us" and "here's the receipt," and auditors strongly prefer the second.
for row in doc.tables.line_items.rows: for cell in row.cells: print(cell.value, cell.bbox.page, cell.bbox.rect) # 'Netto' 1 [112.4, 688.0, 241.2, 703.1]
The old flat representation is still returned (row.values) so existing code doesn't break — the new structure is additive.
Previously, if OCR timed out on page 34 of a 212-page contract, the whole document failed and the pipeline retried from the top. Every customer with long documents hated this, correctly. Now: pages 1–33 are kept, page 34 gets a structured retry record, and your webhook still fires — with a partial: true flag and the failed page ranges listed. You decide whether to re-run just those pages or proceed with what you have.
partial: true. If your consumer treats any successful parse as complete, add a check on the partial field. This is technically a breaking change in error semantics; we gave it a minor version because the old behavior was worse than the ambiguity, and we're prepared to argue about it.heredoc schemas in YAML — define an inline schema without a separate file, for pipelines that are mostly one big extraction with a couple of helpers.unstructred diff CLI: compare pipeline outputs between two SDK versions on the same document set. Built for our own migration testing; customers who beta-tested it now refuse to upgrade without it, which is the review that matters.total.net could differ from the sum of line items by one cent in EUR documents with a 19% VAT rate. One cent. It was reported four times, by four accountants, and every report was correct.| If you use… | Do this |
|---|---|
| Table extraction | Nothing required. New structure is additive; ghost-row consumers should switch to row.spans. |
| Webhook consumers | Handle partial: true (see above). |
| Python 3.8 | Upgrade Python. 3.8's been EOL since October 2024; we held on longer than we should have. |
AsyncClient | Nothing. No, really — this release is API-compatible for async users. |
Full changelog history lives here. Problems with the upgrade? support@unstructred.io — schema questions are our favorite kind, upgrade questions a close second.