Skip to main content

pdftagger — auto-tag PDFs for accessibility from Python

Official Python SDK for the PDF Tagger API. Upload a PDF, submit a tagging job, and download a PDF/UA-tagged, accessible PDF — with correct reading order, headings, tables, and alt text. Tag one PDF, or up to 100 at once as a batch.

pip install pdftagger

How to auto-tag PDFs with the PDF Tagger SDK

Get an API key from the Developers page, then:

from pdftagger import PdfTaggerClient

client = PdfTaggerClient(api_key="sk_live_...")

# Upload, submit, and wait for the tagged PDF — one call.
job = client.tag_pdf("report.pdf", ocr=True)

# Save the accessible, tagged PDF.
client.download_result(job, "report.tagged.pdf")

That's the whole flow. tag_pdf() uploads the file, submits the job, polls until it finishes, and returns the completed job. Each job costs one credit per page, drawn from the same balance as the web app.

Options

job = client.tag_pdf(
    "scan.pdf",
    ocr=True,            # recover text from image-only / scanned pages
    embed_fonts=True,    # embed a non-embedded font where it is safe to do so
    idempotency_key="order-1234",  # safe to retry without double-charging
)

Don't want to block? Submit and poll yourself:

job = client.tag_pdf("report.pdf", wait=False)   # returns immediately
job = client.wait_for_job(job)                    # ...poll later

Many PDFs at once: batches

Tag up to 100 PDFs as one batch. tag_pdfs() uploads every file, submits them in one request, and waits until every job has finished:

batch = client.tag_pdfs(["a.pdf", "b.pdf", "c.pdf"], name="Q3 reports", ocr=True)

print(batch.counts)                 # BatchCounts(total=3, processing=0, completed=3, failed=0)
for job in batch.failed_jobs:       # a failed job does not raise for a batch
    print(job.filename, job.error_code, job.error_message)

client.download_batch_results(batch, "tagged/")   # saves each completed PDF

A batch is all or nothing: if any file is not a PDF, has more than 750 pages, or the credits do not cover every page, nothing is charged and the call raises. BadRequestError.errors lists each bad file by its position:

from pdftagger import BadRequestError

try:
    client.submit_batch([upload_a.key, upload_b.key])
except BadRequestError as e:
    for problem in e.errors:        # {"index": 1, "type": "too_many_pages", "message": "..."}
        print(problem["index"], problem["message"])

Drive it yourself with submit_batch() (upload keys or BatchItems), get_batch() and wait_for_batch(). Each job in a batch is a normal job, so get_job(), get_job_result() and download_result() work on it.

Verifying webhooks

Register a webhook endpoint on the Developers page to be notified when a job finishes. Verify every delivery before trusting it — pass the raw request body:

from pdftagger import verify_signature
from pdftagger.webhooks import SIGNATURE_HEADER

event = verify_signature(
    payload=raw_body_bytes,                 # the exact bytes you received
    header=request.headers[SIGNATURE_HEADER],
    secret="whsec_...",                     # the endpoint secret
)

if event.type == "job.completed":
    url = client.get_job_result(event.job.id).result_url
elif event.type == "batch.completed":
    batch = client.get_batch(event.batch.id)    # every job in the batch has finished

verify_signature recomputes the HMAC-SHA256 signature over the raw body, compares it in constant time, and rejects a stale timestamp (replay). It raises WebhookVerificationError on any failure. A delivery can arrive more than once, so dedupe on event.id.

Low-level API

Every endpoint is available directly if you want to drive the flow yourself:

client.get_account()                       # account + credit balance
upload = client.create_upload("report.pdf")
client.upload_file(upload, "report.pdf")
job = client.submit_job(key=upload.key, ocr=True)
job = client.get_job(job.id)               # processing / completed / failed
result = client.get_job_result(job.id)     # short-lived download URL

Errors

All errors derive from PdfTaggerError.

Exception When
AuthenticationError 401 — key missing, invalid, or revoked
BadRequestError 400 — invalid request (for a batch, .errors lists each bad file)
InsufficientCreditsError 402 — not enough credits (a batch charges nothing)
NotFoundError 404 — no such job/resource for this account
ConflictError 409 — idempotency conflict, or result not ready
RateLimitError 429 — rate limited (.retry_after seconds)
JobFailed the job finished with a failed status (credits are refunded)
JobTimeout the job, or the batch, did not finish within the poll timeout
WebhookVerificationError a webhook signature could not be verified

Requirements

  • Python 3.9+
  • httpx (installed automatically)

License

MIT

Metadata

Release files for pdftagger 0.1.1

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

Source distribution (sdist)

Source distribution for pdftagger 0.1.1
File Size Uploaded
pdftagger-0.1.1.tar.gz 20.4 kB Details

Built distribution (wheel)

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

Total release size: 37.1 kB

Release files / pdftagger-0.1.1.tar.gz

Download URL pdftagger-0.1.1.tar.gz
Size 20.4 kB
Tags Source
SHA-256 checksum
How to use checksums
6746c67338fe719bcae0ae40a1228ad0b64c9069903948372d63463d07c0489f
BLAKE2b-256 checksum
How to use checksums
81a13859c594e6b4cfd775480fe4a5373a6d51c5ee4ec8abb129cfa92efb66e3
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 Oct 14, 2026.

Transparency log

Release files / pdftagger-0.1.1-py3-none-any.whl

Download URL pdftagger-0.1.1-py3-none-any.whl
Size 16.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
18532a95bf872e05df20f7076da2b010e10d8ac53ef2e406acaa0e4039803363
BLAKE2b-256 checksum
How to use checksums
f2a7cfdd713b9cb661b1b41d8da7c4a3e1e27a247952b82a12c8ba2238422d97
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 Oct 14, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.1 This release

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