Screenshot any URL, render a Chart.js config, render from a saved template, or render inline from a full template definition in the request body. Cached endpoints respond in under 200 ms on repeat requests.
Navigates a headless Chrome to any URL and returns a screenshot. Results are cached in KV for 30 days. The cache key rolls over weekly so images stay fresh without manual cache-busting.
GET/render?url=https://example.com GET/render?url=https://example.com&format=png&w=1200&h=630 GET/render?url=https://example.com&selector=.og-card&api_key=ogsk_…
| Param | Default | Description |
|---|---|---|
| url | — | Target URL to screenshot. Must be a fully-qualified URL including scheme. |
| w | 1440 | Viewport width in pixels. Clamped to 1–4096. |
| h | 788 | Viewport height in pixels. Clamped to 1–4096. |
| dpr | 2 | Device pixel ratio — 1, 2, or 3. Higher values produce sharper images on HiDPI screens but increase file size. |
| format | jpeg | Output image format: jpeg, png, or webp. JPEG quality is fixed at 85. PNG and WebP use a transparent background. |
| selector | — | CSS selector to clip the screenshot to a single element. Falls back to full-page if the selector isn't found within 5 s. |
| scheme | — | Override the URL scheme before navigating — e.g. https. Useful when the url param omits the scheme. |
| digest | ISO week | Cache-key suffix. Defaults to YYYY-Www (rolls weekly). Pass any string to pin a specific render version. |
| force | — | Set force=1 to bypass the KV cache and always re-render. |
| api_key | — | API key for authenticated access. Also accepted as Authorization: Bearer <key>. See Authentication. |
| …extra params | — | Any other query params are forwarded to the target URL and baked into the cache key. Useful for auth tokens, feature flags, preview modes, etc. |
ALLOWED_HOSTS is configured (comma-separated exact hostnames),
requests to unlisted hosts return 403 host_not_allowed:<host>. Empty means allow-all.
Renders a QuickChart-style Chart.js config to a PNG image using the browser renderer. This is a focused image API for static charts: v1 supports bar and line charts, multiple datasets, labels, and Chart.js color fields.
# Copy-paste curl: --data-urlencode handles the JSON query param curl -G "https://open-graph.com/chart" \ --data-urlencode 'c={"type":"bar","data":{"labels":["A","B"],"datasets":[{"label":"Sales","data":[10,20],"backgroundColor":"#4ade80"}]}}' \ --data-urlencode "w=1200" \ --data-urlencode "h=630" \ --output chart.png
GET/chart?c={"type":"bar","data":{"labels":["A","B"],"datasets":[{"label":"Sales","data":[10,20]}]}} GET/chart?c={"type":"line","data":{"labels":["Jan","Feb"],"datasets":[{"label":"MRR","data":[1200,1800],"borderColor":"#4ade80"}]}}&w=1200&h=630 GET/chart?c={…}&api_key=ogsk_…
| Param | Default | Description |
|---|---|---|
| c | — | URL-encoded JSON chart config. The accepted shape mirrors Chart.js: type, data.labels, and data.datasets. |
| type | inside c | Supported values are bar and line. Other Chart.js chart types are rejected in v1. |
| w / width | 600 | Output width in pixels. Clamped to 1–4096. w wins when both aliases are present. |
| h / height | 300 | Output height in pixels. Clamped to 1–4096. h wins when both aliases are present. |
| dpr | 2 | Device pixel ratio — 1, 2, or 3. Higher values produce sharper images. |
| digest | ISO week | Cache-key suffix. Defaults to YYYY-Www (rolls weekly). Pass any string to pin a specific chart version. |
| force | — | Set force=1 to bypass the KV cache and re-render the chart. |
| api_key | — | API key for authenticated access. Also accepted as Authorization: Bearer <key>. See Authentication. |
Renders a saved Satori template to a PNG image. No browser needed — renders from a JSON tree in ~200 ms.
Always returns image/png. Missing a required param returns 400 with usage instructions.
GET/template/hero?title=Hello+World GET/template/hero?title=Hello&subtitle=World&eyebrow=My+Site GET/template/card?title=Post+Title&api_key=ogsk_…
| Param | Default | Description |
|---|---|---|
| w | template default | Output width in pixels. Clamped to 1–4096. |
| h | template default | Output height in pixels. Clamped to 1–4096. |
| dpr | 2 | Resvg zoom factor 1–3. Higher values produce sharper images. |
| digest | week + updatedAt | Cache-key suffix. Defaults to ISO week combined with the template's updatedAt timestamp — so edits in the admin UI automatically bust the cache. |
| force | — | Set force=1 to bypass the KV cache. |
| api_key | — | API key for authenticated access. Also accepted as Authorization: Bearer <key>. |
| …template params | — | All other query params are passed as template variables (e.g. title, subtitle). Required params are declared per-template; missing ones return 400. |
hero — dark bg, title + optional eyebrow & subtitle (1200×630, requires title) ·
card — light bg, title + subtitle + byline (1200×630, requires title) ·
promo — dynamic colors, feature bullets, CTA (requires title).
Manage all templates in the admin UI.
Renders a full template definition from the request body to a PNG image. No saved template needed — send the
complete template JSON and params in one request. Always returns image/png. No authentication required.
POST/render-inline Content-Type: application/json { "template": { /* full TemplateDef object */ }, "params": { "title": "Hello World", "subtitle": "..." } }
# Render an inline template and save the PNG to disk curl -X POST https://open-graph.com/render-inline \ -H "Content-Type: application/json" \ -d '{ "template": { "name": "agency", "defaultSize": { "w": 1200, "h": 630 }, "required": ["title"], "defaults": { "eyebrow": "Amazon Optimization Agency", "site": "bigdetailpage.com" }, "tree": { "type": "div", "tw": "flex h-full w-full flex-col justify-between", "style": { "backgroundColor": "#030712", "padding": "56px 64px", "backgroundImage": "radial-gradient(ellipse at 90% 10%, rgba(245,158,11,0.14) 0%, transparent 60%)" }, "children": [ { "type": "div", "tw": "flex flex-col", "style": { "flex": "1", "justifyContent": "center" }, "children": [ { "type": "div", "tw": "flex items-center", "style": { "marginBottom": "24px" }, "children": [ { "type": "div", "style": { "width": "40px", "height": "4px", "backgroundColor": "#f59e0b", "marginRight": "16px" } }, { "type": "span", "style": { "color": "#f59e0b", "fontSize": "36px", "fontWeight": "700", "letterSpacing": "0.08em", "textTransform": "uppercase" }, "text": "{{eyebrow}}" } ] }, { "type": "div", "style": { "color": "#ffffff", "fontSize": "92px", "fontWeight": "900", "lineHeight": "1.05", "letterSpacing": "-0.02em", "marginBottom": "28px" }, "text": "{{title}}" }, { "type": "div", "style": { "color": "#9ca3af", "fontSize": "54px", "lineHeight": "1.35", "maxWidth": "1000px" }, "text": "{{subtitle}}", "when": "subtitle" } ] }, { "type": "div", "tw": "flex items-center justify-between", "style": { "borderTopWidth": "1px", "borderTopStyle": "solid", "borderTopColor": "#1f2937", "paddingTop": "24px" }, "children": [ { "type": "div", "style": { "color": "#6b7280", "fontSize": "36px", "fontWeight": "600" }, "text": "{{site}}" }, { "type": "div", "tw": "flex items-center", "children": [ { "type": "div", "style": { "width": "14px", "height": "14px", "borderRadius": "50%", "backgroundColor": "#f59e0b", "marginRight": "12px" } }, { "type": "span", "style": { "color": "#f59e0b", "fontSize": "34px", "fontWeight": "700" }, "text": "White-Glove Agency" } ] } ] } ] } }, "params": { "title": "Your Listings. Expertly Managed.", "eyebrow": "Amazon Optimization Agency", "subtitle": "Powered by AI, run by humans who know Amazon.", "site": "bigdetailpage.com" } }' \ --output og.png
| Body field | Required | Description |
|---|---|---|
| template | yes | A complete TemplateDef object — same structure as a saved template, including name, defaultSize, tree, required, etc. |
| params | — | Template variable values as a flat string map (e.g. {"title":"Hello"}). Merged with template.defaults; required params must be present. |
| Query param | Default | Description |
|---|---|---|
| w | template default | Output width in pixels. Clamped to 1–4096. |
| h | template default | Output height in pixels. Clamped to 1–4096. |
| dpr | 2 | Resvg zoom factor 1–3. Higher values produce sharper images. |
Cache-Control: no-store. For cacheable production rendering, save the template via the admin UI and use GET /template/:name instead.
Templates are JSON objects defining a Satori node tree. Edit them live in the admin UI — no redeploy needed.
{
"name": "hero",
"defaultSize": { "w": 1200, "h": 630 },
"required": ["title"],
"sampleParams": { "title": "Hello", "subtitle": "World" },
"defaults": { "eyebrow": "My Site" },
"tree": {
"type": "div",
"tw": "flex h-full w-full flex-col justify-center bg-slate-950 text-white px-20",
"children": [
{ "type": "div", "tw": "text-2xl text-slate-400", "text": "{{eyebrow}}", "when": "eyebrow" },
{ "type": "div", "tw": "text-7xl font-bold", "text": "{{title}}" },
{ "type": "div", "tw": "text-4xl text-slate-300 mt-6", "text": "{{subtitle}}", "when": "subtitle" }
]
}
} div, span, img, p, h1–h6.flex. Supports {{name}} interpolation.{{name}} interpolates from query params.img elements. Supports {{name}} interpolation.{{name}} interpolation — useful for dynamic colors.
API keys control access to the image endpoints. Keys are ogsk_-prefixed 64-character hex strings managed in the admin UI.
# query param — works in <img src> tags /render?url=https://example.com&api_key=ogsk_abc123… # Authorization header curl -H "Authorization: Bearer ogsk_abc123…" /chart?c='{"type":"bar","data":{"labels":["A"],"datasets":[{"data":[1]}]}}'
Cached image endpoints share the same KV-backed cache with a 30-day TTL.
| Scenario | Result |
|---|---|
Default request (no digest) | Cache key includes the ISO week (YYYY-Www). Images roll over weekly automatically. |
Pinned digest=v2 | Cache key is locked to that string. The same image is served until you change the digest. |
| Template edited in admin UI | The default digest for /template/:name includes updatedAt, so saved edits automatically bust cached renders. |
force=1 | Skips the cache read. A new image is rendered and written back to KV. |
Cache-Control: public, max-age=31536000, immutable.
Serve them through a CDN or directly — the KV cache is the source of truth.