Docs
PDFops API reference
Seven endpoints, running on V8 edge runtimes — Cloudflare
Workers, Vercel, Deno, Bun — so you call them from inside your
edge function, no headless browser required. Keyless calls get
100 requests per IP per month; a free API key raises that to 250
requests per month — POST your email to
/api/signup and pass the emailed key as
X-API-Key. Paid tiers (Indie 4,000, Pro 25,000
requests/month) are live at /pricing. All
endpoints accept and return PDF or JSON; no SDK required.
There's also a no-code path: email a
blank fillable PDF + values to fill@pdfops.dev and the reply
comes back filled, with the equivalent API call included.
Endpoints
-
POST /api/fill-formFill AcroForm fields in a PDF template you upload. Multipart request with
pdf(binary) andfields(JSON). Returns the filled PDF asapplication/pdf. -
POST /api/mergeMerge two or more PDFs into one. Multipart request with repeated
pdffields, concatenated in field order. Returns the merged PDF asapplication/pdf. -
POST /api/inspectList the AcroForm fields in a PDF you upload — names, types, options, current values — plus a paste-ready
fillTemplatefor/api/fill-form. Multipart request withpdf(binary). Returns JSON. -
POST /api/invoiceGenerate a complete US-Letter invoice PDF from JSON — no template upload. JSON body with
from,to,items; optional number, dates, currency, tax rate, notes. Deterministic: the same JSON returns a byte-identical PDF. Returnsapplication/pdf. -
POST /api/signupGet a free API key (250 requests/month) emailed to you. JSON body with
email. Returns{ "ok": true }; the key arrives by email. Re-signing up rotates your key. -
GET /api/usageCheck your key's quota: tier, limit, used, remaining, period, reset date. Requires
X-API-Key. Reads the same counter the rate limiter enforces. -
POST /api/waitlistFeedback: tell us what to build next. JSON body with use case, current solution, expected volume, pay range, optional email + message. Returns
{ "ok": true }. Not needed for keys — those are self-serve at/pricing.
Using this for real? A free API key raises the keyless 100 calls per IP to 250 requests a month, with no card.
Contract — applies to every endpoint
- Base URL:
https://pdfops.dev - Auth: Optional
X-API-Keyheader on the PDF endpoints. No header: anonymous, per-IP rate-limited. Valid key: your tier's monthly quota. Invalid or revoked key:401 invalid_api_key— no fallback to the IP cap. - Rate limit: anonymous, 100 successful (2xx) requests per IP per calendar month (UTC); keyed, your tier's quota — Free 250, Indie 4,000, Pro 25,000 per month. The middleware only counts accepted requests; 4xx and 5xx don't consume budget.
- Rate-limit response:
429 Too Many RequestswithRetry-Afterseconds-until-next-month — same shape anonymous or keyed. - Request body format:
multipart/form-datafor PDF-upload endpoints (/fill-form,/merge,/inspect);application/jsonfor/invoice(structured data in, PDF out),/signup, and/waitlist. - Response on success:
200 OKwithapplication/pdfbody (binary endpoints),application/jsonwith{ "ok": true }(signup, waitlist), or a JSON report (/inspect,/usage). - Response on validation failure:
400 Bad Requestwithapplication/jsonbody{ "error": "<code>", "details": "<explanation>" }. - Response on server error:
500 Internal Server Errorwith the same JSON shape. Treat as retryable.
Authentication & quotas
The PDF endpoints (/api/fill-form, /api/merge, /api/inspect) resolve your quota from the X-API-Key header:
| Request | Result |
|---|---|
No X-API-Key header | Anonymous: 100 requests/IP/month. |
| Valid key | Your tier's quota: Free 250 / Indie 4,000 / Pro 25,000 requests per month. |
| Invalid or revoked key | 401 invalid_api_key. No fallback to the IP cap — a caller who thinks they're keyed should find out, not be quietly mis-metered. |
| Over quota | 429 with { "error": "rate_limited" } and a Retry-After header (seconds until the 1st of next month, UTC). |
Quota is consumed only on 2xx responses — failed requests are free. Get a free key at /pricing (or POST /api/signup); check consumption with GET /api/usage or the dashboard.
Clients & integrations
- TypeScript SDK:
npm install pdfops-sdk— typed, zero-dependency client for every endpoint. Edge-safe: Cloudflare Workers, Vercel Edge, Deno, Bun, Node 18+, browsers. Source · npm. - Python client:
pip install pdfops— zero-dependency, typed client for every endpoint (Python 3.9+);inspect,fill_form,merge,invoice,usage. PyPI · source. - n8n node (verified by n8n):
n8n-nodes-pdfops— Fill Form, Merge, Inspect, Invoice. On self-hosted n8n, install it from Settings → Community Nodes. Get a free key for the node. Three published templates: fill, validate and email forms, merged onboarding packets, Stripe invoice PDFs. - Zapier (beta): Fill PDF Form, Merge PDFs, Inspect PDF Fields and Generate Invoice PDF as Zap actions, with file inputs and outputs that chain into Drive, Gmail or Dropbox steps. Accept the invite to use it before the public listing goes live.
- MCP server:
npx -y pdfops-mcp— gives AI agents (Claude Code, Claude Desktop, Cursor, any MCP client) localpdf_inspect/pdf_fill/pdf_merge/pdf_invoicetools. Tools take file paths, so PDF bytes never enter the model's context. Listed in the official MCP registry asdev.pdfops/pdfops-mcp. Setup for Claude Code, Claude Desktop, Cursor and VS Code · Source. - OpenAPI 3.1:
/openapi.json— full request/response schemas for codegen and tool integration.
Fillable PDFs — quick start
Don't have a fillable PDF handy? Grab the sample invoice-template.pdf — it has customer_name + total AcroForm fields ready to fill via the curl example on each docs page.
For tax forms (W-9, W-8BEN, I-9) the IRS publishes AcroForm-ready PDFs at irs.gov/forms-instructions — just rename the default IRS field names to something predictable (see the W-9 walkthrough) and your code survives the annual form refresh.
Get a free API key. Keyless calls get 100 requests per IP per month; a free key raises that to 250, with no card. Enter your email, the key arrives in your inbox, and the signup page walks you through a first call. For calling PDFops from an edge function, that call is one line:
const pdf = await fetch("https://pdfops.dev/api/invoice", { method: "POST", headers: { "X-API-Key": env.PDFOPS_API_KEY, "Content-Type": "application/json" }, body: JSON.stringify({ from: "Acme LLC", to: "Globex", items: [{ description: "Consulting", quantity: 2, unit_price: 150 }] }) });