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.

FieldTypeNotes
urlstring Page to capture. Provide url or html, not both. Private-network targets are blocked.
htmlstringRaw HTML to render instead of a URL.
widthintViewport width. Default 1280.
heightintViewport height. Default 800.
full_pageboolCapture the full scroll height instead of the viewport.
selectorstringCSS selector - capture just that element.
dark_modeboolEmulate prefers-color-scheme: dark.
formatstringpng (default) or jpeg.
qualityintJPEG only, 1-100. Default 85.
delay_msintExtra 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.png

POST /v1/pdf

Print a URL or raw HTML to PDF. PDFs are heavier, so they cost 2 renders.

FieldTypeNotes
urlstringPage to print. Provide url or html.
htmlstringRaw HTML to print instead of a URL.
landscapeboolLandscape orientation.
print_backgroundboolInclude CSS backgrounds.
header_htmlstringChromium header template.
footer_htmlstringChromium footer template.
scalefloatPrint 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.pdf

Templates

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.

EndpointPurpose
POST/v1/templates Create. Body: name, html (max 256KB), width, height (default 1200×630, capped 4096).
GET/v1/templatesList 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.

FieldTypeNotes
varsobject String map filling the template's {{placeholders}}. Missing vars render empty.
formatstringpng (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.png

Signed 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:

FieldTypeNotes
kindstringscreenshot or image.
urlstringTarget page (kind: screenshot).
templatestringTemplate id (kind: image).
varsobjectTemplate variables (kind: image).
width / heightintOptional dimensions.
full_pageboolScreenshots only.
ttl_hoursintExpiry. 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 - ..."}}
StatusCodeMeaning
400bad_requestMalformed JSON or invalid parameters.
401invalid_api_keyMissing or unknown API key.
403bad_signatureSigned URL failed verification or expired.
404not_foundTemplate (or signed route) doesn't exist.
422render_failed Chromium couldn't render the target - bad URL, blocked private address, or a selector that never appeared.
429quota_exhaustedMonthly cap reached. Upgrade or wait for the period to reset.