Skip to content

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.pdf

Request body

FieldTypeDescription
urlstring (required)Public http(s) URL to render.
callback_urlstringIf set, respond 202 immediately and POST the PDF here when ready.
pdf.formatauto | a4 | letter | legal | …Paper size. Default a4. auto uses the page's own CSS @page size when it declares one, otherwise A4.
pdf.single_pagebooleanOne 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.landscapebooleanLandscape orientation. Default false.
pdf.margin{ top, right, bottom, left }Numbers (px) or strings like '10mm'.
pdf.print_backgroundbooleanInclude background colors and images. Default true.
pdf.header_template / footer_templatestringHTML with pageNumber, totalPages, date, title classes.
wait.untilload | domcontentloaded | networkidle0 | networkidle2When navigation is done. Default load (fast); for single-page apps prefer wait.selector or wait.ready_signal.
wait.selectorstringWait until this CSS selector exists (strict).
wait.ready_signalbooleanWait until the page sets window.pdfReady = true (strict).
wait.delay_msnumberExtra fixed delay, max 10000.
timeout_msnumberPer-render budget, capped by your plan. Default 30000.
emulate_mediaprint | screenCSS media type. Default print.
auto_scrollbooleanScroll the page to trigger lazy-loaded images.
hide_selectorsstring[]Hide cookie banners, chat widgets and the like.
cssstringExtra CSS to inject (e.g. @font-face).
headers / cookies / basic_authobjectAuthenticate to pages behind your own login.
metadataobjectEchoed 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.

StatusCodesMeaning
400VALIDATION_FAILED, URL_NOT_ALLOWEDFix the request; see errors[] for field details.
401INVALID_API_KEYMissing, invalid or revoked key.
402QUOTA_EXCEEDEDMonthly quota and credits exhausted.
409IDEMPOTENCY_CONFLICT, CONFLICTKey reused for a different request, or still processing.
422TARGET_UNREACHABLE, TARGET_BLOCKED, WAIT_TIMEOUT, RENDER_TIMEOUTThe page itself couldn't be rendered. Not charged.
429RATE_LIMITED, CONCURRENCY_LIMITSlow down; honour Retry-After.
503RENDER_UNAVAILABLETemporary. Retry after Retry-After, or send Prefer: respond-async.