Skip to content

HTML → PDF

POST /pdf/v1/html renders an HTML string to a PDF with full fidelity — your CSS, web fonts, and JavaScript (including charts) all render. If it looks right in a browser, it looks right in the PDF.

Request body

{
  "html": "<h1>Hello</h1>",
  "data": { "name": "Ada" },
  "options": {
    "format": "A4",
    "orientation": "portrait",
    "margins": { "top": "20mm", "right": "15mm", "bottom": "20mm", "left": "15mm" },
    "include_page_numbers": false
  },
  "security": null,
  "delivery": "inline"
}

Only html is required; everything else has sensible defaults.

Field Type Default Notes
html string required. Your HTML. May contain {{ variables }}.
data object {} Values for the {{ variables }} in html (sandboxed Jinja2).
options.format string A4 A3 · A4 · Letter · Legal · Tabloid
options.orientation string portrait portrait · landscape
options.margins object all 0 CSS lengths as strings: "20mm", "1in", "0"
options.include_page_numbers bool false Adds page numbers in the footer
security object | null null Password + permissions — see below
delivery string inline inline (PDF bytes) or url (signed URL)

Page layout

curl -X POST https://api.kikidoc.dev/pdf/v1/html \
  -H "X-API-Key: $KIKIDOC_API_KEY" -H "Content-Type: application/json" \
  -d '{
    "html": "<h1>Landscape report</h1>",
    "options": {
      "format": "Letter",
      "orientation": "landscape",
      "margins": { "top": "10mm", "right": "10mm", "bottom": "10mm", "left": "10mm" },
      "include_page_numbers": true
    }
  }' --output report.pdf

Delivery: bytes vs a signed URL

  • "delivery": "inline" (default) — the response body is the PDF.
  • "delivery": "url" — KikiDoc uploads the PDF and returns a time-limited signed URL instead:
{ "url": "https://…/renders/ab12.pdf?…", "expires_at": "2026-06-24T12:00:00Z" }

Use url when you'd rather hand a link to a downstream step than move bytes around (great in no-code flows). The link expires (default 1 hour).

PDF security (password + permissions)

Add a security block to encrypt the PDF (AES-256):

{
  "html": "<h1>Confidential</h1>",
  "security": {
    "user_password": "open-sesame",
    "owner_password": "admin-only",
    "print": false,
    "extract_content": false
  }
}
  • user_password (required when security is present) — needed to open the document.
  • owner_password — gates permission changes (auto-generated if you set flags but omit it).
  • Permission flags (omit = allowed): annotations, extract_content, modify, assemble, print, print_high_res.

Other inputs

  • Render a URL instead of an HTML body: POST /pdf/v1/url with { "url": "https://…" } (SSRF-guarded; optional credentials for basic-auth pages).
  • Screenshot instead of a PDF: POST /pdf/v1/screenshot → PNG/JPEG/WebP.

See the API reference for the full schema of every endpoint.