Skip to main content
Pre-release

This release is a pre-release and may not be stable for production use.

ocr-batch

Batch OCR a directory tree of PDFs two ways:

  • locally, with PyMuPDF, extracting whatever text layer each PDF already has (fast, free, exact for born-digital documents);
  • remotely, with the Mistral asynchronous batch OCR API (discounted, handles scans and handwriting).

Both runs use the same page separators, so the two renderings of a document can be diffed line by line.

Install

Requires Python 3.14+.

With uv, no checkout needed:

uvx --from git+https://github.com/nidrissi/ocr-batch ocr-batch run ./applications ./ocr-output

Or from a clone:

git clone https://github.com/nidrissi/ocr-batch
cd ocr-batch
uv sync
uv run ocr-batch run ./applications ./ocr-output

Export your Mistral API key before running either command (or pass --api-key):

export MISTRAL_API_KEY=...

Usage

ocr-batch run ./applications ./ocr-output

run is the whole pipeline: it starts local extraction, uploads the PDFs, creates the batch job(s), waits for them, downloads and splits the results, and deletes the uploaded originals. The individual stages are also available, and submitted jobs are resumable because their ids and uploaded file ids are kept in <output_dir>/_state.json as they arrive:

Command What it does
ocr-batch submit IN OUT Local extraction, uploads, batch job creation. Records job ids and uploaded file ids.
ocr-batch status OUT One-shot progress report for a submitted run.
ocr-batch fetch OUT Waits (--no-wait to fail fast), downloads results, writes per-PDF outputs, cleans up. Idempotent.
ocr-batch cleanup OUT Deletes the uploaded originals from Mistral. Safe to re-run.
ocr-batch run IN OUT submit + fetch.

If a run is interrupted, the job keeps going on Mistral's side; pick it back up with ocr-batch fetch OUT. Recorded live jobs are not submitted twice: submit refuses to start while a job from a previous run is still live, and skips documents whose complete outputs already exist (--force overrides both).

Output layout

For applications/nested/smith.cv.final.pdf, under OUT/:

nested/smith.cv.final.native.txt    local PyMuPDF text, page-separated
nested/smith.cv.final.ocr.md        Mistral OCR markdown, page-separated
nested/smith.cv.final.ocr.json      raw per-document OCR response
_manifest.json                      custom_id -> relative path, hash, page count
_state.json                         resumable run state (job ids, upload ids)
_mistral_batch_results.jsonl        raw batch output as downloaded
_mistral_batch_errors.jsonl         per-request errors, when the batch reports any

Every suffix is appended to the full name, so smith.cv.final.pdf and smith.pdf never collide. Sources that would collide (a.pdf and a.PDF) are refused before anything is uploaded.

Useful flags

--no-ocr / --no-native        run only one of the two extraction paths
--force                       redo work whose outputs already exist
--jobs N                      local extraction processes (default: one per CPU)
--upload-workers N            concurrent uploads (default: 8)
--batch-size N                requests per batch job (default: 500)
--timeout-hours N             batch job timeout (default: 24)
--no-include-blocks           smaller responses: no per-block bounding boxes
--confidence-granularity ...  none | page | word | block (default: block)
--keep-remote                 keep the uploaded originals on Mistral
--cancel-on-interrupt         cancel the job on Ctrl-C instead of leaving it running

Exit codes: 0 success, 1 error, 3 finished with per-document failures, 130 interrupted.

Data handling

The PDFs are uploaded to Mistral (purpose: ocr, visibility: user) and made readable to the batch job through a signed URL valid for up to 168 hours. Once a job is terminal, the CLI attempts deletion even if fetching or splitting its results fails; ocr-batch cleanup re-runs any deletion that failed. As a backstop for a crash between an upload and its id reaching disk, uploads also carry a server-side expiry (job timeout + 24h by default).

Mistral Annotations are deliberately not used: no document_annotation_format and no bbox_annotation_format are ever sent.

Development

$ uv run pytest
$ uv run ruff check . && uv run ruff format --check .
$ uv run pyright

The test suite drives the whole pipeline through a fake client and never touches the network.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

ocr_batch-0.1.0b2.tar.gz (18.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

ocr_batch-0.1.0b2-py3-none-any.whl (23.1 kB view details)

Uploaded Python 3

File details

Details for the file ocr_batch-0.1.0b2.tar.gz.

File metadata

  • Download URL: ocr_batch-0.1.0b2.tar.gz
  • Upload date:
  • Size: 18.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for ocr_batch-0.1.0b2.tar.gz
Algorithm Hash digest
SHA256 f6cad6e6b60ac648acde7ff771347bbd03c7cd853dafb9e20f8a2d2a449ca361
MD5 8eed12949ae191aff2eef798abb7b08c
BLAKE2b-256 8741988a42f3ebedf9fdbf8569547c37620b309d599aa8f384b5dfca5b1f0187

See more details on using hashes here.

Provenance

The following attestation bundles were made for ocr_batch-0.1.0b2.tar.gz:

Publisher: release.yml on nidrissi/ocr-batch

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file ocr_batch-0.1.0b2-py3-none-any.whl.

File metadata

  • Download URL: ocr_batch-0.1.0b2-py3-none-any.whl
  • Upload date:
  • Size: 23.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for ocr_batch-0.1.0b2-py3-none-any.whl
Algorithm Hash digest
SHA256 b1bbb2100b458675c5f6256e212e111be31b87ab5c25f7c5030c5955eb5438ae
MD5 092205f22e26c45a3fb4863263db3ec2
BLAKE2b-256 8665ff9c9c3a793e405538b24e702545f7e9f9589d2ba5356a9657e4fd9be0c9

See more details on using hashes here.

Provenance

The following attestation bundles were made for ocr_batch-0.1.0b2-py3-none-any.whl:

Publisher: release.yml on nidrissi/ocr-batch

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

This release

0.1.0b2 This release

2 files

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page