API reference
Base URL https://pdfgen.arsi.in. Authenticate with your project's API key as a bearer token. Machine-readable spec: /v1/openapi.json.
Render a PDF
POST /v1/pdf returns the PDF bytes directly.
curl -X POST https://pdfgen.arsi.in/v1/pdf \
-H "Authorization: Bearer pk_live_your_key" \
-H "Content-Type: application/json" \
-d '{"url": "https://example.com/invoice/42", "pdf": {"format": "a4"}}' \
-o invoice.pdfRequest body
| Field | Type | Description |
|---|---|---|
| url | string (required) | Public http(s) URL to render. |
| callback_url | string | If set, respond 202 immediately and POST the PDF here when ready. |
| pdf.format | auto | a4 | letter | legal | … | Paper size. Default a4. auto uses the page's own CSS @page size when it declares one, otherwise A4. |
| pdf.single_page | boolean | One continuous page as tall as the content, with no page breaks. Width follows viewport.width (default 1920). Ignores format, landscape and margin. Default false. |
| pdf.landscape | boolean | Landscape orientation. Default false. |
| pdf.margin | { top, right, bottom, left } | Numbers (px) or strings like '10mm'. |
| pdf.print_background | boolean | Include background colors and images. Default true. |
| pdf.header_template / footer_template | string | HTML with pageNumber, totalPages, date, title classes. |
| wait.until | load | domcontentloaded | networkidle0 | networkidle2 | When navigation is done. Default load (fast); for single-page apps prefer wait.selector or wait.ready_signal. |
| wait.selector | string | Wait until this CSS selector exists (strict). |
| wait.ready_signal | boolean | Wait until the page sets window.pdfReady = true (strict). |
| wait.delay_ms | number | Extra fixed delay, max 10000. |
| timeout_ms | number | Per-render budget, capped by your plan. Default 30000. |
| emulate_media | print | screen | CSS media type. Default print. |
| auto_scroll | boolean | Scroll the page to trigger lazy-loaded images. |
| hide_selectors | string[] | Hide cookie banners, chat widgets and the like. |
| css | string | Extra CSS to inject (e.g. @font-face). |
| headers / cookies / basic_auth | object | Authenticate to pages behind your own login. |
| metadata | object | Echoed back in callbacks (X-PDFGen-Metadata). |
Async rendering & callbacks
Add callback_url and the API responds 202 with a job. When the PDF is ready we POST it to your URL as application/pdf (or a JSON pdf.failed event). Poll GET /v1/jobs/:id any time for status and a signed download link.
import crypto from "node:crypto";
// X-PDFGen-Signature: t=<unix seconds>,v1=<hex hmac>
export function verify(rawBody, header, secret) {
const { t, v1 } = Object.fromEntries(header.split(",").map((p) => p.split("=")));
if (Math.abs(Date.now() / 1000 - Number(t)) > 300) return false;
const expected = crypto
.createHmac("sha256", secret)
.update(Buffer.concat([Buffer.from(`${t}.`), rawBody]))
.digest("hex");
return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(v1));
}Reliability features
- Idempotency-Key header: retries return the original result without rendering or charging again.
- Prefer: respond-async: on temporary overload, get a 202 job instead of a 503.
- Failed renders are never charged; your quota is released automatically.
Errors
Errors use application/problem+json with a stable code.
| Status | Codes | Meaning |
|---|---|---|
| 400 | VALIDATION_FAILED, URL_NOT_ALLOWED | Fix the request; see errors[] for field details. |
| 401 | INVALID_API_KEY | Missing, invalid or revoked key. |
| 402 | QUOTA_EXCEEDED | Monthly quota and credits exhausted. |
| 409 | IDEMPOTENCY_CONFLICT, CONFLICT | Key reused for a different request, or still processing. |
| 422 | TARGET_UNREACHABLE, TARGET_BLOCKED, WAIT_TIMEOUT, RENDER_TIMEOUT | The page itself couldn't be rendered. Not charged. |
| 429 | RATE_LIMITED, CONCURRENCY_LIMIT | Slow down; honour Retry-After. |
| 503 | RENDER_UNAVAILABLE | Temporary. Retry after Retry-After, or send Prefer: respond-async. |