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

FieldTypeDescription
htmlstringRaw HTML document or fragment. Full CSS support, up to 2 MB.
urlstringPublic http(s) URL to render. Private/internal addresses are blocked.
markdownstringGitHub-flavored Markdown, styled by theme: clean (default), github, or serif.
templatestringID of a built-in template; pass its variables in data.

The options object (all optional)

OptionDefaultDescription
format"A4"A0–A6, Letter, Legal, Tabloid, Ledger.
landscapefalseLandscape orientation.
margin0Object of CSS sizes: {"top":"1in","bottom":"20mm"}.
printBackgroundtrueInclude background colors/images.
scale1Render scale, 0.1–2.
pageRangesalle.g. "1-3, 5".
headerTemplate / footerTemplateHTML for repeating header/footer. Supports Chromium's pageNumber/totalPages classes.
preferCSSPageSizefalseRespect @page CSS size.
filenameautoFilename 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.

IDBest forNotable fields
invoiceBilling clientsbrand{name,color}, to{}, items[], taxRate, bankDetails
receiptPayment confirmationsbrand{}, customer{}, items[], paymentMethod, paidDate
quoteProposals & estimatesto{}, items[], validUntil, terms + signature block
reportBusiness reportingtitle, metrics[{label,value,delta}], sections[{heading,body}] (body accepts HTML)
certificateCourses & awardsrecipient, 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:

StatusCodeMeaning
400validation_errorBad body — the message says exactly what.
401missing_api_key / invalid_api_keyNo key, malformed key, or deactivated key.
403demo_restrictionPlayground endpoint doesn't allow URL mode.
408render_errorThe page didn't finish loading within 30s.
422render_errorURL couldn't be fetched (DNS, connection refused…).
429quota_exceeded / rate_limitedMonthly quota or daily anti-abuse limit reached.
503billing_not_configuredStripe endpoints before configuration.