HTML frames (render at times)
POST /api/html-framesRender a self-contained HTML document in headless Chromium with all network access refused, call its window.renderAt(t) for each requested time in seconds, and return a PNG still per time (base64) plus one contact-sheet PNG of every still in a grid. Send POST /api/html-frames with the required fields html and times and pay $0.02 per call over x402 or MPP (there is no free tier). It returns a JSON object with width, height, frameCount, frames, contactSheet and 2 more.
For previews of canvas, SVG or CSS animations an agent wrote itself. The document must carry everything inline: every request it makes (fetch, images, fonts, frames, sockets, navigations) is refused and counted; data: and blob: URLs it builds itself work. A script error while loading, a missing renderAt or a renderAt that throws answers 400. Limits: html up to 512 KB, 1 to 12 times, width up to 1280 and height up to 720 (default 640x360), 5 s per renderAt and 25 s for loading and every renderAt together, 8 MB of PNG output in all. Deterministic code, no model. Body: {"html":"<!doctype html>…","times":[0,0.5,1],"width":640?,"height":360?}.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
html | string | yes | A self-contained HTML document (max 512 KB) that defines window.renderAt(t); it may return a promise Also accepted as content, body, markup, text, source. |
times | array of number | yes | 1 to 12 times in seconds (0 to 3600), one still each |
width | integer | no | Viewport width in px, 16 to 1280 (default 640) |
height | integer | no | Viewport height in px, 16 to 720 (default 360) |
Example request
curl -i -X POST https://agent402.tools/api/html-frames \
-H "Content-Type: application/json" \
-d '{"html":"<!doctype html><html><body style=\"margin:0\"><canvas id=\"c\" width=\"320\" height=\"180\"></canvas><script>const x=document.getElementById('\''c'\'').getContext('\''2d'\'');window.renderAt=function(t){x.fillStyle='\''#10243e'\'';x.fillRect(0,0,320,180);x.fillStyle='\''#4cc38a'\'';x.fillRect(20+t*100,70,40,40);};</script></body></html>","times":[0,1,2],"width":320,"height":180}'
Without payment this returns HTTP 402 Payment Required with the exact price for html-frames; any x402 v2 or MPP client pays it and retries.
Example response
{
"width": 320,
"height": 180,
"frameCount": 3,
"frames": [
{
"t": 0,
"bytes": 645,
"pngBase64": "iVBORw0KGgo…"
},
{
"t": 1,
"bytes": 645,
"pngBase64": "iVBORw0KGgo…"
},
{
"t": 2,
"bytes": 644,
"pngBase64": "iVBORw0KGgo…"
}
],
"contactSheet": {
"columns": 2,
"rows": 2,
"width": 664,
"height": 420,
"bytes": 3282,
"pngBase64": "iVBORw0KGgo…"
},
"totalBytes": 5216,
"network": {
"blockedRequests": 0,
"policy": "all network access refused; only inline script and style, and data: or blob: URLs the document builds itself"
}
}
| Field | Type | Always present | In the example |
|---|---|---|---|
width | number | yes | 320 |
height | number | yes | 180 |
frameCount | number | yes | 3 |
frames | array of objects | yes | 3 items in the example |
contactSheet | object | yes | 6 fields: columns, rows, width, height, bytes, pngBase64 |
totalBytes | number | yes | 5216 |
network | object | yes | 2 fields: blockedRequests, policy |
From an MCP client
catalog.call {
"slug": "html-frames",
"params": {
"html": "<!doctype html><html><body style=\"margin:0\"><canvas id=\"c\" width=\"320\" height=\"180\"></canvas><script>const x=document.getElementById('c').getContext('2d');window.renderAt=function(t){x.fillStyle='#10243e';x.fillRect(0,0,320,180);x.fillStyle='#4cc38a';x.fillRect(20+t*100,70,40,40);};</script></body></html>",
"times": [
0,
1,
2
],
"width": 320,
"height": 180
}
}
The hosted connector at https://agent402.tools/mcp needs a payment for html-frames; the stdio package pays it from a wallet or from an AGENT402_CREDITS_KEY issued earlier. Local install: npx -y agent402-mcp.
Errors and behavior
htmlandtimesare required. An input the tool rejects returns an HTTP 4xx whose body carrieserror,tool,expected,requiredandexample, so the caller can correct it.- A paid call that ends in any status of 400 or above is not charged over x402, MPP or a prepaid credits key: settlement is cancelled when the tool fails. The exception is a Tempo push credential, a transfer the buyer sent before the call: it settles before the tool runs, so if the tool then fails the payment is recorded as a refund owed to the paying wallet.
- Wallet-only: this tool reaches the network or stored state, so it has no proof-of-work tier. A prepaid card-credits key issued earlier (
Authorization: Bearer a402_...) also pays it. - A
GETorHEADto /api/html-frames returns the same 402 quote, so the price can be read without a body. - An
Idempotency-Keyheader makes a retried paid call replay the first 200 instead of charging again (an answer larger than 1 MB is not replayed).
Paid call (JavaScript agent)
import { wrapFetchWithPayment } from "@x402/fetch";
import { x402Client } from "@x402/core/client";
import { registerExactEvmScheme } from "@x402/evm/exact/client";
import { privateKeyToAccount } from "viem/accounts";
const client = new x402Client();
client.setSpendControls?.({ maxAmountPerPayment: "$0.02" }); // refuse any quote above this tool's price
registerExactEvmScheme(client, { signer: privateKeyToAccount(KEY) });
const payFetch = wrapFetchWithPayment(fetch, client);
const res = await payFetch("https://agent402.tools/api/html-frames", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
"html": "<!doctype html><html><body style=\"margin:0\"><canvas id=\"c\" width=\"320\" height=\"180\"></canvas><script>const x=document.getElementById('c').getContext('2d');window.renderAt=function(t){x.fillStyle='#10243e';x.fillRect(0,0,320,180);x.fillStyle='#4cc38a';x.fillRect(20+t*100,70,40,40);};</script></body></html>",
"times": [
0,
1,
2
],
"width": 320,
"height": 180
}),
});
Related tools
HTML links
POST /api/html-linksEnumerate every <a href> in an HTML string with its anchor text and rel attribute.
HTML meta (from string)
POST /api/html-metaExtract <title>, <meta description>, OpenGraph/Twitter cards, canonical URL, and JSON-LD blocks from an HTML string.
HTML select
POST /api/html-selectRun a CSS selector against an HTML string and return the matches (text, attrs, and optionally outerHTML for each).
HTML strip to text
POST /api/html-stripStrip all HTML tags and return plain text.
HTML table to JSON/CSV
POST /api/html-tableExtract a <table> from an HTML string as JSON rows (header-keyed) or CSV.