serverlesspdf API Reference

Integrate PDF generation into your product in minutes. A single predictable API for render and download flows.

Quickstart

Create an account, confirm your email, create an API key in the dashboard, then send the request below from your backend.

  • Sign up and confirm your email, then create an API key in the dashboard.
  • POST your HTML to /v1/documents/render and read documentId from the JSON response.
  • GET /v1/documents/:documentId/download with the same key and follow the redirect to the signed URL.
curl -X POST https://serverlesspdf.com/v1/documents/render \
  -H "Authorization: Bearer <YOUR_API_KEY>" \
  -H "Content-Type: application/json" \
  -d '{"html": "<h1>Hello PDF!</h1>"}'
# → {"documentId": "<id>", "downloadUrl": "/v1/documents/<id>/download", ...}
curl -L https://serverlesspdf.com/v1/documents/<id>/download \
  -H "Authorization: Bearer <YOUR_API_KEY>" -o out.pdf

downloadUrl is relative — prepend https://serverlesspdf.com. On success the requestId lives in the x-request-id response header.

JavaScript (fetch)
const res = await fetch("https://serverlesspdf.com/v1/documents/render", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${process.env.SERVERLESSPDF_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ html: "<h1>Hello PDF!</h1>", options: { pageSize: "A4" } }),
});
const { documentId, downloadUrl } = await res.json();
const pdf = await fetch(`https://serverlesspdf.com${downloadUrl}`, {
  headers: { "Authorization": `Bearer ${process.env.SERVERLESSPDF_API_KEY}` },
});
Python (urllib, standard library only)
import json, os, urllib.request

KEY = os.environ["SERVERLESSPDF_API_KEY"]
BASE = "https://serverlesspdf.com"

def call(path, payload=None):
    req = urllib.request.Request(
        BASE + path,
        data=json.dumps(payload).encode() if payload is not None else None,
        headers={"Authorization": f"Bearer {KEY}", "Content-Type": "application/json"},
        method="POST" if payload is not None else "GET",
    )
    with urllib.request.urlopen(req) as res:
        return json.load(res), res.headers.get("x-request-id")

body, _ = call("/v1/documents/render", {"html": "<h1>Hello PDF!</h1>"})
pdf, _ = call(body["downloadUrl"])  # follows the signed-URL redirect
print(body["documentId"], body["pageCount"])

POST /v1/documents/render

POST

Primary PDF generation method. Accepts HTML, CSS and render options. Returns a ready document or a link to it.

Request Headers
Authorization: Bearer <api_key>
Content-Type: application/json
Request Body
{
  "html": "<!doctype html><html>...</html>",
  "options": {
    "pageSize": "A4",
    "landscape": false,
    "printBackground": true,
    "margin": {
      "top": "12mm",
      "right": "12mm",
      "bottom": "12mm",
      "left": "12mm"
    }
  }
}
Request fields
  • html (required, string up to 10 MB) — the full ready-made HTML document.
  • options.pageSize (optional, A4 | LETTER) — page size, default A4.
  • options.landscape (optional, boolean) — landscape orientation.
  • options.printBackground (optional, boolean) — print CSS backgrounds.
  • options.margin.top | right | bottom | left (optional, string) — CSS sizes, e.g. 12mm.

On success the API returns the document id and a download path you can call with the same API key.

Render response
{
  "documentId": "<id>",
  "status": "success",
  "downloadUrl": "/v1/documents/<id>/download",
  "sizeBytes": 12345,
  "pageCount": 1,
  "checksum": "<sha256>"
}

GET /v1/documents/:documentId/download

GET

Fetch a generated PDF document by ID. On success the route returns a redirect to a short-lived signed URL for secure downloads.

When an Authorization header is present, only the API key is used; an invalid key returns AUTH_FAILED 401 without falling back to the session.

On success the route answers with a 302 redirect to a short-lived signed URL. Download the PDF by following the redirect.

curl -L https://serverlesspdf.com/v1/documents/<id>/download \
  -H "Authorization: Bearer <YOUR_API_KEY>" -o out.pdf
# → 302 Found
# → Location: https://storage/v1/object/sign/documents/<id>.pdf?token=<short-lived>
# → followed automatically with -L; without -L read the Location header
Auth options
  • Session cookie (browser)
  • Bearer API key (backend integrations)

Error format

All errors use the same structured response shape:

Error codes
  • 400 VALIDATION_ERROR / INVALID_HTML: fix the payload and retry.
  • 401 AUTH_FAILED: missing or invalid API key.
  • 403 INSUFFICIENT_SCOPE / QUOTA_EXCEEDED: the key lacks the required scope or the quota window is exhausted.
  • 404 DOCUMENT_NOT_FOUND: unknown id or a document from another workspace.
  • 413 HTML_TOO_LARGE: shrink the HTML payload and retry.
  • 429 RATE_LIMITED / CONCURRENT_LIMIT_EXCEEDED: back off and respect the Retry-After header.
  • 500 RENDER_ERROR / 502 STORAGE_UPLOAD_FAILED / 504 RENDER_TIMEOUT: retry with simpler HTML and keep requestId for support.
{
  "error": {
    "code": "QUOTA_EXCEEDED",
    "message": "Monthly limit reached",
    "requestId": "req_123abc"
  }
}
# 429 responses include a Retry-After header (seconds): Retry-After: 60

The message names the offending field, so agents and SDKs can repair the payload without asking:

VALIDATION_ERROR example
{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Invalid render payload: \"options.pageSize\" — Invalid option: expected one of \"A4\"|\"LETTER\".",
    "requestId": "req_456def"
  }
}

Public Beta limits

  • 1,000 renders per quota window and up to 30 concurrent jobs.
  • HTML up to 10 MB, generated PDF up to 100 MB, and up to 1,000 pages per document.
  • Generated PDFs remain available for up to 90 days through signed download URLs.

Errors and recovery

  • QUOTA_EXCEEDED or CONCURRENT_LIMIT_EXCEEDED: wait for the quota window or reduce parallel requests.
  • INVALID_HTML or HTML_TOO_LARGE: validate the payload and keep it within the documented size limit.
  • RENDER_TIMEOUT or RENDER_ERROR: retry with simpler HTML, then keep the requestId when contacting support.