# space-ocr > space-ocr is a field extraction API with built-in verification (and a web app on top): extract the fields you name, and know what to review. It reads a document image three ways — as structured named fields, as layout-preserving Markdown, or as plain text. Whichever shape you ask for, every value comes back with the on-page coordinates it was read from plus a verification flag, and anything the cross-check could not confirm lands on an explicit review list (paths and reasons — a work list, not a bare confidence score), so silent mismatches get surfaced and results can be traced back to the pixels they came from. Field results are stored as queryable sheets (no database needed) and export to UTF-8 BOM CSV that opens cleanly in Excel, including Japanese/Korean/Chinese text; Markdown and plain-text results export as .md / .txt. Key facts: - API base URL: https://api.space-ocr.com — three document endpoints, one HTTP call each: `POST /ocr/fields` (structured fields), `POST /ocr/markdown` (layout-preserving Markdown with per-element boxes), `POST /ocr/text` (reading-order plain text with per-block boxes). - Verification is uniform across the three endpoints, down to the field names — every response is one four-part structure, `data = { values, cells, review, image }`. `values` is pure user data (exactly the shape you asked for — no reserved keys, storable as-is). `cells` is a flat map keyed by path (`items[0].price`, `elements[2].cells[1]`, `blocks[7]`); each entry carries `box` (axis-aligned rectangle) and `quad` (four tilt-following corners) — both always present, normalized to 0–1000, with `image` converting them back to pixels — plus `verified`, the cell's VERDICT and a mirror of `review` — `false` whenever anything is flagged (including a rule you declared), `true` when nothing is flagged and a check actually ran, `null` when nothing is flagged but there was nothing to check (geometry-only entries like row unions); one field is enough to gate on, `review` (`null` when passed, `{ reasons }` when a human should look — an array ordered by rank, index 0 being the primary verdict, always an array even at length one; the vocabulary: `type_mismatch`, `out_of_range`, `pattern_mismatch`, `near_mismatch`, `near_ambiguous`, `near_conflict`, `nobox`, `text_mismatch`, `crop_mismatch`, `low_ratio`, `weak_source`, `low_ocr_confidence`, `ambiguous_occurrence`, `overwide_box`, `missing`), and `evidence` with the raw signals behind the verdict: `text_match` (the character cross-check itself, present only when it ran), `source` (how the coordinate was produced), `match_ratio`, `printed_text` (what the OCR pass itself read at those coordinates — present on field extraction; `values` is the model reading and the character cross-check folds full-width forms, brackets, hyphens, case and whitespace before comparing, so a value the model re-spelled still passes, and this is the side to use when you need an exact string match such as reconciling against a master table; it does not supersede `values` — the OCR pass has its own misreads, which is why the two are cross-checked — and it joins glyphs only, so spacing between words is not reconstructed — compare ignoring whitespace, and do not read a missing space as proof the page prints none), `ocr_confidence` (the lowest of the OCR pass's own per-glyph confidence scores under the value — below 0.5 the value is flagged `low_ocr_confidence` even when the engines agree, the one witness left when both agree on the same misread), and `crop_verified` (a second cropped-box re-read that runs only for values the first check could not confirm — a confirming crop promotes the value, a contradicting one flags `crop_mismatch`, an empty read abstains), and `multiline` (present only when true — the value wraps across lines, so its box is the union of them). `review` is the document-level summary: `unit` ("field" / "element" / "block") names what was counted, with `declared` / `returned` on field extraction (`total` plus a token `coverage` block on document conversion), `boxed`, `verified`, and `flagged` — an explicit work list of `{ path, reasons }` whose length IS the review count, with `by_reason` as the breakdown — it counts every entry in `reasons`, not just the ranked one, so its total is at least the number of flagged entries; `cells` keys share the `flagged` path grammar, so a flagged path is a direct lookup. On `POST /ocr/fields` a field declared `required: true` that comes back empty — or is absent from the response entirely — is flagged with reason `missing`: the one error class the character cross-check cannot see, because a value that was never returned has nothing to disagree with; empty values never get a cell, so `review.flagged` is the only place they appear. `required` is never shown to the model, so extraction is unchanged; it is a review declaration, worth setting only on values the document always prints. A top-level non-container field (`string` / `number` / `integer` / `date`) may also declare `label` — the label printed next to the value (one string, or an array of candidates): when the same value appears more than once on the page, the returned coordinates anchor to the occurrence beside that label. A `label` on an array or object, or on any of its children, is ignored — a label printed once on the page cannot say which repeating row a value belongs to, and line-item children are already anchored within their own row, so a shared column heading like 数量 needs no label. Like `required` it never reaches the model; the extracted text is unchanged, only the coordinate anchor moves. Labels spanning several printed words (`消費税(8%)`) work as written, and when the label is not printed exactly once the normal search is used and `review.notes` reports it as issue `label_unresolved` — the value still comes back either way, so that note is the only way to tell a declaration that fired from one that did nothing. The character cross-check itself — does the value equal the words actually read at those coordinates, two independent engines agreeing — is `evidence.text_match`, present only when the check ran. It is not the gate and never was: its failure has its own review reason (`text_mismatch`), and a cell can be `verified: false` with `text_match: true`, meaning the characters agreed and a rule you declared is what caught it. Passing that check is likewise not a claim that the value is the field you meant: if the model picks up a different piece of printed text, the box follows that text and the comparison still agrees — the coordinates are evidence of where a value came from, not proof that it is the right item. A field may also declare what its value should BE: a scalar `type` (`number` / `integer` / `date`), a `pattern` regex (one, or an array of alternatives), `min` / `max` bounds, or an `enum` of allowed values. None of these reach the model either — declaring a type to a language model is what makes it invent a value of that shape — so `values` is unchanged by declaring one. (`values` is the model reading of the page held to the printed text by the character cross-check, not a byte-for-byte copy of it — use `evidence.printed_text` when you need the page own spelling.) What a declared type buys is a fifth part of the response, `data.normalized`: the same reading parsed into that type and shaped exactly like `values`, so `normalized.items[0].qty` sits beside `values.items[0].qty` ("¥13,220" → 13220, "令和8年8月16日" → "2026-08-16", "3袋" → 3). Parsing is deterministic — no extra model call, so the same page yields the same number on every run — and a value that will not parse comes back `null` there and is flagged `type_mismatch`, which is usually a misread worth looking at rather than a nuisance; the per-cell diagnostic (`type`, `method`, `error`) rides on `cells[path].normalized`. `error` names the kind of refusal: `not_numeric` / `not_an_integer` / `not_a_date` mean the reading may be a misread (`l510` for `1510`), `no_year` a date whose year is not printed, `conventional_token` a placeholder the document really prints (`一式`, `別途`, a dash-only cell), and `relative_date` a payment term that leans on another field (`翌月末払い`) — the last two will not yield a value on a re-read, so they belong in your own business rules rather than a review queue. `reasons` stays `type_mismatch` for all of them, so no count moves. Printed month-end dates (`2026年9月末日`, `令和8年9月末`) parse to the calendar's last day; without a year (`9月末日`) it is refused as `no_year` rather than resolved against the issue date. Breaking a declared rule raises `out_of_range` or `pattern_mismatch`. These caller-declared reasons rank above the engine's inferred ones, and because one cell can trip both layers every verdict carries `reasons` — the full list, ordered by rank, with index 0 the primary verdict (the singular `reason` key that shipped alongside it was removed once it became a pure duplicate of `reasons[0]`). `enum` in particular is the slot for handing the API a value set the business already owns — a vendor master's company names, an item catalogue, a unit list (袋/本/個) — and the only handle on a misread both engines agree on (冊 read as 申), which character cross-checking is structurally blind to. A field may also declare `near` — the vocabulary expected to be printed beside its value, one term or an array of alternatives (`["御中", "様"]` for an addressee company, `["登録番号", "〒", "TEL"]` for an issuer block): after extraction the declared terms are located on the page, and a value anchored away from every occurrence is flagged `near_mismatch`. That is the only handle on the class where the model reads perfectly but picks the value from the WRONG spot — a form prints two company names, and picking the other one still passes character cross-checking with `verified: true`, while `enum` cannot tell two legitimate master values apart; `near` does not make the pick right, it makes a wrong pick visible. When no declared term is printed anywhere on the page the check abstains and `review.notes` reports issue `near_unresolved`. Like every declaration above it never reaches the model. In the other direction a field may declare `review: "off"`, which withholds the reasons the ENGINE infers for it while keeping every piece of evidence — for free-text columns like 品名 or 備考, so the flags on 登録番号 or 合計 stay readable; it cannot silence `missing` or a constraint violation, because those are rules the caller wrote. The coordinates are never produced by the language model — they come from the OCR pass, so a value can always be checked against the pixels it claims to come from. - How a page becomes data (the pipeline, in order): an OCR pass reads the page first — paragraph, word and character level — and is the only source of coordinates; a multimodal model then sees the original image alongside that OCR text and decides what to read and in what order, without ever producing coordinates; each returned value is matched character by character against the symbols actually detected on the page, and how much matched is reported as `evidence.match_ratio`, with the model's word-token hints used only as a secondary override on top of that; finally the two sides are cross-checked, which is where `verified` and the `review` flags come from — and any value that check could not confirm has its box cropped and read once more (`evidence.crop_verified`) before it is flagged. - The accuracy figures on the landing page — field accuracy 91.1% vs 70.0%, and run-to-run variance of 16 vs 100 values out of 463 across three runs of the same pages — come from a controlled side-by-side benchmark against Mistral Document AI (`mistral-ocr-latest`, document_annotation): same photos, same field schema, same grading script (8 cases over 7 documents · 463 fields · 3 runs each · August 2026). The compared photos are shown on the landing page itself, and the full method, raw responses and grading code are public: https://space-ocr.com/articles/space-ocr-vs-mistral-ocr-benchmark/ — Earlier internal regression-corpus figures (93.7% value accuracy with every verification stage on, 22 cells fixed / 0 broken, 92.1% of returned boxes spelling their value, 6.4× lift for `text_verified: false` (the flag's v1-era name; the same signal is `evidence.text_match` in today's v2 responses); 333 hand-labelled cells, July 2026, not a public benchmark) remain published with method at https://space-ocr.com/articles/measuring-ocr-verification/ - Engine identity is public: `GET /health` (no auth, not billed) reports the public API's contract version (`version`, currently v2.8), the running engine version, its deploy stamp (`engine.build.sha` / `engine.build.deployed_at`) and two caller-facing changelogs of behaviour-visible changes, newest first — the top-level `changelog` for the API itself (paired with `version`, `v2.x`) and `engine.changelog` for the engine (paired with `engine.version`, `vNN`). They are separate axes and either can move alone. The request/response structure is stable, so entries describe additive keys and content-level shifts. Integrators measuring the engine (error rates, flag volumes, re-run stability) should record both versions alongside their measurements. - Synchronous OCR waits up to 180 seconds for the engine; past that the call returns 504 `ocr_engine_timeout` and is not charged. When a document does hit that ceiling the cause is almost always density — small type across several pages — rather than pixel count, so the fix is to send one image per page, or to use the async `POST /upload` path, which allows longer processing. Downscaling a dense page usually makes it unreadable rather than faster. - Request size: the JSON body caps at 28MB (base64 ≈ 1.33× the file, so roughly a 20MB original image); over that returns 413 with `details.limitBytes` / `details.receivedBytes`. Requests over 32MiB are cut off by the infrastructure before reaching the API, and that response is HTML rather than JSON. For bigger files pass a URL or use `POST /upload` (async, 20MB per file) with `GET /jobs/:jobId` polling or webhooks. Large photos may be downscaled to 4000px on the longest side before reading, depending on how the image encodes server-side; the size actually read is reported as `data.image`, and since coordinates are normalized to 0–1000 their meaning is unchanged. Orientation is handled the same way: EXIF orientation is baked into the pixels before reading, so a photo taken sideways is read as an upright page and both values and coordinates come back in that orientation — `data.image` is where width and height swap relative to the file you sent (send 4000×3000, get 3000×4000). `data.image` is therefore the frame every coordinate lives in: draw outlines against it, never against the dimensions of the file you uploaded. No deskew is applied — the page is never rotated to rebuild coordinates, so on a tilted photo the quads follow the tilt, in the input image's own coordinate system. - Results are also a storage layer, so there is no database to stand up: `POST /create` makes a folder, a sheet (with its own column schema), a document bundle or a memo; `POST /upload` sends up to 20 images into a sheet or bundle and each image becomes one row or one page; `POST /edit` overwrites a single cell or a memo body; `POST /remove` deletes. Items are addressed by slash path (`/invoices/2026-08`) or by the `uniqueKey` returned at creation, and `GET /space?path=&depth=` walks the tree. - Stored results are queryable via `GET /view` without re-running OCR: on sheets it takes repeatable `where` filters (`total>=10000`, `vendor~ABC`; operators `= != > >= < <= ~` over the sheet's columns plus `name` / `ocrStatus` / `createdAt`; stored values are the model reading of the page, so comparisons normalize full-width characters and strip currency marks, digit separators and accounting negatives before deciding whether to compare numerically), `sort` (`-invoice_date`, repeatable for tie-breaks), `select` projection, `limit` / `offset` paging with a `nextOffset` cursor, and `boxes=0` to drop coordinate payloads — all server-side and not charged. Rows come back with their values plus the same per-value coordinates and verification flags the OCR endpoints return. The query parameters apply to sheets only; folders, document bundles, memos and images return their full contents. Webhooks (`item.created` / `upload.received` / `ocr.completed` / `ocr.failed`) drive automation on top of the same store. - AI agents reach the same API through an MCP server, so an agent can read a document and then keep what it read: three reading tools (`ocr_extract`, `ocr_markdown`, `ocr_text`) and ten workspace tools (`space_guide`, `space_list`, `space_view`, `space_create`, `space_inbox`, `space_upload`, `space_job`, `space_edit`, `space_balance`, `space_delete`) that call the REST routes above with identical billing — only reading an image and uploading one cost a credit. `space_upload` takes images that are already public https:// URLs, so anything else — a file on the user's disk, a photo attached to the conversation — goes in through `space_inbox`, which opens a short-lived upload link the files are sent to directly; `space_guide` returns the server's own how-to guides (`start`, `upload`, `schemas`, `verification`, `queries`, `workflows`) without a network call or a credit. Hosted over Streamable HTTP at `https://mcp.space-ocr.com/mcp` — there is no install and no local mode. Clients that can set a header send the API key as a bearer token; clients that cannot (claude.ai, Claude Desktop, Claude mobile) add the URL as a custom connector and authorize through OAuth, choosing which API key the connector acts with. The server also publishes MCP resources (`space-ocr://guide/schemas`, `/verification`, `/queries`, `/workflows`) covering schema design, how to read the verification flags, the filter language, and batch processing, plus prompts (`file_documents`, `review_flagged`, `ask_documents`). Two differences from REST: `space_view` omits coordinates unless you pass `boxes: true`, and `space_delete` is a two-step tool — called without a confirm token it deletes nothing and returns what would be removed (counts and a sample) plus a token signed against the caller's key and that exact path, so an agent has to show the user before it can call again to actually delete. The root path is always refused. - `POST /ocr/text` restores true reading order with a vision model (multi-column and skewed scans do not come back interleaved) and degrades to a raw-OCR-order transcription rather than failing; `useLlm: false` opts out. There is no language setting to configure on the request. - In the web app you pick the output shape at upload time: a sheet (each photo becomes a row of named fields) or a doc bundle (each photo becomes one `.md` or `.txt` page). Both keep per-value coordinates, so hovering any cell, heading, paragraph or table focuses the exact region of the source photo it came from. - Pay-per-credit pricing with a free monthly tier; no credits are charged for failed scans. Consumption order is free quota → flat-fee plan → prepaid balance, so the prepaid balance does not move while free quota remains — a remaining-credit display should show both `free.remaining` and `balance`. - The site is available in English (default), Japanese (`/ja/`), Korean (`/ko/`), Simplified Chinese (`/zh/`), and Traditional Chinese (`/zh-TW/`). URLs below are the English canonicals; swap in the locale prefix for translations. ## Docs - [API Documentation](https://space-ocr.com/docs): REST API reference — authentication, `POST /ocr/fields`, `POST /ocr/markdown`, `POST /ocr/text`, the MySpace storage and query routes, webhooks, the MCP server for AI agents, and response schemas with bounding boxes. - [Pricing](https://space-ocr.com/pricing/): plans, pay-as-you-go rates, and the free tier. ## Guides - [Best OCR Software for Receipts and Invoices (2026 Guide)](https://space-ocr.com/guides/ocr-receipts-invoices/): buyer's guide covering verifiable accuracy, line items, export, API, webhooks, audit trail, and pricing — with a live demo. - [OCR in Claude Code with the space ocr MCP server](https://space-ocr.com/guides/claude-code-ocr-mcp/): connecting the hosted MCP server (`claude mcp add --transport http`, or `mcp.json` / an OAuth custom connector elsewhere), the thirteen tools it publishes, getting images in with `space_inbox`, and reading back located fields and queryable rows. - [How to Convert Scanned Documents Into CSV (Step by Step)](https://space-ocr.com/guides/scanned-documents-to-csv/): define columns once, upload a photo or scan, and each document fills a row; review the values in data.review.flagged, then export Excel- and CJK-safe CSV. - [Document OCR with an Audit Trail: Verify Every Extracted Value](https://space-ocr.com/guides/ocr-audit-trail/): how path-keyed cells (box, quad, verified, review, evidence) and the data.review.flagged list let any field be traced back to the source pixels. - [API for Extracting Data From Invoices: A Developer Guide](https://space-ocr.com/guides/invoice-data-extraction-api/): `POST /ocr/fields` with curl and Python, a declared `fields[]` schema or `autoFields`, and per-value `box`/`quad` coordinates with the `data.review` contract. - [請求書OCR API・納品書OCRでCSV化 — 開発者ガイド](https://space-ocr.com/guides/invoice-delivery-ocr-api-japan/): Japanese-language developer guide to invoice/delivery-note OCR — a declared `fields[]` schema with `date`/`number` types, `near`/`not_near` for addressee vs issuer, `data.cells[path]` coordinates, `data.review.flagged`, and CSV export. - [領収書・レシートをOCRでCSV変換 — freee/マネーフォワード/弥生取り込み](https://space-ocr.com/guides/receipts-to-csv-japan/): Japanese-language walkthrough for converting receipts to CSV and importing into Japanese accounting software. ## Solutions - [OCR API](https://space-ocr.com/ocr-api/): developer-focused overview of the structured-field OCR API. - [Invoice OCR](https://space-ocr.com/invoice-ocr/): extracting invoice headers and line items into structured rows. - [PDF OCR](https://space-ocr.com/pdf-ocr/): converting scanned PDFs into structured data page by page. - [Image OCR](https://space-ocr.com/image-ocr/): extracting structured fields from photos and scans. - [Japanese OCR](https://space-ocr.com/japanese-ocr/): Japanese-document OCR with mojibake-safe CSV export. - [AI OCR](https://space-ocr.com/ai-ocr/): how AI-based field extraction differs from raw text OCR. ## Built with space-ocr Open-source demos written by outside developers against the public API. Each repository is the primary source for what it does; the landing page (https://space-ocr.com/#showcase) shows a clip of each one running. - [Invoice review ledger](https://github.com/hwang-yh-cto/space-ocr-invoice-review-demo): Next.js / TypeScript / SQLite. Puts the extracted values, their coordinates on the image and the API's verification result on one screen so a person confirms and corrects before anything is committed to the ledger, then exports CSV. - [invoice2yayoi](https://github.com/izumisounosuke/invoice2yayoi): Python. Turns invoice photos into a journal-entry CSV (27 columns, CP932) that imports straight into Yayoi accounting, plus a separate list of what a person should check; uses `pattern`, `type: "date"`, `review: "off"` and `label` in the field schema. - [Construction cost ledger](https://github.com/oisidonut/space-ocr-construction-cost-ledger): Python / FastAPI / SQLite. Reads paper purchase orders, delivery notes and invoices, runs qty × unit-price and line-total checks, matches project and vendor masters, detects duplicate postings, and draws a leader line from each input field to the spot on the original it was read from (using `quad`, so tilted photos still line up). - [Folder-to-CSV desktop app](https://github.com/oisidonut/space-ocr-folder-to-csv): Python / PySide6, shipped as a single Windows exe. Points at a folder of document photos and PDFs, batch-reads every page and exports a CSV; only cells that need review are coloured, with the source spot outlined by `quad` on the original, and the screen shows values as printed while the CSV carries the normalized ones. The API call is the only network traffic. - [Delivery-note check-and-match pipeline](https://github.com/fp24k0818/space-ocr-demo): Cloudflare Workers / Hono / TypeScript. Takes one photo of a printed delivery note through extraction, arithmetic checks and a match against purchase-order data before registering it in Google Sheets; rows that fail either check are set aside with the reason, and the per-line verdict is drawn over the original photo as three colours of outline. - [Screen-reader reading order](https://github.com/oisidonut/space-ocr-screen-reader-demo): Python / FastAPI / Jinja2, reading-order logic in the standard library only. Rebuilds the reading order fora screen reader from the `cells` coordinates — a two-column form read in returned order interleaves the left and right tables into one spoken line, and a recursive XY cut plus column-centre periodicity separates them. Written by a blind engineer; every ordering check was verified by listening with NVDA. ## Articles - [OCR on the Photos Nobody Benchmarks: Rotated, Crumpled, Shot at Night](https://space-ocr.com/articles/ocr-benchmark-real-world-photos/): the controlled benchmark extended to 15 cases over 14 documents and 769 fields, measured through the public API — 89.2% vs 69.3% field accuracy, run-to-run spread of 6 vs 106 fields, a vision LLM collapsing to 0.217/0.130 on two rotated documents, and a receipt that prints ¥711 on four lines where only coordinates can say which one a field came from. - [Image to Markdown OCR: Keep the Structure, Keep the Coordinates](https://space-ocr.com/articles/image-to-markdown-ocr/): layout-preserving Markdown conversion — element types, per-element coordinates and verification flags, and the token-coverage counter that exposes dropped paragraphs. - [OCR to Plain Text: Why Reading Order Is the Hard Part](https://space-ocr.com/articles/ocr-to-plain-text-reading-order/): reading-order restoration for multi-column and skewed scans, per-block boxes, and the Vision fallback when the model pass fails. - [We Switched Our OCR Verification Off to Measure It](https://space-ocr.com/articles/measuring-ocr-verification/): a controlled ablation on 333 hand-labelled cells — turning every verification stage off drops value accuracy 93.7% → 91.3% (22 cells fixed, 0 broken), `text_verified: false` (the v1-era flag name) is right 75% of the time (6.4× lift), and the coordinate judge is the page's own OCR rather than a snapshot — plus the follow-up experiment that shipped crop re-verification (16 of 24 unconfirmed cells recovered by re-reading just the box). - [OCR API with Bounding Boxes: Verify Every Value](https://space-ocr.com/articles/ocr-api-with-bounding-boxes/): coordinate systems across OCR APIs, and reading the 0–1000 `box` plus oriented `quad` in `data.cells[path]` against `data.image`, with the `verified` / `review.flagged` contract that names what to check. - [OCR API with Source Coordinates: Verify Every Value](https://space-ocr.com/articles/ocr-api-source-coordinates/): working `data.review.flagged` instead of a fixed score threshold, keeping edits beside the original, and querying stored results with `GET /view`. - [How to Validate OCR Output Using Bounding Boxes](https://space-ocr.com/articles/validate-ocr-with-bounding-boxes/): using per-field boxes and match ratios to validate extracted data instead of trusting it blind. - [Extract Line Items From Invoices Automatically](https://space-ocr.com/articles/extract-line-items-from-invoices/): declare an array field, get one verifiable row per line item, export to CSV. - [Extract Invoice Line Items to JSON: the API Contract](https://space-ocr.com/articles/extract-invoice-line-items-to-json/): the JSON contract for `type:'array'` fields, parsed in Python and JavaScript, unfolded to CSV. - [Convert a Scanned PDF to Excel: Page Images to CSV](https://space-ocr.com/articles/convert-scanned-pdf-to-excel/): page images to structured fields to a UTF-8 BOM CSV Excel opens cleanly. - [Amazon Textract Alternative: a Verifiable OCR API](https://space-ocr.com/articles/amazon-textract-alternative/): fact-checked comparison — path-keyed cells with box/quad, a data.review.flagged work list, CJK support, queryable sheet, flat pricing, no AWS setup. - [Google Vision vs Space OCR: Raw Text vs Structured Fields](https://space-ocr.com/articles/google-vision-vs-space-ocr/): raw text + pixel boundingPoly versus structured key-value fields with per-value match ratios. - [Tesseract vs Google Vision vs Space OCR for Receipts](https://space-ocr.com/articles/tesseract-vs-google-vision-vs-space-ocr-receipts/): fact-checked three-way comparison for receipt OCR. - [AP Automation with an OCR API and Webhooks](https://space-ocr.com/articles/ap-automation-with-ocr-api-and-webhooks/): building an accounts-payable pipeline on structured OCR output and webhooks. - [Parse Invoices in Python](https://space-ocr.com/articles/parse-invoices-in-python/): practical Python examples for invoice parsing against the API. - [Extract Tables From Images to CSV](https://space-ocr.com/articles/extract-tables-from-images-to-csv/): turning photographed or scanned tables into CSV rows. - [Import Scanned Receipts Into Google Sheets](https://space-ocr.com/articles/import-scanned-receipts-into-google-sheets/): receipts to structured rows to Google Sheets. - [Digitize Receipts for Expense Reports](https://space-ocr.com/articles/digitize-receipts-for-expense-reports/): a practical expense-report digitization workflow. - [Organize Scanned Documents with Folders and Sheets](https://space-ocr.com/articles/organize-scanned-documents-with-folders-and-sheets/): keeping scanned documents queryable without a database. - [Convert PDF to Searchable CSV: Structured Data Extraction](https://space-ocr.com/articles/convert-pdf-to-searchable-csv-a-guide-to-structured-data-extraction/): from static PDF pages to searchable, structured CSV. - [Implementing a Structured-Field OCR API with Bounding Boxes](https://space-ocr.com/articles/implementing-a-structured-field-ocr-api-with-bounding-boxes-in-2026/): implementation notes for structured-field extraction with verifiable coordinates. - [Low-Cost OCR for Startups](https://space-ocr.com/articles/low-cost-ocr-for-startups-a-pragmatic-guide-to-zero-bloat-document-processing/): a pragmatic guide to zero-bloat document processing. - [We ran Mistral Document AI on our 8 hardest test cases. Here is what the score table doesn't show](https://space-ocr.com/articles/space-ocr-vs-mistral-ocr-benchmark/): the controlled side-by-side behind the landing-page figures — 8 cases over 7 documents · 463 fields · 3 runs each · August 2026, with field accuracy, run-to-run stability and the coordinate gap a score table cannot show. - [How space-ocr is different from LLM OCR: verifiable, structured extraction](https://space-ocr.com/articles/llm-ocr-vs-space-ocr/): what a JSON-extraction call to a general multimodal model does not include — source coordinates cross-checked against a separate OCR pass, a `verified` verdict per value, and `data.review.flagged` as the list of what to look at. - [Turn Photos of Documents into a Spreadsheet](https://space-ocr.com/articles/photos-of-documents-to-spreadsheet/): phone photos of receipts, invoices and forms into spreadsheet rows — tilted shots and mixed languages handled without a language setting, every value carrying the place it was read from. - [スキャンしたPDFをExcelに変換する(日本語・文字化け対策)](https://space-ocr.com/articles/scanned-pdf-to-excel-japan/): Japanese-language guide to converting scanned PDFs to Excel without mojibake. ## Optional - [About](https://space-ocr.com/about): company information. - [Consultation](https://space-ocr.com/consult): talk to the team about document-processing workflows. - [Partner terms](https://space-ocr.com/partners/): terms for contract developers who build space ocr into client projects — 10% cash share of the project's API usage fees, the two billing arrangements, and how support and service notice work. - [Articles index](https://space-ocr.com/articles/): all articles. - [Guides index](https://space-ocr.com/guides/): all long-form guides. - [Sitemap](https://space-ocr.com/sitemap.xml): full URL list including all locale variants.