ocracy
One façade over many OCR engines — plus a ledger to help you choose between them.
OCR ("read the text in this image") is solved a dozen ways: local engines
(Tesseract, EasyOCR, PaddleOCR), cloud APIs (Google Vision, AWS Textract, Azure),
and VLM readers — each with its own install, API, pricing, and language coverage.
ocracy gives you a uniform call, a browsable catalog of every option, and the
tools to wrap any of them.
import ocracy
text = ocracy.read_text("scan.png") # just the text, default backend
result = ocracy.ocr("scan.png") # full result: text + word boxes + confidence
print(result) # -> the recognized text
for word in result.words:
print(word.text, word.bbox.as_tuple, word.confidence)
Install
import ocracy is dependency-free. Install only the backends you use, via extras:
pip install "ocracy[tesseract]" # local, free (also needs the system `tesseract` binary)
pip install "ocracy[easyocr]" # local, pip-only
pip install "ocracy[google-vision]" # cloud API
pip install "ocracy[table]" # pandas, for catalog.to_dataframe()
Backends that ship today
The Blocks column is the one people get caught by: each backend reports exactly
one granularity, so result.words is empty on a line-level backend and
result.lines is empty on a word-level one — an empty list, not an error. See
Granularity just below.
| Backend | backend= id |
Local / Remote | Cost | Install | Blocks | Notable |
|---|---|---|---|---|---|---|
| Tesseract | tesseract |
local | free | ocracy[tesseract] (+ system tesseract) |
word | 100+ language baseline |
| EasyOCR | easyocr |
local | free | ocracy[easyocr] |
line | 80+ languages, scene text |
| RapidOCR | rapidocr |
local | free | ocracy[rapidocr] |
line | same PP-OCR models as Paddle, light CPU/ONNX — recommended default for local plain text |
| PaddleOCR | paddleocr |
local | free | ocracy[paddleocr] |
line | the platform: server models, GPU, on-ramp to tables/layout (PP-Structure) & VL — heavier install |
| ocrmac (Apple Vision) | ocrmac |
local (macOS) | free | ocracy[ocrmac] |
line | on-device, handwriting |
| pix2tex (LaTeX-OCR) | pix2tex-latex-ocr |
local | free | ocracy[pix2tex] |
text only | one printed equation → LaTeX, offline |
| TrOCR (handwritten) | trocr-handwritten |
local | free | ocracy[trocr] |
text only | one pre-segmented handwritten line, MIT weights |
| OCR.space | ocr-space |
remote | free tier | ocracy[ocr-space] |
word | zero-install REST |
| Google Cloud Vision | google-vision |
remote | paid (+free tier) | ocracy[google-vision] |
word | high accuracy, handwriting, structure |
| AWS Textract | aws-textract |
remote | paid (+free tier) | ocracy[aws-textract] |
word | business docs, handwriting (forms/tables in analyze mode) |
| Azure Document Intelligence | azure-document-intelligence |
remote | paid (+free tier) | ocracy[azure] |
word | layout/tables/handwriting, on-prem container option |
| Mistral OCR | mistral-ocr |
remote | paid (pay-as-you-go) | ocracy[mistral] |
text only | cheap VLM → clean Markdown + math/tables |
| Mathpix | mathpix |
remote | paid (+free tier) | ocracy[mathpix] |
text only | math/handwriting → LaTeX & Markdown |
| Claude Vision | claude-vision |
remote | paid | ocracy[anthropic] |
text only | prompt-driven "read + reason" over messy/mixed documents |
| GPT-4o Vision | gpt-4o-vision |
remote | paid | ocracy[openai] |
text only | same, via OpenAI; schema-constrained JSON output |
…plus 49 more engines/services catalogued in the ledger that you can turn into a working façade with one command (see Add a backend below).
Granularity: which backends fill .words, which fill .lines
Every backend emits exactly one granularity level. Nothing promotes words into
lines or splits lines into words, and result.words / result.lines are just
filters over the one flat blocks list — so asking for the level a backend does
not produce returns [], silently:
r = ocracy.ocr("scan.png", backend="rapidocr")
r.lines # populated
r.words # [] — rapidocr reports lines, and nothing raises
r = ocracy.ocr("scan.png", backend="tesseract")
r.words # populated
r.lines # [] — tesseract reports words
- word-level (
.wordspopulated,.linesempty):tesseract,google-vision,aws-textract,azure-document-intelligence,ocr-space. - line-level (
.linespopulated,.wordsempty):easyocr,rapidocr,paddleocr,ocrmac. - text only (
blocks == [], so both are empty andmean_confidenceisNone):claude-vision,gpt-4o-vision,mathpix,mistral-ocr,pix2tex-latex-ocr,trocr-handwritten. Useresult.text/result.markdown.
Iterating a result (for block in result:) yields whatever units that engine gave
you, at its own level — it is not "lines by default".
The ledger's bounding_boxes flag describes the engine, not ocracy's adapter.
claude-vision, mathpix and mistral-ocr are all bounding_boxes: true (their
APIs can return geometry) while ocracy's façades for them return text only. Treat
ocracy.find(bounding_boxes=True) as "engines worth wrapping for geometry", and
the list above as what you get today. If your code needs a level, assert it:
result = ocracy.ocr(img, backend=chosen)
if not result.words:
raise ValueError(f"{chosen} does not report word-level boxes")
Getting a backend running
Some backends need more than pip install (Tesseract's system binary, Paddle's
framework, GPU wheels, first-run model weights, or an API key). ocracy turns that
into structured, OS-aware guidance — handy for humans and AI agents alike:
ocracy.doctor() # what's usable now vs what each missing one needs
ocracy.check("paddleocr") # -> True/False (usable right now?)
print(
ocracy.requirements("paddleocr").instructions()
) # exact plan: pip + system deps + GPU + weights
ocracy.install(
"rapidocr", yes=True
) # run the pip install + verify (yes=False = dry run)
ocracy doctor · ocracy requirements paddleocr --gpu · ocracy install rapidocr --yes
requirements() even suggests a lighter alternative when one exists (e.g.
PaddleOCR → RapidOCR). The ocracy-install-backend skill drives all of this.
Three tiers of access
From simplest to most powerful — reach for the next tier only when you need it:
ocracy.ocr(img) # 1. facade, default (installed) backend
ocracy.ocr(img, backend="easyocr", languages=["en", "fr"])
ocracy.services.tesseract.read(img, psm=6) # 2. pick a backend explicitly
ocracy.services.tesseract.adapter # 3. the raw engine adapter, full control
Every backend returns the same OcrResult, so your code doesn't change when you
switch engines: result.text, result.words / result.lines, each block's
.bbox and .confidence (normalized to 0..1), result.mean_confidence,
result.markdown (when a backend produces it), and result.raw (the untouched
engine output).
The shape is uniform; the granularity is not. Each engine fills either
.words or .lines (or neither), and the other is an empty list rather than an
error — see Granularity
before writing code that depends on one.
Command line
Install the CLI extra (pip install "ocracy[cli]") for an ocracy command:
ocracy read scan.png # print recognized text
ocracy read scan.png --backend easyocr --languages en,fr
ocracy read scan.png --output json # text + blocks (boxes, confidence)
ocracy backends # backends you can run now
ocracy backends --capability math
ocracy status # readiness table: all / implemented / set-up / tested
ocracy status --level set_up --names # one level + the "Name (website)" lists
ocracy status --run-tests # also OCR-test set-up backends (real calls for remotes)
ocracy find --local --free --handwriting # browse/filter the ledger
ocracy info google-vision # a backend's full record
ocracy scaffold surya # start a new façade from the ledger
ocracy validate tesseract # smoke-test a backend
Everything is also reachable as python -m ocracy <command>. cw
builds the parser from the function signatures in ocracy/tools.py; argcomplete gives
tab completion when activated.
The ledger — choose a backend with eyes open
ocracy ships a curated, data-driven catalog of every engine we researched —
not only the ones with a working façade — so you can compare on the axes that
matter (local vs remote, price, accuracy, languages, handwriting, math, tables,
privacy). The data lives in ocracy/data/backends.json, separate from code.
ocracy.catalog # <Catalog … backends … implemented …>
ocracy.catalog["google-vision"] # one backend's full record
ocracy.find(is_local=True, open_source=True) # filter the ledger
ocracy.find(handwriting="yes", is_remote=True)
ocracy.catalog.can("math") # engines that read formulas
ocracy.catalog.supports_language("Arabic")
ocracy.find(implemented=True) # only what ocracy can run today
ocracy.catalog.to_dataframe() # browse as a pandas table
ocracy.catalog.compare(["tesseract", "google-vision", "mathpix"])
implemented is computed live from the code, so the ledger never lies about what
actually runs. See ocracy/data/SCHEMA.md for every field. The full cited research
report behind the ledger is in misc/docs/ocr_landscape_research.md.
Getting API keys (remote backends)
Remote backends need a credential in an environment variable. ocracy tells you exactly which one — with a link to get it — the moment it's missing:
>>> ocracy.ocr("receipt.png", backend="ocr-space")
MissingCredentialError: No credential found for ocr-space (set one of: OCR_SPACE_API_KEY).
How to get a credential for ocr-space: Register a free API key by email; free tier
allows 25,000 requests/month. Get a key: https://ocr.space/ocrapi/freekey
| Backend | Environment variable(s) | Where to get a key |
|---|---|---|
ocr-space |
OCR_SPACE_API_KEY |
https://ocr.space/ocrapi/freekey (free, 25k/month) |
google-vision |
GOOGLE_APPLICATION_CREDENTIALS (service-account JSON path) |
https://cloud.google.com/vision/docs/setup |
aws-textract |
AWS_ACCESS_KEY_ID + AWS_SECRET_ACCESS_KEY + AWS_DEFAULT_REGION |
https://docs.aws.amazon.com/textract/latest/dg/getting-started.html |
azure-document-intelligence |
AZURE_DOCUMENT_INTELLIGENCE_KEY + AZURE_DOCUMENT_INTELLIGENCE_ENDPOINT |
https://learn.microsoft.com/azure/ai-services/document-intelligence/create-document-intelligence-resource |
mistral-ocr |
MISTRAL_API_KEY |
https://console.mistral.ai/api-keys |
mathpix |
MATHPIX_APP_ID + MATHPIX_APP_KEY |
https://mathpix.com/ocr-api |
The same guidance (and links for AWS, Azure, Mistral, OpenAI, Anthropic, Gemini)
lives in ocracy.credentials.CREDENTIAL_GUIDANCE, and powers the dynamic errors.
Tip: drop the variables in a .env file — ocracy soft-loads it via python-dotenv
if that package is installed.
Add a backend (wrap any engine in minutes)
The catalog is large; ocracy ships façades for a curated subset and gives you
the machinery to add any other one. Scaffold a backend straight from its ledger
entry, fill in the adapter, and validate it:
from ocracy.make_backend import scaffold_backend, validate_adapter
scaffold_backend(
"mathpix"
) # creates ocracy/backends/mathpix/ prefilled from the ledger
# ... implement adapter.py's _read (call the engine, return an OcrResult) ...
validate_adapter("mathpix") # smoke-test it end to end
The full, step-by-step process — including the adapter contract, input/output
normalization helpers, credential handling, and packaging — is captured as an
agent skill at ocracy/data/skills/ocracy-add-backend/SKILL.md. The
ocracy/backends/tesseract/ package is a complete worked example.
Agent skills (Claude Code & other AI agents)
ocracy ships four Anthropic-style skills (under ocracy/data/skills/,
installed with the package) so AI coding agents can both use and extend it:
| Skill | Audience | What it helps with |
|---|---|---|
ocracy |
users | OCR an image/PDF, choose & install a backend, read the result |
ocracy-choose-backend |
users | filter & compare the 64-backend ledger to pick the right engine |
ocracy-install-backend |
users / agents | get a backend running — heavy installs, system deps, GPU, weights, keys |
ocracy-add-backend |
developers | wrap a new engine behind ocracy's interface (scaffold → adapter → validate) |
In a repo using Claude Code they're discoverable via the .claude/skills/
symlink bridge; they also travel with pip install ocracy under
ocracy/data/skills/.
How it works
ocracy/base.py— the normalized types (OcrResult,TextBlock,BBox).ocracy/catalog.py— the ledger reader/filter (ocracy/data/backends.json).ocracy/registry.py— lazy backend discovery + default selection.ocracy/services.py— the three-tier service layer.ocracy/make_backend.py— the façade-building toolkit (BaseOcrAdapter,make_block,scaffold_backend,validate_adapter).ocracy/backends/<id>/— one subpackage per engine (config.py+adapter.py).ocracy/credentials.py— credential resolution for remote backends.ocracy/tools.py+ocracy/__main__.py— theocracyCLI (cw).
The architecture mirrors the sibling façade packages
denote (audio→symbol) and
aix (LLM providers).
Metadata
Release files for ocracy 0.1.10
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| ocracy-0.1.10.tar.gz | 250.0 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| ocracy-0.1.10-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 518.3 kB
Release files / ocracy-0.1.10.tar.gz
| Download URL | ocracy-0.1.10.tar.gz |
|---|---|
| Size | 250.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
765c967aa1afb5a49130cd13101c6431b23937df5e71c0690621c91cac1ebfe2
|
|
BLAKE2b-256 checksum How to use checksums |
ce6cc10a58234465d933c93e41350ddecd13355c974b1780dc4da92eef5b71dc
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.17 {"installer":{"name":"uv","version":"0.12.17","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / ocracy-0.1.10-py3-none-any.whl
| Download URL | ocracy-0.1.10-py3-none-any.whl |
|---|---|
| Size | 268.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
db08975233d2baedd00b3d57e7efbf5ed6e9eac57bf5bfe54ebc113288009a91
|
|
BLAKE2b-256 checksum How to use checksums |
f36cabc4f260e4efa35849cd79d195f85bbf0dbe70f25247000a073e3b1d5f34
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.17 {"installer":{"name":"uv","version":"0.12.17","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|