API Reference

Open Graph

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.

GET /render

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_…
ParamDefaultDescription
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.
Host allowlist: if ALLOWED_HOSTS is configured (comma-separated exact hostnames), requests to unlisted hosts return 403 host_not_allowed:<host>. Empty means allow-all.

GET /chart

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_…
ParamDefaultDescription
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.
Static output: animations and responsive layout are disabled before rendering. Root-level Chart.js plugins are not supported; send plain JSON configs only.

GET /template/:name

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_…
ParamDefaultDescription
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.
Shipped templates: 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.

POST /render-inline

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 fieldRequiredDescription
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 paramDefaultDescription
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.
No caching. Responses are returned with Cache-Control: no-store. For cacheable production rendering, save the template via the admin UI and use GET /template/:name instead.

Template structure

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" }
    ]
  }
}
typeElement tag: div, span, img, p, h1–h6.
twTailwind classes (Satori subset). Containers with more than one child must include flex. Supports {{name}} interpolation.
textLeaf text content. {{name}} interpolates from query params.
srcImage source URL for img elements. Supports {{name}} interpolation.
whenConditional render — node is dropped when the named param is missing or empty string.
styleInline CSS object (fallback for things the Tailwind subset doesn't cover). String values support {{name}} interpolation — useful for dynamic colors.
childrenNested node array.
defaultsPer-param fallback values applied when the request omits them or passes an empty string.

API keys

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]}]}}'
Enforcement is in transition. Requests without a valid key currently receive a placeholder SVG image rather than an HTTP error. Hard enforcement (401/403) will be enabled once all callers have migrated.

Cache behaviour

Cached image endpoints share the same KV-backed cache with a 30-day TTL.

ScenarioResult
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.
HTTP responses carry Cache-Control: public, max-age=31536000, immutable. Serve them through a CDN or directly — the KV cache is the source of truth.