---
title: HTTP API
description: Two POST endpoints, a bearer key, and a binary response. Everything the render API does.
sidebar:
  label: HTTP API
  order: 2
---

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](https://snhtml.com/dashboard) under **API Keys** and are shown only once.

```bash
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.

```bash cURL
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:

```json
{
  "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.

```bash cURL
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](/templates) for how templates and variables work.

Every other field is shared with `/v1/render` — see [Render options](/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](/errors).

```ts TypeScript
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());
```

:::note
Successful and failed render attempts both count toward your monthly usage — the attempt is recorded before the browser runs. A request rejected for a bad key or an invalid body never reaches that point and costs nothing.
:::

## 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.
