Metadata-Version: 2.4
Name: html2dochub
Version: 0.1.0
Summary: Official Python client for the HTML2DocHub HTML-to-PDF API.
Project-URL: Homepage, https://html2dochub.com
Project-URL: Documentation, https://html2dochub.com/docs
Project-URL: Source, https://github.com/megovind/html2dochub/tree/main/sdks/python
Project-URL: Issues, https://github.com/megovind/html2dochub/issues
Project-URL: Changelog, https://github.com/megovind/html2dochub/blob/main/sdks/python/CHANGELOG.md
Author-email: HTML2DocHub <hello@html2dochub.com>
License-Expression: MIT
License-File: LICENSE
Keywords: html to image,html to pdf,html2dochub,pdf,pdf api,pdf generation
Classifier: Development Status :: 3 - Alpha
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: Topic :: Internet :: WWW/HTTP
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.9
Requires-Dist: httpx<1.0,>=0.24
Provides-Extra: dev
Requires-Dist: mypy>=1.8; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
Requires-Dist: pytest>=7.4; extra == 'dev'
Requires-Dist: respx>=0.20; extra == 'dev'
Requires-Dist: ruff>=0.4; extra == 'dev'
Description-Content-Type: text/markdown

# html2dochub

Official Python client for the [HTML2DocHub](https://html2dochub.com) HTML-to-PDF API.

```bash
pip install html2dochub
```

Pay-per-use HTML → PDF rendering on real Chromium. No subscriptions, no
binary dependencies on your server, no Docker Chromium layer to maintain.

## Quick start

```python
from html2dochub import Client

client = Client(api_key="sk_live_YOUR_KEY")

pdf_bytes = client.render(
    html="<h1>Hello from Python</h1>",
    options={"format": "A4", "margin": "18mm"},
)

with open("out.pdf", "wb") as f:
    f.write(pdf_bytes)
```

Get your API key at [html2dochub.com/dashboard/api-keys](https://html2dochub.com/dashboard/api-keys).
New accounts get free starter credit — enough to render around 100 pages.

## Async (FastAPI, asyncio)

```python
from html2dochub import AsyncClient

async with AsyncClient(api_key="sk_live_YOUR_KEY") as client:
    pdf = await client.render(html="<h1>Hello</h1>", options={"format": "A4"})
```

## Render from a URL

```python
pdf = client.render(url="https://example.com/invoice/42")
```

## Async mode with webhook (for long reports)

Large renders (50+ pages) should run in the background so your request
doesn't block. Call `render_async` with a `webhook_url` — we POST the
completed job back to it.

```python
job = client.render_async(
    html=big_html,
    webhook_url="https://yours.com/webhooks/pdf-ready",
    tag="q1-report",
    idempotency_key="report-42-v3",
)
# job.status == "pending"
```

In your webhook handler:

```python
@app.post("/webhooks/pdf-ready")
def pdf_ready(payload: dict):
    if payload["status"] == "completed":
        pdf_bytes = client.download(payload["download_url"])
        store(payload["id"], pdf_bytes)
```

## Error handling

Every API error surfaces as a subclass of `HTML2DocHubError`, so a single
`except` catches them all. Specific classes let you react differently:

```python
from html2dochub import (
    Client,
    AuthenticationError,
    InsufficientFundsError,
    RateLimitError,
    ValidationError,
    APIError,
)

try:
    pdf = client.render(html=html)
except InsufficientFundsError:
    # wallet is empty — prompt the user to top up
    ...
except RateLimitError as e:
    # hit the per-API-key throttle
    time.sleep(e.retry_after or 2)
except ValidationError as e:
    # the request body was malformed (exactly one of html/url required, etc.)
    ...
except AuthenticationError:
    # the API key is wrong or revoked
    ...
except APIError as e:
    # catch-all for anything else (5xx after retries, unknown status, etc.)
    ...
```

## Automatic retries

The client retries `429` and `5xx` responses with exponential backoff
(`0.5s`, `1s`, `2s`, up to `30s`). Retries are tunable:

```python
client = Client(api_key="...", max_retries=5, timeout=120)
```

`Retry-After` headers on `429` responses are honoured.

## Idempotency

Pass `idempotency_key` on any render to make the request safe to retry
without double-billing. Same key within the retention window returns the
original response instead of rendering again.

```python
client.render(
    html=invoice_html,
    idempotency_key=f"invoice-{invoice.id}-v{invoice.version}",
)
```

Useful inside Celery tasks, BullMQ workers, or any queue with at-least-once
delivery semantics.

## Configuration

```python
Client(
    api_key="sk_live_...",
    base_url="https://api.html2dochub.com",  # override for self-hosted
    timeout=60,                              # seconds, applied to each HTTP call
    max_retries=3,                           # for 429 and 5xx responses
    user_agent="my-app/1.2",                 # prepended to the default UA
)
```

## Type safety

`html2dochub` ships with inline type hints and a `py.typed` marker. `mypy`
in strict mode is clean on the public API.

## API reference

- `Client` / `AsyncClient`
  - `render(html=..., url=..., type="pdf", options=..., tag=..., idempotency_key=...) → bytes`
  - `render_job(...) → Job` — same as render but returns the Job object with metadata
  - `render_async(webhook_url=..., ...) → Job` — queue a background render
  - `get_job(job_id) → Job` — poll the state of a job
  - `download(url) → bytes` — fetch a signed download URL
  - `close()` / `aclose()` — release the underlying HTTP pool

- `Job` dataclass: `id`, `status`, `type`, `mode`, `output_pages`, `final_cost`,
  `download_url`, `created_at`, `started_at`, `completed_at`, `raw` (preserves
  unknown fields).

Full API documentation: [html2dochub.com/docs](https://html2dochub.com/docs).

## License

MIT — see [LICENSE](./LICENSE).
