turboocr
Typed Python client for the TurboOCR server. Sync + async, HTTP + gRPC, layout-aware Markdown rendering, searchable-PDF generation.
- Install · Quickstart · What you get
- Examples · API reference · CLI · Errors
Install
pip install turboocr # HTTP client + CLI + searchable-PDF
pip install 'turboocr[grpc]' # add the gRPC transport
pip install 'turboocr[all]' # everything optional (currently == [grpc])
Requires Python 3.12+.
Quickstart
Start a TurboOCR server (the C++/CUDA OCR engine — this repo is just the Python client):
docker run --gpus all -p 8000:8000 -p 50051:50051 \
-v trt-cache:/home/ocr/.cache/turbo-ocr \
-e TABLE_BACKEND=slanext -e FORMULA_BACKEND=ppformulanet_s \
ghcr.io/aiptimizer/turboocr:latest
The default OCR_MODEL=tiny covers Latin + Chinese + Japanese; small/medium
trade speed for accuracy, and arabic, eslav, korean, thai, greek are
baked in too. The two backend env vars enable table → HTML and formula → LaTeX
recognition (strict per-request opt-ins). See the
TurboOCR repo for build-from-source,
benchmarks, and the full set of server env vars.
Then recognise an image and turn a PDF into Markdown:
from pathlib import Path
from turboocr import Client
with Client(base_url="http://localhost:8000") as client:
# Image OCR
img = client.recognize_image("page.png", layout=True, include_blocks=True)
print(f"{len(img.results)} text items, {len(img.blocks)} blocks")
print(img.text)
# PDF → Markdown file (rendered server-side: tables → HTML, formulas →
# LaTeX, figures embedded as data URIs — a real, self-contained .md)
Path("paper.md").write_text(client.pdf_markdown("paper.pdf", dpi=150))
# Tables + formulas as structured fields (strict opt-in, v3.1+ server)
rich = client.recognize_image("paper.png", tables=True, formulas=True)
for table in rich.tables:
print(table.html)
for formula in rich.formulas:
print(formula.latex)
# Searchable PDF (invisible text overlay)
overlay = client.make_searchable_pdf("scan.pdf", dpi=200)
open("scan.searchable.pdf", "wb").write(overlay)
That's the 80% case. Full runnable examples for async, gRPC, batch, retries,
custom httpx.Client, hooks, Markdown styling, folder pipelines, and more live
in examples/ — every script runs end-to-end against the bundled
ACME invoice fixture.
What you get
- Sync + async, HTTP + gRPC. Four clients (
Client,AsyncClient,GrpcClient,AsyncGrpcClient) with identical method surfaces. - Typed, immutable responses (pydantic v2). IDE autocomplete, and if a newer
server adds a field your SDK doesn't know about, parsing still succeeds — the
extra lands on
.model_extrainstead of crashing. - Layout-aware Markdown.
render_to_markdown(...)walks the reading order and maps each layout class (doc_title,display_formula,table, …) to a Markdown construct. Pluggable viaMarkdownStyle. - Searchable PDFs.
make_searchable_pdf(...)overlays an invisible text layer aligned to the page geometry. Auto-discovers a Unicode font for non-Latin scripts, or passfont_path=. - Production-friendly. Configurable retry policy (HTTP status + gRPC status
Retry-After), per-request timeouts, customhttpx.Client,on_request/on_responseevent hooks, uuid7X-Request-IDper call.
- Tables → HTML, formulas → LaTeX.
tables=True/formulas=True(server v3.1+, strict opt-in) populateresponse.tables[*].htmlandresponse.formulas[*].latex;client.capabilities()tells you what the running server has loaded. - Server-side Markdown.
client.pdf_markdown(...)converts a whole PDF in one call (as_pages=Truefor per-page chunks — the RAG-friendly shape);client.page_markdown(...)does a single image.render_to_markdown(...)stays for client-side, style-customizable rendering. - Per-page streaming.
client.stream(...)yields NDJSON events as each page completes, so you can start consuming page 1 while page N is still being OCR'd. - Precise exception hierarchy. Maps the server's error codes to typed exceptions — see Errors.
turbo-ocrCLI included in the default install.
Configuration
from turboocr import Client, RetryPolicy
client = Client(
base_url="http://localhost:8000", # or TURBO_OCR_BASE_URL env
api_key="sk-...", # or TURBO_OCR_API_KEY env
auth_scheme="bearer", # "bearer" | "x-api-key"
timeout=30.0,
default_headers={"X-Tenant": "acme"},
retry=RetryPolicy(attempts=5, backoff=0.5),
)
Pass http_client=httpx.Client(...) for custom TLS, connection limits, or
proxies — see examples/08_custom_httpx_client.py.
Retry defaults: HTTP {429, 502, 503, 504}, gRPC
{UNAVAILABLE, DEADLINE_EXCEEDED, RESOURCE_EXHAUSTED}, 3 attempts, exponential
backoff + jitter, Retry-After honoured. Tune via RetryPolicy(...) — see
examples/07_retry_and_timeout.py.
Errors
TurboOcrError
├── APIConnectionError # transport-level
│ ├── Timeout
│ ├── NetworkError
│ └── ProtocolError
├── InvalidParameter # 4xx: bad params / headers / dims
├── EmptyBody # 4xx: empty body / batch / PDF
├── BackendDisabled # tables/formulas/autorotate without that backend
│ └── LayoutDisabled # layout requested with DISABLE_LAYOUT=1
├── ImageDecodeError # bad bytes / bad base64
├── DimensionsTooLarge # image / PDF / batch over server limits
├── PoolExhausted # "Server at capacity" / SERVER_BUSY
├── PdfRenderError # PDF rasterization failed
├── InferenceTimeout # per-request deadline elapsed (504)
└── ServerError # 5xx, no specific code
Server-side exceptions carry .code, .status_code, and .payload. Transport
exceptions inherit from APIConnectionError.
| Symptom | Cause | Fix |
|---|---|---|
NetworkError: Connection refused |
server not running | start the docker container (above) |
DimensionsTooLarge |
image > MAX_IMAGE_DIM (default 16384) |
downscale, or raise the server limit |
LayoutDisabled |
server started with DISABLE_LAYOUT=1 |
restart without that env var |
BackendDisabled |
tables=True/formulas=True without the backend |
start with TABLE_BACKEND=slanext / FORMULA_BACKEND=ppformulanet_s |
PoolExhausted |
server queue full | retry with backoff, or scale PIPELINE_POOL_SIZE |
Timeout |
per-request timeout hit | pass timeout=N, or raise RetryPolicy.attempts |
CLI
turbo-ocr ocr page.png --output markdown --tables --formulas
turbo-ocr pdf doc.pdf --dpi 150 --output json
turbo-ocr markdown doc.pdf -o doc.md # server-side PDF → Markdown
turbo-ocr searchable-pdf doc.pdf -o out.pdf --font-path /path/to/font.ttf
turbo-ocr capabilities
turbo-ocr health --ready
--output accepts json | blocks | text | markdown. Reads TURBO_OCR_BASE_URL
and TURBO_OCR_API_KEY from the environment. Run turbo-ocr --help
for the full surface.
Logging
import logging
logging.getLogger("turboocr").setLevel(logging.DEBUG)
Emits method path -> status (Xms) [req=<short-id>] per HTTP request. Retry
warnings go to turboocr.retry / turboocr.grpc.retry. Searchable-PDF font
resolution logs to turboocr.searchable_pdf. Every HTTP request sends a uuid7
X-Request-ID header (gRPC uses x-request-id metadata).
Learn more
examples/— 14 runnable scripts (each runs against the bundled ACME invoice fixture, no server config needed beyondTURBO_OCR_BASE_URL)docs/— full docs source (MkDocs + mkdocstrings, deployed at https://aiptimizer.github.io/TurboOCR-python/). Preview locally withuv run --extra docs mkdocs serve -f docs/mkdocs.yml- Server compatibility:
SERVER_API_VERSION_MIN/SERVER_API_VERSION_MAX_EXCLUSIVEdocument the supported server range;extra="allow"on response models means additive server changes don't break parsing
Testing
pytest -q # offline (respx)
TURBO_OCR_BASE_URL=http://localhost:8000 pytest tests/integration -v
python examples/03_searchable_pdf.py # smoke test
License
MIT. See LICENSE.
turboocr.com · ⭐ Star TurboOCR on GitHub
Sponsored by Miruiq — AI-powered data extraction from PDFs and documents — and DiaIQ.
Metadata
Release files for turboocr 0.3.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| turboocr-0.3.0.tar.gz | 263.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| turboocr-0.3.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 330.8 kB
Release files / turboocr-0.3.0.tar.gz
| Download URL | turboocr-0.3.0.tar.gz |
|---|---|
| Size | 263.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
a4a39bbebde54ada421e665f13d3a6ca9ba76170eb4b09b4064e9dc0ac01d231
|
|
BLAKE2b-256 checksum How to use checksums |
99b556edb467ba0ef0bb5a5361cfdd1e169ce0eecc9037b774a24f8e23fd6476
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 16, 2026.
Transparency logRelease files / turboocr-0.3.0-py3-none-any.whl
| Download URL | turboocr-0.3.0-py3-none-any.whl |
|---|---|
| Size | 67.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
7ed66796191915c1fb0873207768f28430f906708480ce9cbd9536fe8c862770
|
|
BLAKE2b-256 checksum How to use checksums |
960e471fe20ef324ba8746c4da0a08b40ad81d9290e28cbf33f7b9d81a622832
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 16, 2026.
Transparency log