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.
/api/v1/pdfAuthentication
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
| Parameter | Type | Default | Description |
|---|---|---|---|
urlrequired | string | — | Absolute URL of the page to render. Public http(s) addresses only — localhost, private hosts, and internal IP ranges are rejected. |
paper_format | string | "letter" | Paper size of the generated PDF. One of: letter, a4, legal. |
landscape | boolean | false | Rotate the selected paper size to landscape orientation. |
print_background | boolean | true | Include CSS background colors and images in the PDF. |
prefer_css_page_size | boolean | false | Let the page's CSS @page size override paper_format when the page defines one. |
scale | number | 1 | Render scale applied to the page content. Range: 0.1–2. |
margin | object | {"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.top | number | string | 0 | Margin length: a non-negative number (CSS pixels, up to 2000) or a CSS length string using px, in, cm, or mm. |
margin.right | number | string | 0 | Margin length: a non-negative number (CSS pixels, up to 2000) or a CSS length string using px, in, cm, or mm. |
margin.bottom | number | string | 0 | Margin length: a non-negative number (CSS pixels, up to 2000) or a CSS length string using px, in, cm, or mm. |
margin.left | number | string | 0 | Margin length: a non-negative number (CSS pixels, up to 2000) or a CSS length string using px, in, cm, or mm. |
viewport | object | {"width":1440,"height":900} | Browser viewport applied before printing. Controls the page's responsive layout, not the paper dimensions. |
viewport.width | integer | 1440 | Viewport width in CSS pixels. Range: 320–3,840. |
viewport.height | integer | 900 | Viewport height in CSS pixels. Range: 320–2,160. |
wait_until | string | "networkidle2" | Page readiness event to wait for before rendering. One of: load, domcontentloaded, networkidle0, networkidle2. |
delay_ms | integer | 0 | Extra wait after page readiness, in milliseconds. Useful for animations or late client-side rendering. Range: 0–10,000. |
response | string | "pdf" | "pdf" streams application/pdf bytes back; "json" returns render metadata and a hosted PDF URL instead. One of: pdf, json. |
Examples
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.pdfimport { 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()));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 bytesResponse
Success status: 200. The PDF bytes, or render metadata in JSON mode. Content types: application/pdf, application/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
}x-shotwisp-id.Errors
Every error uses the envelope {"error": {"code", "message"}}. This endpoint can return:
| Code | HTTP status | Retryable | Meaning |
|---|---|---|---|
unauthorized | 401 | no | The 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_exceeded | 402 | no | The 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_error | 422 | no | A 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_limited | 429 | yes | The 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_limited | 429 | yes | The 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. |
internal | 500 | yes | Unexpected error on Shotwisp's side. The request was not metered. Safe to retry with exponential backoff. |
render_failed | 502 | yes | The 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.