Every API here is available over the APIs.io API and to AI agents over MCP.
MCP server
One button, every client — Claude, Cursor, VS Code and the rest.
https://apis.io/mcp
Tools for apis
7 MCP tools reach this
find_apisBrowse and filter every API in the catalog.
get_api_artifactsOne API's artifacts, grouped by type.
get_openapiThe primary OpenAPI for this API.
find_similar_apisAPIs that look like this one.
apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
resolveTurn a domain, URL or GitHub org into the provider it belongs to.
find_cohortsEvery scored population of providers in the catalog.
All 92 tools →
Call it yourself
curl for this page
This API
curl "https://apis.io/api/v1/apis/ironfang:ironfang-templates-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"
Discovery needs no key. Ratings and market analysis are Pro.
Get an API key
Free tier, no form to fill in. Signing in shares your email address with us — we
store it to create your key and to recognise you if you sign in with another
provider. See our Privacy Policy and
Terms.
A second provider on the same verified email joins the account you already have.
openapi: 3.2.0
info:
title: Ironfang Render Templates API
version: 1.0.0
summary: Screenshots, PDFs, dynamic images and video through one API.
description: Ironfang Render renders web pages to images, PDFs and video.
contact:
name: Ironfang
email: hello@ironfang.uk
url: https://ironfang.com
termsOfService: https://ironfang.com/legal/terms
license:
name: Proprietary - use governed by the Ironfang terms of service
url: https://ironfang.com/legal/terms
servers:
- url: https://api.ironfang.com/render
description: Production
- url: https://api.ironfang.com
description: Production, unpartitioned - retained for existing clients
security:
- apiKey: []
tags:
- name: Templates
description: Stored HTML with `{{variable}}` placeholders, rendered on demand.
paths:
/v1/templates:
get:
tags:
- Templates
summary: List templates
operationId: listTemplates
description: Paged newest first. Follow next_cursor until empty; `total` is the whole count and does not change between pages. summary=true returns an empty html field; fetch a template by ID to edit or render it.
parameters:
- name: cursor
in: query
schema:
type: string
maxLength: 256
description: Opaque next_cursor from the previous page; empty means the first page.
- name: limit
in: query
schema:
type: integer
minimum: 1
maximum: 100
default: 50
description: Maximum templates per page.
- name: summary
in: query
schema:
type: boolean
default: false
description: Omit HTML bodies from the database read.
responses:
'200':
description: 'Your templates, wrapped in a `templates` property - not a bare
array. Clients that iterate must read `templates`.
'
content:
application/json:
schema:
$ref: '#/components/schemas/TemplateList'
example:
total: 1
next_cursor: ''
templates:
- id: 01a01c7c-682e-7b2a-95cd-ffb2f2a4d9be
name: og-card
width: 1200
height: 630
'400':
description: Malformed cursor, repeated or unknown parameter, or limit outside 1-100.
'401':
$ref: '#/components/responses/Unauthorized'
post:
tags:
- Templates
summary: Create a template
description: 'Store HTML containing `{{placeholders}}`. Render it with
`POST /v1/image/{id}`, supplying values for the placeholders.'
operationId: createTemplate
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/TemplateInput'
examples:
og:
summary: An Open Graph card
value:
name: og-card
html: <div style="font:700 64px sans-serif;padding:80px">{{title}}</div>
width: 1200
height: 630
responses:
'201':
description: Created.
content:
application/json:
schema:
$ref: '#/components/schemas/Template'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
/v1/templates/{id}:
parameters:
- $ref: '#/components/parameters/TemplateId'
get:
tags:
- Templates
summary: Fetch a template
operationId: getTemplate
description: Returns a stored rendering template belonging to the authenticated account, including its HTML and output dimensions.
responses:
'200':
description: The template.
content:
application/json:
schema:
$ref: '#/components/schemas/Template'
'401':
$ref: '#/components/responses/Unauthorized'
'404':
$ref: '#/components/responses/NotFound'
put:
tags:
- Templates
summary: Replace a template
operationId: updateTemplate
description: Replaces the authenticated account's stored template with the supplied name, HTML and dimensions. Subsequent renders use the updated template.
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/TemplateInput'
responses:
'200':
description: Updated.
content:
application/json:
schema:
$ref: '#/components/schemas/Template'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'404':
$ref: '#/components/responses/NotFound'
delete:
tags:
- Templates
summary: Delete a template
operationId: deleteTemplate
description: Removes a stored template from the authenticated account so it can no longer be used for new template renders.
responses:
'204':
description: Deleted.
'401':
$ref: '#/components/responses/Unauthorized'
'404':
$ref: '#/components/responses/NotFound'
/v1/image/{id}:
parameters:
- $ref: '#/components/parameters/TemplateId'
post:
tags:
- Templates
summary: Render a template
description: 'Substitutes `vars` into the template''s `{{placeholders}}` and renders it
at the template''s own width and height. Returns the image bytes.'
operationId: renderTemplate
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/RenderTemplateRequest'
examples:
card:
value:
vars:
title: Shipping UUIDv7 everywhere
format: png
responses:
'200':
$ref: '#/components/responses/Image'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/Forbidden'
'404':
$ref: '#/components/responses/NotFound'
'409':
$ref: '#/components/responses/IdempotencyConflict'
'422':
$ref: '#/components/responses/RenderFailed'
'429':
$ref: '#/components/responses/RateLimited'
'402':
$ref: '#/components/responses/BillingRefused'
'503':
$ref: '#/components/responses/BillingUnavailable'
components:
responses:
NotFound:
description: No such template, or not yours.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
Image:
description: 'The rendered image. `png` unless `format: jpeg` was requested.
'
headers:
X-Ironfang-Cache:
description: '`hit` when served from cache, in which case it counted nothing.'
schema:
type: string
enum:
- hit
X-Ironfang-Meter:
$ref: '#/components/headers/Meter'
X-Ironfang-Quantity:
$ref: '#/components/headers/Quantity'
X-Ironfang-Render-Ms:
description: Render time in milliseconds, excluding any requested delay.
schema:
type: integer
X-Ironfang-Delay-Ms:
description: The `delay_ms` actually waited, when one was requested.
schema:
type: integer
X-Ironfang-Captured-At:
description: 'RFC 3339 timestamp of a fresh capture. Present only on
`no_cache` renders, where the response is also `no-store` - the
pairing is what makes a capture usable as evidence.
'
schema:
type: string
format: date-time
content:
image/png:
schema:
type: string
format: binary
image/jpeg:
schema:
type: string
format: binary
Unauthorized:
description: Missing, malformed, revoked or unknown API key.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
IdempotencyConflict:
description: '`idempotency_key_used` - this Idempotency-Key already made its render
(or is making it, or failed); nothing was reserved again.
`idempotency_conflict` - the key was used for a different request.
'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
BillingRefused:
description: 'Paid usage cannot be billed: `paid_usage_paused`, `payment_required`,
`payment_action_required` or `account_restricted`. Resolve it in
Billing; nothing was reserved or counted. The same body can also
carry `account_closed` (410) and `operation_too_large` (413).
'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
BillingUnavailable:
description: '`billing_temporarily_unavailable` - the billing ledger could not admit
the request just now. Retry after `Retry-After` seconds; nothing was
reserved.
'
headers:
Retry-After:
schema:
type: integer
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
RenderFailed:
description: 'The page did not render: the host did not resolve, it never finished
loading, it timed out, or it was blocked. Try `delay_ms`, and check the
target loads in a normal browser.
'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
Forbidden:
description: '`insufficient_scope` - the key does not carry the scope;
`account_not_linked` - the key''s account belongs to no Ironfang
organisation, so its usage cannot be billed; `product_not_eligible` -
paid use of Ironfang Render needs an eligible billing account.
'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
RateLimited:
description: "Rate limits and exhausted billing allowances share this status,\nseparated by `error.code`:\n\n- `rate_limited` - your account exceeded 120 renders/minute\n- `target_rate_limited` - the site being rendered is being hit too hard\n across all customers (60/minute per host)\n- `clip_busy`, `preview_busy` - every encoder or preview slot on the\n worker is in use; `Retry-After` says when to come back\n- `concurrency_limit` - your account already has 2 browser\n operations in progress, synchronous renders and running jobs\n together; retry after `Retry-After`, or submit a durable job,\n which waits for a free slot instead of being refused\n- `queue_full` - your account already has 1,000 unfinished jobs, or\n the batch would take it past that; nothing was enqueued\n- `free_allowance_exhausted` - this month's free allowance for the\n meter is used up and paid usage is not enabled\n- `account_budget_exhausted`, `product_budget_exhausted` - the\n billing account's monthly limit, or Ironfang Render's, is reached\n- `exemption_limit_reached` - an exempt account's agreed limit is reached\n\nThe rate and capacity limits clear by themselves, so retry with\nbackoff, after `Retry-After` where it is sent. The billing codes do\nnot: enable paid usage or raise the limit in Billing, or wait for the\nmonth to roll over. Nothing was reserved or counted. Branch on the\ncode rather than the status.\n"
headers:
Retry-After:
description: Seconds to wait before retrying, on the refusals that clear by themselves.
schema:
type: integer
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
BadRequest:
description: Malformed body, or neither/both of `url` and `html`.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
schemas:
Cookie:
type: object
required:
- name
- domain
properties:
name:
type: string
value:
type: string
domain:
type: string
description: 'Required. Chromium silently drops a domainless cookie set before
navigation, so the render would quietly come back logged out.
'
path:
type: string
default: /
Error:
type: object
required:
- error
properties:
error:
type: object
required:
- code
- message
properties:
code:
type: string
description: 'Stable, safe to branch on. New codes may be added: treat a
code you do not recognise as a failure of the class its HTTP
status indicates.
'
enum:
- bad_request
- unauthorized
- invalid_api_key
- auth_failed
- forbidden
- billing_moved
- not_found
- rate_limited
- target_rate_limited
- render_failed
- bad_signature
- signing_disabled
- account_gone
- account_not_linked
- account_required
- admission_failed
- bad_destination
- bad_target
- batch_plan_failed
- batch_submit_failed
- batch_too_large
- bundle_failed
- clip_busy
- concurrency_limit
- delegated_not_allowed
- delivery_disabled
- delivery_plan_failed
- destination_disabled
- idempotency_conflict
- idempotency_key_used
- insufficient_scope
- invalid_token
- job_finished
- job_plan_failed
- job_submit_failed
- logo_fetch_failed
- pdf_too_long
- preview_busy
- queue_full
- result_expired
- result_not_ready
- result_read_failed
- secret_failed
- site_preview_disabled
- store_failed
- unknown_destination
- unscannable
- vault_failed
- video_disabled
- free_allowance_exhausted
- account_budget_exhausted
- product_budget_exhausted
- paid_usage_paused
- payment_required
- payment_action_required
- account_restricted
- account_closed
- product_not_eligible
- exemption_limit_reached
- operation_too_large
- billing_temporarily_unavailable
message:
type: string
description: Human-readable. May change; do not match on it.
docs:
type: string
format: uri
description: Where the error codes are documented.
request_id:
type: string
description: The X-Ironfang-Request-ID of the failed request. Quote it to support.
example:
error:
code: target_rate_limited
message: too many renders for that host, try again shortly
docs: https://ironfang.com/render/docs#errors
request_id: 01992f6e-7c1a-7b3e-9d52-4f0a6c8e2b17
RenderTemplateRequest:
allOf:
- $ref: '#/components/schemas/RenderCommon'
- type: object
properties:
vars:
type: object
additionalProperties:
type: string
description: Values for the template's placeholders.
format:
type: string
enum:
- png
- jpeg
- webp
default: png
TemplateList:
type: object
additionalProperties: false
required:
- templates
- next_cursor
- total
properties:
templates:
type: array
maxItems: 100
items:
$ref: '#/components/schemas/Template'
next_cursor:
type: string
description: Cursor for the next page; empty on the final page.
total:
type: integer
minimum: 0
description: How many templates the account has in total, independent of this page. Read it rather than counting pages.
PageAction:
description: 'One ordered pre-capture action. Selectors are CSS selectors in the main
page. click and hover wait until the first matching element is
interactable; wait_for_selector waits for its presence (not visibility).
All actions share the remaining timeout_ms render budget.
'
oneOf:
- type: object
additionalProperties: false
required:
- type
- selector
properties:
type:
type: string
enum:
- click
- hover
- wait_for_selector
selector:
type: string
minLength: 1
maxLength: 1024
description: Non-blank CSS selector; at most 1024 UTF-8 bytes.
- type: object
additionalProperties: false
required:
- type
- duration_ms
properties:
type:
type: string
enum:
- delay
duration_ms:
type: integer
minimum: 0
maximum: 10000
description: Delay in milliseconds; all action delays together may total at most 10000 ms.
RenderCommon:
type: object
properties:
no_cache:
type: boolean
default: false
description: 'Force a fresh render instead of serving a cached one. Always
billable. Excluded from the cache key, so repeated fresh requests
do not collide.
'
block_ads:
type: boolean
default: false
description: 'Drop requests to known ad and tracker networks before they load.
Stopped as network requests rather than hidden afterwards, so the
page never spends time fetching them.
'
block_cookie_banners:
type: boolean
default: false
description: 'Hide common consent banners, and undo the scroll lock they set -
without which a full-page capture is one viewport tall.
Hidden, not accepted. Clicking "accept" would be a decision made
on the site owner''s behalf and recorded as theirs.
'
hide_selectors:
type: array
items:
type: string
description: 'CSS selectors to hide before capturing. Applied after load, so
elements injected by script are covered.
'
css:
type: string
maxLength: 65536
example: '.chat-widget { display: none !important; }'
description: 'Custom stylesheet, at most 65536 UTF-8 bytes. Applied after normal
load/wait conditions and built-in hiding, before script, actions,
settling and capture. Supports screenshots and PDFs, including
@media print rules. Malformed stylesheet syntax is a 400
bad_request; Chromium determines supported properties and values.
'
script:
type: string
maxLength: 16384
example: document.querySelector('#pricing')?.click();
description: 'JavaScript statements executed inside the target page''s main-frame
context after load/wait conditions and css, before actions and
capture. Runs as an async function body: use await or return a
promise for asynchronous work. Completion is awaited; return values
are discarded. At most 16384 UTF-8 bytes and 5000 ms execution,
further bounded by the remaining timeout_ms budget. Syntax errors,
execution failures and timeouts return 422 render_failed with a
sanitized message. Supports screenshots and PDFs; no browser-session
or Playwright/Puppeteer APIs are exposed.
'
actions:
type: array
maxItems: 20
items:
$ref: '#/components/schemas/PageAction'
example:
- type: click
selector: '#show-more'
- type: wait_for_selector
selector: '#details'
description: 'Up to 20 actions, executed in order after css and script, before
settling and capture. Supports screenshots and PDFs. Missing or
unready selectors and action timeouts fail with 422 render_failed,
identifying the zero-based action index and type. Invalid action
shapes are 400 bad_request. An action failure stops the list and is
not retried in queued jobs. All options, including action order,
are part of the screenshot cache key; use no_cache for a fresh run.
'
headers:
type: object
additionalProperties:
type: string
description: Extra HTTP headers sent with every request for the page.
cookies:
type: array
items:
$ref: '#/components/schemas/Cookie'
description: 'Cookies to set before navigating. Set beforehand because a session
cookie that arrives after load has missed the request it was meant
to authenticate.
'
authorization:
type: string
description: 'Sets the `Authorization` header - the common case, spelled once.
An explicit `Authorization` in `headers` wins over this.
'
user_agent:
type: string
description: 'Overrides the user agent, including any set by `device`.
'
wait_until:
type: string
enum:
- load
- domcontentloaded
- networkidle
default: load
description: 'When the page counts as ready. `networkidle` waits for traffic to
stop and is bounded by the render timeout, because a page with
polling never truly idles.
'
wait_for_selector:
type: string
description: 'Wait for this element before capturing. Fails the render if it
never appears, rather than returning a half-drawn page.
'
timeout_ms:
type: integer
description: 'Per-render timeout. Clamped to the service maximum - it can lower
the ceiling but not raise it.
'
device_scale_factor:
type: number
minimum: 1
maximum: 3
description: 'Pixel density. 2 is retina: the same CSS size at twice the pixels.
'
Template:
type: object
properties:
id:
type: string
format: uuid
name:
type: string
html:
type: string
width:
type: integer
height:
type: integer
created_at:
type: string
format: date-time
updated_at:
type: string
format: date-time
TemplateInput:
type: object
required:
- name
- html
properties:
name:
type: string
html:
type: string
description: HTML with `{{placeholder}}` markers.
width:
type: integer
default: 1280
height:
type: integer
default: 800
parameters:
TemplateId:
name: id
in: path
required: true
description: Template id (UUIDv7).
schema:
type: string
format: uuid
headers:
Quantity:
description: 'The quantity this request committed to its meter: 1 for a unit
render, output seconds (rounded up) for video, `0` for a cache hit or
an unmetered key. Read it to log usage per request without polling
`/v1/usage`.
'
schema:
type: integer
Meter:
description: 'The billing meter this request counted against, e.g.
`render.screenshot`. Present on every billable response, cache hits
included.
'
schema:
type: string
securitySchemes:
apiKey:
type: http
scheme: bearer
description: 'An API key from the portal, sent as `Authorization: Bearer rw_live_...`.
'
externalDocs:
description: Ironfang Render documentation
url: https://ironfang.com/render/docs