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.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 pdftagger 0.1.0
File Size Uploaded
pdftagger-0.1.0.tar.gz 20.4 kB Details

Built distribution (wheel)

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

Total release size: 37.1 kB

Release files / pdftagger-0.1.0.tar.gz

Download URL pdftagger-0.1.0.tar.gz
Size 20.4 kB
Tags Source
SHA-256 checksum
How to use checksums
88a336d5a9eebdf830a88a64ed2f71c10c852ff41a9e6b057614302cabebdfec
BLAKE2b-256 checksum
How to use checksums
259c9da7819b2f5c5037b9455bd59b499cdfe124318c921d9c50c90b49bc28f3
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.0-py3-none-any.whl

Download URL pdftagger-0.1.0-py3-none-any.whl
Size 16.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
6b1c10a1f775a08d3c566ab3237a6e0d4cd17c3621c7f9674174baa972be80ed
BLAKE2b-256 checksum
How to use checksums
5cdb0dcade89ae0a1648c6dddc7a00c67cce3c63a8f472f1905674395ad4f1ca
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

0.1.1

2 release files

This release

0.1.0 This release

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