Metadata-Version: 2.4
Name: ironfang-render
Version: 2.0.0
Summary: Ironfang Render API client: screenshots, PDFs, QR codes, template images, clips and site previews from a URL or HTML.
Author: Ironfang Ltd
License-Expression: MIT
Project-URL: Homepage, https://ironfang.com/render
Project-URL: Documentation, https://ironfang.com/render/docs
Keywords: screenshot,pdf,qr,html-to-image,ironfang
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Operating System :: OS Independent
Classifier: Topic :: Internet :: WWW/HTTP
Requires-Python: >=3.10
Description-Content-Type: text/markdown

# ironfang-render

The official Python client for the [Ironfang Render](https://ironfang.com/render)
API: screenshots, PDFs, QR codes, template images, clips and site previews
from a URL or from HTML you send. No dependencies; Python 3.10 and later.

Formerly `ironfang-renderwolf`; 2.0.0 removed `ironfang_renderwolf` and the old
`Renderwolf` and `RenderwolfError` names. Webhooks are signed with the
`Ironfang-*` headers.

```sh
pip install ironfang-render
```

```python
import os
from ironfang_render import IronfangRender

with IronfangRender(api_key=os.environ["IRONFANG_API_KEY"]) as rw:
    shot = rw.screenshot(url="https://example.com", full_page=True)
    shot.save("page.png")
    print(shot.credits, shot.render_ms, shot.request_id)
```

Every render returns the bytes plus what the API reports about them: content
type, credits charged (zero on a cache hit), render time and the request id
to quote to support. Long outputs stream: `save()` and `iter_bytes()` never
hold a whole clip in memory.

## Change the page before capture

```python
rw.screenshot(
    html="<div class='chat-widget'>Chat</div><details id='details'><summary id='show-more'>Pricing</summary><p>Expanded pricing</p></details>",
    css=".chat-widget { display: none !important; }",
    script="document.querySelector('#show-more').textContent = 'Show pricing';",
    actions=[
        {"type": "click", "selector": "#show-more"},
        {"type": "wait_for_selector", "selector": "#details[open]"},
    ],
).save("expanded.png")
```

The same options work with `rw.pdf()` and URL input. After normal load/waits,
Ironfang Render applies CSS, awaits the script, then executes actions in order
before settling and capture. JavaScript runs in the target page context as an
async function body; use `await` or return a promise for asynchronous work. It
has a five-second limit (or the remaining render timeout, if shorter).

CSS is limited to 64 KiB of UTF-8, script to 16 KiB, and actions to 20. Actions
are `click`, `hover`, `wait_for_selector` (presence), and `delay` with
`duration_ms`; combined delays may total at most 10,000 ms. Selectors are limited
to 1,024 UTF-8 bytes and share the render timeout. Invalid input returns
`bad_request`; script/action execution errors return sanitized `render_failed`
messages. A screenshot cache hit does not re-execute controls; use `no_cache`
for fresh execution. [Full reference](https://ironfang.com/render/docs#page-controls).

## Durable jobs

```python
job = rw.submit_job("pdf", {"html": invoice_html, "paper_format": "a4"}, idempotency_key=f"invoice-{invoice.id}")
done = rw.wait_for_job(job["id"])
if done["status"] == "succeeded":
    rw.job_result(job["id"]).save(f"invoice-{invoice.id}.pdf")
```

## Verifying a webhook

Run the check over the raw request body, before parsing it.

```python
from ironfang_render import verify_webhook

ok = verify_webhook(
    secret=os.environ["RENDER_WEBHOOK_SECRET"],
    timestamp=request.headers["Ironfang-Timestamp"],
    signature=request.headers["Ironfang-Signature"],
    payload=request.get_data(),
)
```

## Errors

Failures raise `IronfangRenderError` with `status`, the API's `code`
(`quota_exhausted`, `render_failed`, ...) and `request_id`. Synchronous
renders are never retried by the client: a render that timed out may already
have been charged, and retrying it blind doubles the bill.
