Renderwolf · API reference
The docs are the contract
Everything the API does, on one page. If it isn't documented here, it isn't part of the API.
Quickstart
All endpoints live under https://api.ironfang.uk. Grab an API key from the portal, then render your first screenshot:
curl -X POST https://api.ironfang.uk/v1/screenshot \
-H "Authorization: Bearer rw_live_..." \
-H "Content-Type: application/json" \
-d '{"url": "https://example.com", "width": 1280}' \
--output shot.png Successful render calls return the binary asset directly (image/png, image/jpeg or application/pdf) - no JSON envelope, no base64. Everything else returns JSON.
Authentication
Every request carries your API key as a bearer token: Authorization: Bearer rw_live_.... Keys are created and revoked in the portal; the secret is shown once at creation and stored hashed. A missing or unknown key returns 401 invalid_api_key.
POST /v1/screenshot
Capture a URL or raw HTML with a warm Chromium instance. Costs 1 render.
| Field | Type | Notes |
|---|---|---|
url | string | Page to capture. Provide url or html, not both. Private-network targets are blocked. |
html | string | Raw HTML to render instead of a URL. |
width | int | Viewport width. Default 1280. |
height | int | Viewport height. Default 800. |
full_page | bool | Capture the full scroll height instead of the viewport. |
selector | string | CSS selector - capture just that element. |
dark_mode | bool | Emulate prefers-color-scheme: dark. |
format | string | png (default) or jpeg. |
quality | int | JPEG only, 1-100. Default 85. |
delay_ms | int | Extra settle time after load, for late-painting pages. |
curl -X POST https://api.ironfang.uk/v1/screenshot \
-H "Authorization: Bearer rw_live_..." \
-d '{"url": "https://example.com", "full_page": true, "dark_mode": true}' \
--output page.pngPOST /v1/pdf
Print a URL or raw HTML to PDF. PDFs are heavier, so they cost 2 renders.
| Field | Type | Notes |
|---|---|---|
url | string | Page to print. Provide url or html. |
html | string | Raw HTML to print instead of a URL. |
landscape | bool | Landscape orientation. |
print_background | bool | Include CSS backgrounds. |
header_html | string | Chromium header template. |
footer_html | string | Chromium footer template. |
scale | float | Print scale. Default 1.0. |
curl -X POST https://api.ironfang.uk/v1/pdf \
-H "Authorization: Bearer rw_live_..." \
-d '{"html": "<h1>Invoice #42</h1>", "print_background": true}' \
--output invoice.pdfTemplates
Store reusable HTML once, render it many times with different variables - built for OG images and social cards. Variables use {{name}} placeholders; substituted values are HTML-escaped, so user input cannot inject markup. Managing templates is free; rendering them costs renders. Ids are UUIDv7 - opaque but time-ordered, so they sort by creation.
| Endpoint | Purpose |
|---|---|
POST/v1/templates | Create. Body: name, html (max 256KB), width, height (default 1200×630, capped 4096). |
GET/v1/templates | List your templates. |
GET/v1/templates/{id} | Fetch one. |
PUT/v1/templates/{id} | Update. Edits bust the render cache automatically. |
DELETE/v1/templates/{id} | Delete. |
curl -X POST https://api.ironfang.uk/v1/templates \
-H "Authorization: Bearer rw_live_..." \
-d '{
"name": "og-card",
"html": "<div class=\"card\"><h1>{{title}}</h1><p>{{author}}</p></div>",
"width": 1200,
"height": 630
}'POST /v1/image/{id}
Render a stored template to an image, filling its variables. Costs 1 render; identical requests are served from cache for free.
| Field | Type | Notes |
|---|---|---|
vars | object | String map filling the template's {{placeholders}}. Missing vars render empty. |
format | string | png (default) or jpeg. |
curl -X POST https://api.ironfang.uk/v1/image/0198c9f1-5b7a-7c2e-9f1d-3a8b2c4d5e6f \
-H "Authorization: Bearer rw_live_..." \
-d '{"vars": {"title": "Hello from Renderwolf", "author": "Rick"}}' \
--output og.pngSigned URLs
Mint a stable GET URL you can drop straight into an <img> tag or an og:image meta tag - no key exposed, no server-side proxying. The signature covers every parameter, including the account it meters against, so nothing can be tampered with.
POST/v1/sign with:
| Field | Type | Notes |
|---|---|---|
kind | string | screenshot or image. |
url | string | Target page (kind: screenshot). |
template | string | Template id (kind: image). |
vars | object | Template variables (kind: image). |
width / height | int | Optional dimensions. |
full_page | bool | Screenshots only. |
ttl_hours | int | Expiry. 0 = never expires. |
The response contains the full url (and its path). Fetching it renders on demand and meters against your account; repeat fetches hit the cache for free.
curl -X POST https://api.ironfang.uk/v1/sign \
-H "Authorization: Bearer rw_live_..." \
-d '{"kind": "image", "template": "0198c9f1-5b7a-7c2e-9f1d-3a8b2c4d5e6f", "vars": {"title": "My post"}, "ttl_hours": 0}'
{"url": "https://api.ironfang.uk/v1/r/0/a1b2c3...?kind=image&template=0198c9f1-5b7a-7c2e-9f1d-3a8b2c4d5e6f&v.title=My+post"}GET /v1/usage
Your current calendar-month consumption against your plan's hard cap:
{"period": "2026-08", "renders": 1240, "limit": 50000} Plans: Free 100, Hobby 5,000, Pro 15,000, Scale 50,000 renders/month. PDFs count double. Hitting the cap pauses renders with 429 quota_exhausted - it never bills overage. Subscribed accounts meter billing-anniversary to billing-anniversary (the period is the cycle's start date); free accounts meter per calendar month.
Caching
Identical render requests are served from cache and are free - cache hits carry an X-Renderwolf-Cache: hit header and don't touch your quota. Assets return Cache-Control: public, max-age=3600, so browsers and CDNs can hold them for an hour. Editing a template busts its cache immediately.
Errors
Every error is JSON with a stable machine-readable code:
{"error": {"code": "quota_exhausted", "message": "monthly render quota exhausted - ..."}}| Status | Code | Meaning |
|---|---|---|
| 400 | bad_request | Malformed JSON or invalid parameters. |
| 401 | invalid_api_key | Missing or unknown API key. |
| 403 | bad_signature | Signed URL failed verification or expired. |
| 404 | not_found | Template (or signed route) doesn't exist. |
| 422 | render_failed | Chromium couldn't render the target - bad URL, blocked private address, or a selector that never appeared. |
| 429 | quota_exhausted | Monthly cap reached. Upgrade or wait for the period to reset. |

