PDF API

Render a PDF

Loads the URL in a managed browser and prints it to PDF — by default the application/pdf bytes, or render metadata plus a hosted PDF URL when `"response": "json"`. `viewport` controls the responsive layout the page renders at; `paper_format` controls the printed sheet (navigation budget 25s, total render budget 75s). A successful PDF consumes one render unit of the monthly quota — the same unit type as screenshots; failed renders are not metered.

POST/api/v1/pdf

Authentication

Send your API key as a bearer token on every request: Authorization: Bearer sw_.... Create keys in the dashboard; see the authentication docs for details.

Parameters

Request content type: application/json

ParameterTypeDefaultDescription
urlrequiredstringAbsolute URL of the page to render. Public http(s) addresses only — localhost, private hosts, and internal IP ranges are rejected.
paper_formatstring"letter"Paper size of the generated PDF. One of: letter, a4, legal.
landscapebooleanfalseRotate the selected paper size to landscape orientation.
print_backgroundbooleantrueInclude CSS background colors and images in the PDF.
prefer_css_page_sizebooleanfalseLet the page's CSS @page size override paper_format when the page defines one.
scalenumber1Render scale applied to the page content. Range: 0.1–2.
marginobject{"top":0,"right":0,"bottom":0,"left":0}Page margins. Each side accepts a non-negative number (CSS pixels) or a CSS length string with px, in, cm, or mm units.
margin.topnumber | string0Margin length: a non-negative number (CSS pixels, up to 2000) or a CSS length string using px, in, cm, or mm.
margin.rightnumber | string0Margin length: a non-negative number (CSS pixels, up to 2000) or a CSS length string using px, in, cm, or mm.
margin.bottomnumber | string0Margin length: a non-negative number (CSS pixels, up to 2000) or a CSS length string using px, in, cm, or mm.
margin.leftnumber | string0Margin length: a non-negative number (CSS pixels, up to 2000) or a CSS length string using px, in, cm, or mm.
viewportobject{"width":1440,"height":900}Browser viewport applied before printing. Controls the page's responsive layout, not the paper dimensions.
viewport.widthinteger1440Viewport width in CSS pixels. Range: 320–3,840.
viewport.heightinteger900Viewport height in CSS pixels. Range: 320–2,160.
wait_untilstring"networkidle2"Page readiness event to wait for before rendering. One of: load, domcontentloaded, networkidle0, networkidle2.
delay_msinteger0Extra wait after page readiness, in milliseconds. Useful for animations or late client-side rendering. Range: 0–10,000.
responsestring"pdf""pdf" streams application/pdf bytes back; "json" returns render metadata and a hosted PDF URL instead. One of: pdf, json.

Examples

cURL
curl -X POST https://shotwisp.com/api/v1/pdf \
  -H "Authorization: Bearer $SHOTWISP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com", "paper_format": "a4"}' \
  -o page.pdf
Node.js
import { writeFile } from "node:fs/promises";

const res = await fetch("https://shotwisp.com/api/v1/pdf", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.SHOTWISP_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    url: "https://example.com",
    paper_format: "a4",
    margin: { top: "0.5in", right: "0.5in", bottom: "0.5in", left: "0.5in" },
  }),
});
if (!res.ok) throw new Error((await res.json()).error.message);
await writeFile("page.pdf", Buffer.from(await res.arrayBuffer()));
Python
import os, requests

res = requests.post(
    "https://shotwisp.com/api/v1/pdf",
    headers={"Authorization": f"Bearer {os.environ['SHOTWISP_API_KEY']}"},
    json={"url": "https://example.com", "paper_format": "a4", "landscape": True},
    timeout=90,
)
res.raise_for_status()
open("page.pdf", "wb").write(res.content)  # default response is PDF bytes

Response

Success status: 200. The PDF bytes, or render metadata in JSON mode. Content types: application/pdf, application/json.

200 OK — "response": "json"
{
  "id": "p8Fm3qRt7VwK2xNc",
  "url": "https://shotwisp.com/p/p8Fm3qRt7VwK2xNc",
  "paper_format": "a4",
  "landscape": false,
  "print_background": true,
  "prefer_css_page_size": false,
  "scale": 1,
  "margin": { "top": "0.5in", "right": "0.5in", "bottom": "0.5in", "left": "0.5in" },
  "viewport": { "width": 1440, "height": 900 },
  "wait_until": "networkidle2",
  "delay_ms": 0,
  "duration_ms": 1840,
  "size_bytes": 42718
}
Every response carries rate-limit headers; metered successes add quota headers. Byte responses (images, PDFs) carry the capture id in x-shotwisp-id.

Errors

Every error uses the envelope {"error": {"code", "message"}}. This endpoint can return:

CodeHTTP statusRetryableMeaning
unauthorized401noThe API key is missing, malformed, revoked, or the account is suspended or unverified. Do not retry with the same key. Fix the Authorization header or create a new API key.
quota_exceeded402noThe monthly upload or render quota is used up and overages are off or unavailable on the plan. Do not retry until the monthly quota resets, the plan is upgraded, or overages are enabled.
validation_error422noA request field failed validation. The message states which field and why. Do not retry unchanged. The message names the failing field; fix it first.
rate_limited429yesThe plan's per-minute request budget is spent. Rejected requests are not metered. Retry after waiting the number of seconds in the Retry-After header, then back off exponentially with jitter.
concurrency_limited429yesThe plan's concurrent-capture cap is fully in use by renders still in progress. Retry after the Retry-After delay once an in-flight capture finishes. Reduce parallelism to the plan's concurrency cap.
internal500yesUnexpected error on Shotwisp's side. The request was not metered. Safe to retry with exponential backoff.
render_failed502yesThe target page could not be loaded or rendered as a PDF. Failed renders are not metered. Retry once or twice with backoff. If it persists, the target page cannot be rendered — do not keep retrying.

Limits and machine-readable spec

Plan quotas, rate limits, and every fixed limit are consolidated in Limits. This endpoint is also fully described in the OpenAPI 3.1 specification (operationId createPdf). Building with an AI agent? Read the AI agent guide.