InkPDF API Reference
One endpoint, four input modes, sensible defaults. The base URL for all examples is
your deployment origin (shown as https://api.inkpdf.dev).
Authentication
Pass your API key with every request, either way works:
Authorization: Bearer ink_live_4f2…
# or
X-Api-Key: ink_live_4f2…Keys are created self-serve: free keys at POST /v1/keys/free
(or the form on the homepage), paid keys automatically after Stripe checkout.
Keys are stored hashed and displayed exactly once.
Quickstart
# 1. get a key curl -X POST https://api.inkpdf.dev/v1/keys/free \ -H "Content-Type: application/json" \ -d '{"email":"you@company.com"}' # 2. render your first PDF curl -X POST https://api.inkpdf.dev/v1/pdf \ -H "Authorization: Bearer ink_live_…" \ -H "Content-Type: application/json" \ -d '{"html":"<h1>Hello, PDF!</h1>"}' \ --output hello.pdf
Client libraries
Official, zero-dependency clients wrap the API with typed convenience methods
(invoice(), markdown(),
usage()…):
# Node.js (18+) — npmjs.com/package/inkpdf npm install inkpdf # Python (3.9+) — pypi.org/project/inkpdf-client (imports as `inkpdf`) pip install inkpdf-client
Every other language works with a plain HTTP call — see the per-language guides for PHP, Ruby, Go, and C# examples.
POST /v1/pdf
The response body is the PDF itself (application/pdf),
unless you set "output": "base64" to receive JSON.
Successful responses include your quota state in headers:
x-inkpdf-used, x-inkpdf-quota.
Input modes — provide exactly one
| Field | Type | Description |
|---|---|---|
| html | string | Raw HTML document or fragment. Full CSS support, up to 2 MB. |
| url | string | Public http(s) URL to render. Private/internal addresses are blocked. |
| markdown | string | GitHub-flavored Markdown, styled by theme: clean (default), github, or serif. |
| template | string | ID of a built-in template; pass its variables in data. |
The options object (all optional)
| Option | Default | Description |
|---|---|---|
| format | "A4" | A0–A6, Letter, Legal, Tabloid, Ledger. |
| landscape | false | Landscape orientation. |
| margin | 0 | Object of CSS sizes: {"top":"1in","bottom":"20mm"}. |
| printBackground | true | Include background colors/images. |
| scale | 1 | Render scale, 0.1–2. |
| pageRanges | all | e.g. "1-3, 5". |
| headerTemplate / footerTemplate | — | HTML for repeating header/footer. Supports Chromium's pageNumber/totalPages classes. |
| preferCSSPageSize | false | Respect @page CSS size. |
| filename | auto | Filename for the Content-Disposition header. |
# page numbers via footerTemplate
{
"url": "https://example.com",
"options": {
"margin": {"top": "20mm", "bottom": "18mm"},
"footerTemplate": "<div style='font-size:9px;width:100%;text-align:center'>
Page <span class='pageNumber'></span> of <span class='totalPages'></span></div>"
}
}Built-in templates
List them (with full sample payloads) at GET /v1/templates,
or preview any of them rendered: GET /v1/templates/<id>/preview.
| ID | Best for | Notable fields |
|---|---|---|
| invoice | Billing clients | brand{name,color}, to{}, items[], taxRate, bankDetails |
| receipt | Payment confirmations | brand{}, customer{}, items[], paymentMethod, paidDate |
| quote | Proposals & estimates | to{}, items[], validUntil, terms + signature block |
| report | Business reporting | title, metrics[{label,value,delta}], sections[{heading,body}] (body accepts HTML) |
| certificate | Courses & awards | recipient, course, signatories[] — renders landscape automatically |
{
"template": "invoice",
"data": {
"brand": { "name": "Acme Studio", "color": "#0f766e" },
"invoiceNumber": "INV-2043",
"issueDate": "2026-07-01",
"dueDate": "2026-07-31",
"currency": "USD",
"to": { "name": "Northwind Traders", "address": "1 Market St, SF" },
"items": [
{ "description": "Design work", "quantity": 10, "unitPrice": 95 }
],
"taxRate": 8.5
}
}Template data conventions
Money fields are plain numbers; set currency to any ISO code
(USD, EUR, GBP…). Dates accept anything new Date() parses.
Line totals, subtotals, tax, and grand totals are computed for you from
items[].quantity × items[].unitPrice.
Usage & quotas
curl https://api.inkpdf.dev/v1/usage -H "Authorization: Bearer ink_live_…"
{ "plan": "starter", "month": "2026-07", "used": 412,
"quota": 2000, "remaining": 1588, "watermark": false }Quotas reset on the first of each calendar month (UTC). Only successful renders count. Free-plan renders carry a small footer watermark; paid plans never do.
Errors
Errors are JSON with stable error codes:
| Status | Code | Meaning |
|---|---|---|
| 400 | validation_error | Bad body — the message says exactly what. |
| 401 | missing_api_key / invalid_api_key | No key, malformed key, or deactivated key. |
| 403 | demo_restriction | Playground endpoint doesn't allow URL mode. |
| 408 | render_error | The page didn't finish loading within 30s. |
| 422 | render_error | URL couldn't be fetched (DNS, connection refused…). |
| 429 | quota_exceeded / rate_limited | Monthly quota or daily anti-abuse limit reached. |
| 503 | billing_not_configured | Stripe endpoints before configuration. |