Skip to main content

docgate

docgate logo

Spend the minimum on document AI, and know exactly what you didn't check.

Documentation · PyPI · GitHub

Most document pipelines extract every page and return an empty string when a page could not be read. docgate is deliberately different: it extracts the free text layer first, bounds raster output before allocating memory, and always returns a receipt that distinguishes blank, skipped, and unknown pages.

Formats

Format Local extraction Model call needed
PDF Embedded text per page Only for pages without usable text
DOCX Paragraphs and tables No
XLSX One logical page per worksheet No
PPTX One logical page per slide No
TXT, Markdown, HTML Native text No

Install Office readers when needed:

pip install "docgate[office]"

For a non-PDF byte stream, pass a file path today; stream adapters are an intentional extension point for future formats.

import docgate

result = docgate.read("bundle.pdf")
print(result.text)
print(result.receipt.complete)
print(result.receipt.unknown)

Install

pip install docgate

What it can do

  • Audit a document locally without an API key, render, or provider call.
  • Extract usable embedded PDF text for free, before calling any model.
  • Output-bounded page rasterisation. A pathological declared page size cannot allocate an unbounded image.
  • A Clamp is reported whenever requested output is reduced.
  • A Receipt accompanies every result. Pages that were not read are explicit Unknown records, never silently omitted.
  • Send only pages without a usable text layer to your OCR/vision provider.
  • Fall back across providers and record every failed attempt as a Degradation.

Audit before processing

Run a local preflight before selecting or paying a provider:

docgate audit bundle.pdf
docgate audit bundle.pdf --json
docgate audit bundle.pdf --report audit.html

The audit reports text-layer coverage, pages requiring OCR, embedded images, predicted output clamps, and bounded pixel-buffer estimates. Its JSON excludes the filename, source path, document text, images, and credentials. An incomplete inspection exits with code 2.

The HTML report is a static, offline page with a color-coded coverage map. It contains no document content or external resources and can be shared with a team as an operational preflight artifact.

Process many documents safely

read_many() provides bounded concurrency, per-document isolation, stable input ordering, and aggregate receipts. One corrupt document cannot discard the rest of the batch.

batch = docgate.read_many(
    documents,
    providers=[ocr, vision],
    max_concurrency=8,
    per_document_budget=Decimal("0.02"),
    total_budget=Decimal("10.00"),
)

print(batch.receipt)

Sources are consumed lazily with bounded in-flight work. Concurrent total-budget reservations are atomic, and batch progress events contain only a zero-based input index and operational status.

Bring any OCR or vision model

docgate is provider-neutral. It is not limited to OpenAI, Anthropic, Mistral, or GLM. The ecosystem includes AWS Textract, Azure Document Intelligence, Google Document AI and Gemini, specialist parsers such as LlamaParse and Reducto, hosted vision services such as Together and Groq, and local engines such as Tesseract, PaddleOCR, EasyOCR, Surya, and docTR.

Today Docgate ships lightweight native Mistral and Anthropic adapters, an OpenAI-compatible vision route, and CallableProvider for any existing SDK or local/private engine. Native capability-aware adapters are being added without turning their SDKs into core dependencies. See the researched provider landscape for current support versus the adapter roadmap.

from decimal import Decimal
import docgate

vision = docgate.OpenAICompatibleProvider(
    model="your-vision-model",
    api_key="...",
    base_url="https://your-openai-compatible-host/v1",
    cost_per_page=Decimal("0.004"),
)

result = docgate.read("bundle.pdf", providers=[vision])

Claude uses its native Messages API:

claude = docgate.AnthropicProvider(model="your-claude-vision-model", api_key="...")
result = docgate.read("bundle.pdf", providers=[claude])

For a local or custom engine:

local_ocr = docgate.CallableProvider("my-ocr", lambda png_pages: [run_ocr(p) for p in png_pages])
result = docgate.read("bundle.pdf", providers=[local_ocr])

Control spend and retain private cache hits

Pass a hard budget to stop before an extra paid page. The partial result stays useful and every page not attempted is listed as BUDGET_EXHAUSTED.

result = docgate.read("bundle.pdf", providers=[vision], budget=Decimal("0.10"))
assert result.receipt.spent <= Decimal("0.10")

Use SQLiteCache for durable local cache hits across runs. Cache entries use a SHA-256 digest of the rendered PNG bytes plus provider identity — never a path or filename. Failed and empty responses are never stored.

cache = docgate.SQLiteCache(".docgate-cache.db")
result = docgate.read("bundle.pdf", providers=[vision], cache=cache)

Stay available when providers fail

Provider fallbacks are tried in order. Repeated failures open a circuit breaker, so a dead key does not consume a full timeout for every page. After its cooldown, docgate automatically allows one recovery probe; a successful call closes the breaker. Every skipped or failed provider is recorded in receipt.degraded.

docgate.configure(
    providers=[primary_vision, backup_vision],
    breaker_failures=3,
    breaker_cooldown_s=120,
)

Use ExecutionPolicy when provider calls need bounded waiting and retries:

policy = docgate.ExecutionPolicy(
    timeout_s=30,
    max_attempts=3,
    initial_backoff_s=0.5,
    max_backoff_s=4,
)
result = docgate.read("bundle.pdf", providers=[primary, fallback], execution_policy=policy)

Timeouts and failed attempts remain visible in receipt.degraded. For progress reporting, pass on_progress; events include page/provider/attempt metadata but never document text, images, filenames, or credentials. See the reliability guide for the thread-timeout boundary and fallback behavior.

Escalate OCR to vision only when needed

A provider returning weak text is not the same as a provider outage. Use a TextQualityGate to reject visibly insufficient OCR output and try the next provider, usually a vision model. The receipt records this as quality_gate_rejected, while preserving the OCR provider's health.

ocr = docgate.MistralOcrProvider(api_key="...", cost_per_page=Decimal("0.004"))
vision = docgate.OpenAICompatibleProvider(model="your-vision-model", api_key="...")

result = docgate.read(
    "employment-bundle.pdf",
    need="CV and experience letter",
    selection="top_k",
    max_paid_pages=3,
    providers=[ocr, vision],
    quality_gate=docgate.TextQualityGate(min_characters=40, min_alphanumeric_characters=25),
)

See OCR-to-vision routing for the complete mixed-document workflow and its limits.

Enforce where documents may go

Companies can prevent sensitive pages from crossing a trust boundary while still using the same provider ladder. Policy runs before cache lookup or model invocation; a denial is visible on the receipt and never silently bypassed.

local = docgate.CallableProvider("local-ocr", run_ocr, trust_zone="local")
remote = docgate.OpenAICompatibleProvider(model="your-vision-model", api_key="...")

result = docgate.read(
    "confidential.pdf",
    providers=[local, remote],
    provider_policy=docgate.TrustZonePolicy(("local",)),
)

If local OCR cannot read a page, it becomes Unknown; docgate will not send it to the external fallback. Custom policies receive cost and provider metadata, never document text, images, filenames, paths, or credentials. See provider governance.

Keep estimates auditable

Pricing is supplied by your application because provider pricing changes. Attach the date and source you verified; every provider call records its estimate in receipt.quotes. Cache entries include the exact image bytes plus a non-secret provider/model/prompt identity, so a model or prompt change cannot reuse old output.

from datetime import date
from decimal import Decimal

pricing = docgate.PricingProfile(
    per_page=Decimal("0.004"),
    verified_on=date(2026, 8, 30),
    source="https://provider.example/pricing",
)
ocr = docgate.MistralOcrProvider(api_key="...", pricing=pricing)

read() is a convenience wrapper around plan() then execute(). Planning is local-only and does not invoke any provider.

plan = docgate.plan("bundle.pdf", need="degree certificate")
print(plan.estimated_cost)       # Decimal('0') in v0.1
print(plan.candidate_pages)      # ranked from local text-layer signals
result = docgate.execute(plan)

Plans and receipts also have versioned JSON forms for CI, job metadata, and audit logs. They exclude document text, source paths, images, and secrets by default.

docgate plan bundle.pdf --need "degree certificate" --selection top_k \
  --max-paid-pages 1 --json

docgate read notes.docx --json

An incomplete docgate read exits with code 2. Use --include-text only when the JSON consumer intentionally needs extracted document content.

Select before you spend

Pass selection="candidates" to read only pages with local keyword evidence, or selection="top_k" with max_paid_pages to cap work at the highest-ranked pages. Every excluded page is present in receipt.unknown as SKIPPED_BY_PLAN; partial output is always visible.

result = docgate.read(
    "bundle.pdf",
    need="degree certificate",
    selection="top_k",
    max_paid_pages=1,
    providers=[vision],
)

What docgate does not do

  • It is not an OCR engine or hosted model service.
  • It is not RAG, a vector store, or an agent framework.
  • It does not interpret, score, verify, or make decisions about documents.
  • It has no telemetry or phone-home behaviour.

Status

v0.3 is in development. The current main branch adds local document audits, offline HTML coverage reports, and bounded multi-document execution on top of selective paid-page routing, hard budgets, stable receipts, and the CLI. A local cost/savings quote and policy-as-code are the next production controls.

License

Apache-2.0.

Releases

Releases are published from GitHub Actions using PyPI Trusted Publishing—no long-lived PyPI token is stored in this project. See the release guide.

Metadata

Release files for docgate 0.3.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for docgate 0.3.0
File Size Uploaded
docgate-0.3.0.tar.gz 787.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for docgate 0.3.0
File Interpreter ABI Platform
docgate-0.3.0-py3-none-any.whl Python 3 none any Details

Total release size: 822.0 kB

Release files / docgate-0.3.0.tar.gz

Download URL docgate-0.3.0.tar.gz
Size 787.9 kB
Tags Source
SHA-256 checksum
How to use checksums
c12104ada8631ffa74f420d597a6b1942d7501c98fc00e3eb88ffa4d35ec6359
BLAKE2b-256 checksum
How to use checksums
ac84038b958ab785f3ea118cf98ef605a9634b42c5c94ca557cba620a10b8288
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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 Aug 31, 2026.

Transparency log

Release files / docgate-0.3.0-py3-none-any.whl

Download URL docgate-0.3.0-py3-none-any.whl
Size 34.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
1f805a9f19894408f48e16f5d963c1503ddf03cf73698afcf63922c523b3acf6
BLAKE2b-256 checksum
How to use checksums
17092b6342ac4cd7b5b6da3d767a05d615a31f2a8bcad1671ac9c580cb5152dc
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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 Aug 31, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.3.0 This release

2 release files

0.2.1

2 release files

0.2.0

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page