API documentation
Authenticate with Authorization: Bearer YOUR_API_KEY. Download OpenAPI 3.1. The examples make one request and save a PDF. A successful render consumes one unit; failed renders do not. A completed render whose response is lost may still count; requests have no idempotency support.
curl
curl --fail-with-body https://pdf.1729.studio/v1/pdf \
-H "Authorization: Bearer $PDF_API_KEY" \
-H 'Content-Type: application/json' \
-d '{"html":"<h1>Hello</h1>","format":"A4","print_background":true}' \
-o document.pdfPython
import os, requests
r = requests.post("https://pdf.1729.studio/v1/pdf",
headers={"Authorization": "Bearer " + os.environ["PDF_API_KEY"]},
json={"html": "<h1>Hello</h1>"}, timeout=40)
r.raise_for_status()
open("document.pdf", "wb").write(r.content)JavaScript (Node.js)
import { writeFile } from 'node:fs/promises';
const response = await fetch('https://pdf.1729.studio/v1/pdf', {
method: 'POST',
headers: {Authorization: `Bearer ${process.env.PDF_API_KEY}`,
'Content-Type': 'application/json'},
body: JSON.stringify({html: '<h1>Hello</h1>'}),
signal: AbortSignal.timeout(40000)
});
if (!response.ok) throw new Error(await response.text());
await writeFile('document.pdf', Buffer.from(await response.arrayBuffer()));Options
Provide exactly one of html or url. For URL rendering, explicitly include its host and every redirected/resource host in allow_hosts, for example {"url":"https://example.com/","allow_hosts":["example.com"]}. No wildcards, credentials, non-default ports or private IPs. Default network policy allows no external hosts. HTTPS subrequests use a connection pinned to a checked public IP. DNS is checked again for every new connection.
format: A4 (default) or Letter. landscape and print_background: false by default. margins: top/bottom/left/right in inches, each 0–2; default 0.4. Supplying a margins object makes omitted sides zero. header_template and footer_template: HTML, at most 64 KiB each; Chromium's pageNumber/totalPages classes work, scripts do not. Combined HTML/templates: 5 MiB. Request JSON: 7 MiB maximum.
wait_for_selector: optional CSS selector, at most 512 characters. wait_timeout_ms: 1–10,000, default 5,000, within the total 30-second render budget. Navigation waits for load; a selector can signal async application readiness. Frames, workers, objects, media and form submissions are disabled. WebSockets are unsupported: any attempt (including to an allowed host, or caught by page JavaScript) fails the render with 422 and does not consume quota. Enforced browser security blocks and uncaught JavaScript errors/rejections fail the render with 422 instead of producing a partial PDF. No browser cookies/authentication supplied, no arbitrary script evaluation option.
Raw text/html bodies are accepted with default options and no external network. Outputs are capped at 20 MiB and 50 pages. Failed external resources can fail a render; this service is not a general browser automation engine.
Errors and limits
400 invalid options/JSON; 401 invalid key; 413 oversized raw HTML; 422 render failure, blocked resource or timeout; 429 quota, rate or capacity limit; 502 invalid renderer output; 503 unavailable render worker or usage storage (worker unavailability includes Retry-After: 2). JSON errors have an error field. Some oversized JSON is reported as 400. Retry transient failures with exponential backoff; do not blindly retry 422. Rate limits per account per minute: Free 10, Developer 30, Team 60, Studio 120. There is no waiting queue. An admitted slot can wait up to 750 ms for its worker socket to return; unavailable workers produce 503, not 422. Rendering POSTs are never automatically replayed after transmission.