Metadata-Version: 2.5
Name: pdftagger
Version: 0.1.1
Summary: Official Python SDK for the PDF Tagger API — auto-tag PDFs for PDF/UA accessibility.
Project-URL: Homepage, https://pdf-tagger.com
Project-URL: Documentation, https://pdf-tagger.com/api/reference
Project-URL: Source, https://github.com/PDF-Tagger/pdftagger-python
Project-URL: Issues, https://github.com/PDF-Tagger/pdftagger-python/issues
Author: PDF Tagger
License: MIT License
        
        Copyright (c) 2026 PDF Tagger
        
        Permission is hereby granted, free of charge, to any person obtaining a copy
        of this software and associated documentation files (the "Software"), to deal
        in the Software without restriction, including without limitation the rights
        to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        copies of the Software, and to permit persons to whom the Software is
        furnished to do so, subject to the following conditions:
        
        The above copyright notice and this permission notice shall be included in all
        copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        SOFTWARE.
License-File: LICENSE
Keywords: accessibility,auto-tag,pdf,pdf-ua,pdf/ua,sdk,section-508,tagging,wcag
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.9
Requires-Dist: httpx>=0.24
Provides-Extra: dev
Requires-Dist: build>=1.0; extra == 'dev'
Requires-Dist: pytest>=7; extra == 'dev'
Requires-Dist: respx>=0.20; extra == 'dev'
Description-Content-Type: text/markdown

# pdftagger — auto-tag PDFs for accessibility from Python

Official Python SDK for the [PDF Tagger](https://pdf-tagger.com) 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.

```bash
pip install pdftagger
```

## How to auto-tag PDFs with the PDF Tagger SDK

Get an API key from the [Developers page](https://pdf-tagger.com/developers), then:

```python
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

```python
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:

```python
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:

```python
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:

```python
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 `BatchItem`s), `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:

```python
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:

```python
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`](https://www.python-httpx.org/) (installed automatically)

## Links

- API reference: https://pdf-tagger.com/api/reference
- Create an API key: https://pdf-tagger.com/developers

## License

MIT
