Skip to content
SnapHTML
Esc
navigateopen⌘Jpreview
On this page

HTTP API

Two POST endpoints, a bearer key, and a binary response. Everything the render API does.

The API base URL is https://api.snhtml.com. Every render is a single POST with a JSON body; the response body is the rendered file itself.

Authentication

Send your key as a bearer token. Keys are created in the dashboard under API Keys and are shown only once.

authorization: Bearer snhtml_...
content-type: application/json

A missing, malformed, disabled, or unknown key returns 401 with {"error":"Invalid API key"}. Keys carry your account’s quota, so treat them as server-side secrets — never ship one in a browser bundle or mobile app.

POST /v1/render

Renders HTML you send, or a URL you point at.

Provide html or url — at least one is required. If both are present, html is used.

curl -X POST https://api.snhtml.com/v1/render \
  -H "authorization: Bearer snhtml_..." \
  -H "content-type: application/json" \
  -d '{
    "html": "<main><h1>Invoice #1842</h1></main>",
    "format": "pdf",
    "pdfFormat": "A4",
    "printBackground": true,
    "fileName": "invoice-1842"
  }' \
  --output invoice-1842.pdf

Rendering a live page instead:

{
  "url": "https://example.com/reports/2026-q1",
  "format": "png",
  "width": 1440,
  "height": 900,
  "fullPage": true,
  "waitUntil": "networkidle"
}

The page is fetched by the renderer, not by your server, so it must be reachable from the public internet. Anything behind a login, a VPN, or an IP allowlist won’t render — build the HTML yourself and send it as html instead.

POST /v1/render/template

Renders a template you saved in the dashboard, with {{ variable }} placeholders replaced.

templateId is required. html and url are not accepted on this endpoint — the template is the source.

curl -X POST https://api.snhtml.com/v1/render/template \
  -H "authorization: Bearer snhtml_..." \
  -H "content-type: application/json" \
  -d '{
    "templateId": "k57d2...",
    "variables": {
      "customerName": "Ada Lovelace",
      "invoiceNumber": 1842,
      "total": "$249.00"
    },
    "format": "pdf"
  }' \
  --output invoice-1842.pdf

See Templates for how templates and variables work.

Every other field is shared with /v1/render — see Render options for the full list.

Response

On success you get the bytes, not JSON:

Header Value
content-type image/png, image/webp, image/jpeg, or application/pdf
content-disposition attachment; filename="<fileName>.<ext>"
cache-control no-store

The filename comes from fileName, slugified and capped at 80 characters, with the format’s extension appended. Without fileName, /v1/render falls back to render and /v1/render/template falls back to the template’s name.

On failure you get JSON with an error string and a status code — see Errors.

const response = await fetch("https://api.snhtml.com/v1/render", {
  method: "POST",
  headers: {
    authorization: `Bearer ${process.env.SNAPHTML_API_KEY}`,
    "content-type": "application/json",
  },
  body: JSON.stringify({ html, format: "pdf" }),
});

if (!response.ok) {
  const { error } = (await response.json()) as { error: string };
  throw new Error(error);
}

const pdf = Buffer.from(await response.arrayBuffer());

Limits per request

Limit Value
HTML payload 5 MB
waitForTimeout 10,000 ms
deviceScaleFactor 4
scale (PDF) 0.1 – 2
fileName 160 characters

Unknown fields are rejected rather than ignored, so a typo like pageFormat fails with 400 instead of silently rendering the wrong thing.

Was this page helpful?