Docs

SnappyPDF turns a template plus JSON into a PDF. Design the document once in the editor, mark the parts that change as variables, publish it, then render it by API. Here’s the whole loop.

Overview

Step 1

Design it

Drag blocks onto the page in the editor, or start from a ready-made starter.

Step 2

Variable-ize it

Swap anything that changes per document for a {{ variable }}.

Step 3

Publish it

Snapshot the draft into an immutable version. The API serves only published versions.

Step 4

Render it

POST your JSON with an API key and get the finished PDF back on the same request.

Create a template

From Templates, create a new template (or duplicate a starter) and open the editor. The editor has three parts: a toolbox of blocks on the left, the page canvas in the middle, and a properties panel on the right for whatever block is selected.

Drag blocks onto the page and arrange them. Set the page size and orientation for the whole document, and optionally turn on a header and footer band that repeat on every page. Use the Preview to see the exact rendered output with sample data at any time — the preview and the final PDF come from the same renderer, so what you see is what you get.

When it’s ready, hit Publish. Publishing takes an immutable snapshot — a numbered version — and the render API only ever serves published versions. That means you can keep editing the draft without ever breaking a live integration. Publish again to cut a new version; pin a specific version from the API when you need reproducible output.

How layout works

SnappyPDF uses flow layout, not absolute positioning. Blocks stack top to bottom and flow onto new pages as content grows. There are no x/y coordinates — which is exactly what lets a document with ten line items and one with ten thousand both paginate correctly without you touching the design.

Dimensions like page size, margins and spacing are in millimetres; font sizes are in points — the same units you’d use in print.

Text

Headings and paragraphs, with {{ variable }} tokens anywhere inside.

Image

A fixed or variable image — pass a URL or data URI at render time.

Table

One row per item in a list variable; the header repeats across pages.

Row

Holds columns for side-by-side layout (e.g. logo left, address right).

Section

A container. Can repeat over a list, or fill the page to centre content.

Divider

A horizontal rule between blocks.

Spacer

Fixed vertical gap, in millimetres.

Page break

Forces everything after it onto a new page.

Repeating content

Two blocks turn a list variable into repeating output. A table renders one row per item, and its header row repeats automatically when the table spills onto the next page. A section set to repeat re-renders its whole contents once per item — useful for cards, grouped blocks or anything more elaborate than a table row. Inside either, you reference the current item’s fields directly (see Variables below).

Headers & footers

Header and footer bands are fixed-height (in mm) and render on every page. They take a restricted set of blocks — no tables or page breaks, since a fixed band can’t paginate. They’re the only place page counters work: drop {{ page }} and {{ pages }} to get “Page 1 of 4”.

Variables

A variable is any part of the document that changes between renders. Define your variables in the Variables panel — each has a name and a type. A template’s variables are its data contract: they’re exactly the fields your API payload provides.

Names use letters, numbers and underscores and must start with a letter. Reference a variable anywhere text is allowed with a {{ token }}. Values are HTML-escaped, and you can reach into nested data with dot paths like {{ client.name }}.

Tip: type “/” to insert a variable

In any content input, press / to pop up a menu of every variable on the template. Keep typing to filter it, use the arrow keys to move, and press enter (or click) to drop the token in at your cursor — no need to remember exact names or type the braces yourself.

string

Any text — names, addresses, notes.

number

Amounts, quantities, totals.

date

Dates you interpolate into the document.

boolean

True/false, for showing or hiding content.

image

A URL or data URI rendered as an image.

list

An array of items — each with its own typed fields — for tables and repeating sections.

A list variable holds an array of items, and you give each item its own typed fields (say description, qty, price). Bind a table or a repeating section to the list; inside it, refer to the current item’s fields — the item and its index are in scope for each iteration.

Every variable gets sample data so the editor preview shows a realistic document before you’ve wired up any real payload. The sample data also becomes the fallback the API renders when a request omits data.

API keys

The render API authenticates with an organization API key. Create one under API Keys in your organization settings (owners and admins only). The full key is shown once, at creation — we only ever store a hash of it, so copy it then and keep it somewhere safe. If you lose it, revoke it and make a new one.

Keys are prefixed spdf_. Send yours on every request as either header:

Authorization: Bearer spdf_your_key_here
# — or —
x-api-key: spdf_your_key_here

Keep keys server-side. Anyone holding a key can render any of your organization’s published templates and spend its render allowance.

The render API

POST /api/v1/render takes a small JSON body and returns the finished PDF bytes (application/pdf) on the same request — no polling, no webhooks.

POST /api/v1/render
Authorization: Bearer spdf_your_key_here
Content-Type: application/json

{
  "templateId": "6512f3a9c4d21b0f7e8a4c19",
  "version": 3,
  "data": {
    "clientName": "Acme Studio",
    "total": "$1,130.50",
    "lineItems": [
      { "description": "Design", "qty": 2, "price": "$400.00" }
    ]
  }
}

→ 200 · application/pdf
templateId

Required. The template to render.

version

Optional. Pin a published version; omit for the latest.

data

Optional. Values for your variables; falls back to the template’s sample data.

Every response carries rate-limit headers so integrations can self-throttle: X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset for the per-minute window, plus -Month variants for the monthly window. During the free beta the defaults are 10 renders/minute and 1,000 renders/month per organization.

Error states

Errors come back as standard HTTP status codes with a message. The common ones:

400Invalid request

The JSON body failed validation — usually a missing templateId or malformed data.

401Auth failed

No API key was sent, or the key is unknown/revoked. Send it as a Bearer token or x-api-key header.

404Not found / not published

The templateId does not exist, or it exists but has no published version (publish it in the editor first).

429Rate limited

You exceeded the per-minute or monthly render allowance. Check the Retry-After and X-RateLimit-* headers, then back off.

500Render failed

The template rendered but PDF generation errored. Retry; if it persists, get in touch.

503Capacity exhausted

The render pool is momentarily saturated. The template is fine — retry after a short delay.

401, 404 and 429 mean fix the request (key, template, or back off). 500 and 503 are safe to retry — the template itself is fine.

Usage dashboard

Your organization’s Usage page reports API render activity over the last 7, 30 or 90 days. Editor previews and Download-PDF clicks are metered separately and never counted against your API numbers.

Renders

Total successful API renders in the period, and the volume of PDF generated.

Error rate

Share of requests that failed, with the raw failed count.

Latency

p95 and average render time, so you can see the slow tail, not just the average.

Rate limited

How many requests were rejected with a 429 — a sign you’re near your allowance.

By template

Renders, failures, latency and last-render time broken down per template.

By error

A count of each failure type (not found, not published, invalid request, and so on).

A Beta allowance panel shows how much of your monthly and per-minute render budget is left and when each window resets, so you can see the ceiling before you hit it. Render history is retained for 90 days.

Stuck on something not covered here? Email us — we’re happy to help.

Stop hand-crafting PDFs

Your first template takes five minutes. The ten-thousandth PDF takes a fraction of a second.

Try it free