API Documentation
pdfkitt converts an HTML string — or a live URL — to PDF via a single HTTP endpoint. No SDK required.
Quick Start
1. Sign up and create an API key
Create a free account then go to Dashboard → API Keys to generate your key. Keys look like
pdfw_live_...2. Make your first request
curl -X POST https://api.pdfkitt.dev/v1/convert \ -H "Authorization: Bearer pdfw_live_YOUR_KEY" \ -H "Content-Type: application/json" \ -d '{"html":"<h1>Hello PDF</h1>"}' \ --output hello.pdf3. Check your usage
curl https://api.pdfkitt.dev/v1/usage \ -H "Authorization: Bearer pdfw_live_YOUR_KEY"
Authentication
Every request must include your API key in the Authorization header.
Authorization: Bearer pdfw_live_YOUR_KEYKeep your key secret. If it is compromised, revoke it from the dashboard and create a new one. Keys are hashed in the database — we cannot recover a lost key.
POST /v1/convert
https://api.pdfkitt.dev/v1/convertConverts an HTML string or a URL to a PDF binary. Returns application/pdf.
Request body
{
// Provide exactly one of "html" or "url".
"html": "<h1>Hello</h1><p>World</p>", // an HTML string to render, OR:
"url": "https://example.com/invoice", // a URL to fetch and render
"javascript": false, // run the page's JavaScript? (default: false)
"options": {
"page_size": "A4", // "A4" | "Letter" | "Legal" (default: "A4")
"landscape": false, // boolean (default: false)
"print_background": true, // boolean (default: true)
"scale": 1, // 0.1-2 (default: 1)
"margins": {
"top": "20mm",
"right": "15mm",
"bottom": "20mm",
"left": "15mm"
}
}
}HTML or URL input
Send exactly one of html or url. With a URL, pdfkitt fetches the page server-side and renders it. Requests to private, loopback, or cloud-metadata addresses are blocked, and redirects are re-checked on every hop.
JavaScript rendering
JavaScript is off by default — static HTML and CSS render as-is, which is faster and the safest default. Set "javascript": true to run the page's scripts before the PDF is captured, for client-rendered pages and charts. Each render is capped at 30 seconds.
Response
200 OK — PDF binary with Content-Type: application/pdf. On error, returns JSON (see Error Codes).
Examples
curl
curl -X POST https://api.pdfkitt.dev/v1/convert \
-H "Authorization: Bearer pdfw_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"html": "<h1>Invoice #1001</h1><p>Due: 2026-05-01</p>",
"options": {
"page_size": "Letter",
"margins": { "top": "20mm", "right": "15mm", "bottom": "20mm", "left": "15mm" }
}
}' \
--output invoice.pdfNode.js
const res = await fetch("https://api.pdfkitt.dev/v1/convert", {
method: "POST",
headers: {
"Authorization": "Bearer pdfw_live_YOUR_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({
html: "<h1>Hello PDF</h1>",
options: { page_size: "A4", margins: { top: "20mm", bottom: "20mm" } },
}),
});
if (!res.ok) throw new Error(await res.text());
const pdf = Buffer.from(await res.arrayBuffer());
fs.writeFileSync("output.pdf", pdf);Python
import requests
response = requests.post(
"https://api.pdfkitt.dev/v1/convert",
headers={
"Authorization": "Bearer pdfw_live_YOUR_KEY",
"Content-Type": "application/json",
},
json={
"html": "<h1>Hello PDF</h1>",
"options": {"page_size": "A4"},
},
)
response.raise_for_status()
with open("output.pdf", "wb") as f:
f.write(response.content)Render a URL (JavaScript on)
curl -X POST https://api.pdfkitt.dev/v1/convert \
-H "Authorization: Bearer pdfw_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{ "url": "https://example.com/report", "javascript": true }' \
--output report.pdfGET /v1/usage
https://api.pdfkitt.dev/v1/usageReturns your current quota consumption for the billing period.
Response
{
"used": 142,
"limit": 1000,
"resets_at": "2026-05-01T00:00:00.000Z",
"plan": "free"
}Example
curl https://api.pdfkitt.dev/v1/usage \
-H "Authorization: Bearer pdfw_live_YOUR_KEY"Error Codes
All errors return JSON in this shape:
{
"error": {
"code": "quota_exceeded",
"message": "Monthly quota exceeded",
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"docs_url": "https://pdfkitt.dev/docs#errors"
}
}| Code | HTTP | Meaning |
|---|---|---|
invalid_html | 400 | The html field is missing or empty |
invalid_request | 400 | Body is malformed, or not exactly one of html / url was provided |
invalid_url | 400 | The url is malformed or uses a disallowed port |
blocked_url | 400 | The url or a resource it loads points to a private or disallowed host |
invalid_api_key | 401 | API key is missing or invalid |
quota_exceeded | 429 | Monthly PDF quota exhausted — upgrade plan or wait for reset |
rate_limited | 429 | Too many requests per minute — back off and retry |
pool_busy | 503 | All render slots are busy — retry after a moment |
render_timeout | 408 | HTML took longer than 30 s to render — simplify the document |
render_failed | 500 | Internal rendering error — contact support with request_id |
Limits
Monthly quota
| Plan | PDFs / mo |
|---|---|
| Free | 1,000 |
| Starter | 10,000 |
| Growth | 50,000 |
| Scale | 200,000 |
Rate limits
| Plan | Req / min |
|---|---|
| Free | 30 |
| Starter | 60 |
| Growth | 120 |
| Scale | 300 |
Quota resets on a rolling monthly cycle (one month after each period begins) — see resets_at in the /v1/usage response for your exact date. Rate limits apply per API key per minute. Exceeding either returns a 429 response.