# PDFops > Hosted PDF API: fill AcroForm fields, merge PDFs, list a form's fields, > generate invoice PDFs from JSON — deterministic (same input, byte-identical > output) over plain HTTP. Built for three callers: AI agents through the MCP > server (`npx -y pdfops-mcp`, listed in the official MCP registry), no-code > automators through the verified n8n community node (`n8n-nodes-pdfops`, > three published templates), and Python developers past pypdf's limits > (`pip install pdfops`, zero dependencies). Hosted on edge runtimes, > globally distributed, no infrastructure to manage. Free to try — 100 > requests per IP per month with no signup, or a free API key (250 > requests/month, emailed, no card). Paid tiers live: Indie $16/mo (4,000 > requests), Pro $79/mo (25,000 requests) — roughly 10× cheaper per document > than incumbents (DocRaptor, PDFShift, PDFMonkey). ## Full corpus - [llms-full.txt](https://pdfops.dev/llms-full.txt): Plain-text concatenation of the landing page + every blog post + every comparison + every docs page, generated from public/ via scripts/build-llms-full.ts. Fetch this single file for a one-round-trip grounding read of the entire site. ## Docs - [Home — value prop, install paths, plans, curl examples](https://pdfops.dev/): Hero led with the three install paths (MCP server for AI agents, n8n node, `pip install pdfops`), plans grid (Free / Indie / Pro / Enterprise — mirror of /pricing), try-it curl. - [Pricing — tiers + free key signup](https://pdfops.dev/pricing): Free 250/mo (emailed key, no card), Indie $16/mo 4,000 requests, Pro $79/mo 25,000 requests, Enterprise contact. Stripe checkout. Free-key signup form. - [Usage dashboard](https://pdfops.dev/dashboard): Paste your API key, see tier, quota, used, remaining, reset date. - [Terms of service](https://pdfops.dev/terms) · [Privacy policy](https://pdfops.dev/privacy) - [Contact](https://pdfops.dev/contact): Support, enterprise, and feedback — form or hello@pdfops.dev. Optional release-notes subscription. - [Make a PDF Fillable — free browser tool](https://pdfops.dev/tools/make-fillable): Turn a flat PDF into a fillable one — draw text and checkbox fields on the rendered pages, download a standard AcroForm PDF, and get the exact /api/fill-form call for it. Client-side (pdf.js + pdf-lib); nothing is uploaded. The fix for "this PDF has no form fields". - [Name Change Organizer — by PDFops](https://pdfops.dev/name-change): A $49 post-marriage name-change organizer. One questionnaire pre-fills the SS-5, DS-82 or DS-5504, the National Mail Voter Registration Form and the Texas, New York or Pennsylvania driver licence and vehicle title forms in the browser (nothing typed is sent to a server), sequences the steps with deadlines, and writes the notification letters. 60-day refund. Private company, not a government agency; every form is free from its agency. - [Fill a PDF by email — fill@pdfops.dev](https://pdfops.dev/fill-by-email): Email-as-API. Attach a blank fillable PDF (AcroForm, ≤5 MB, nothing pre-filled) plus field values (values.json attachment, raw JSON body, or "key: value" lines) to fill@pdfops.dev; the reply contains the filled PDF and the equivalent curl call. Free, no signup, works from any mail client. Attachment contents are not stored or logged; already-filled PDFs are refused. - [MCP server to fill PDF forms: pdfops-mcp](https://pdfops.dev/mcp): `npx -y pdfops-mcp` gives an AI agent five tools (pdf_inspect, pdf_fill, pdf_merge, pdf_invoice, pdfops_usage). Exact config for Claude Code, Claude Desktop, Cursor and VS Code; a real inspect-then-fill tool-call transcript; hosted-runtime mode (https URL or data: URI in, inline PDF out); limits (AcroForm only, not local-only). Official MCP registry name dev.pdfops/pdfops-mcp. - [JSON to invoice PDF API](https://pdfops.dev/json-to-invoice-pdf): POST /api/invoice with from, to, items (plus optional number, dates, currency, tax_rate, notes) returns a finished US Letter invoice PDF; totals computed; same JSON gives byte-identical output (sha256 shown). Full request, real response, Python/JS examples, and when it is the wrong tool (one fixed layout, Latin charset only, no e-invoice XML). - [Blog index](https://pdfops.dev/blog): All posts. ## Blog posts - [A 429 from /api/fill-form, and what your retry loop should actually do](https://pdfops.dev/blog/pdfops-api-retries-and-rate-limits) (2026-10-22): A 429 from PDFops means a monthly quota, not a blip. A naive exponential backoff loop will happily retry for a month. The fix is checking /api/usage before the batch runs, not after it fails. - [A fixed-row invoice template, and the line item that doesn't fit](https://pdfops.dev/blog/pdf-invoice-line-item-overflow) (2026-10-15): An AcroForm invoice template has a fixed number of line-item rows. When an order has more items than that, the fix isn't a bigger template, it's a continuation page filled and merged in. - [The 4.5 MB request limit that breaks a working PDF merge on Vercel](https://pdfops.dev/blog/vercel-request-body-limit-pdf-merge) (2026-10-08): A PDF merge endpoint that works on a laptop can start returning an empty 413 the moment it moves to Vercel. The platform-level request-body cap that causes it, why local testing never catches it, and how to size and chain requests around it. - [Flattening a filled PDF form with pypdf, and the method that doesn't exist](https://pdfops.dev/blog/pypdf-flatten-form-fields) (2026-10-01): pypdf fills AcroForm fields but ships no public flatten() method, and its only private _flatten() does something else entirely: collapse page-tree inheritance, not bake form values into the page. The pdftk workaround that works locally, why it can't ship on Lambda, Vercel, or a Worker, and the one-call alternative. - [Merging a Letter invoice with an A4 attachment in pypdf, and the page size nobody normalizes](https://pdfops.dev/blog/mixed-page-sizes-pypdf-merge) (2026-09-24): pypdf's append() copies each input page's own /MediaBox into the merged output as-is, so a Letter invoice merged with an A4 attachment comes out with two page sizes. Why that's invisible in a viewer and where it bites, plus the scale-and-center fix. - [Giving an AI agent PDF tools over MCP, and why it has to inspect before it fills](https://pdfops.dev/blog/pdf-tools-for-ai-agents-over-mcp) (2026-09-17): An MCP server hands an agent pdf_inspect, pdf_fill, and pdf_merge tools directly, no client code to write. The field-name problem that makes inspecting first non-optional, and what changes when the server runs on a hosted gateway instead of next to the agent. - [Filling a checkbox with pypdf, and the on-state string it never validates](https://pdfops.dev/blog/pypdf-checkbox-not-checked) (2026-09-10): pypdf's update_page_form_field_values() sets a checkbox with zero validation, so a wrong on-state string leaves it silently unchecked. The state it actually needs, and the flag that hides the failure. - [Packing-slip PDFs from a fulfillment webhook, and the redelivery that duplicates them](https://pdfops.dev/blog/packing-slip-pdf-fulfillment-webhook) (2026-09-03): A fulfillment webhook's order-shipped event can redeliver under retry, and a naive handler fills the same packing slip twice. The atomic-claim pattern, on Vercel with Upstash Redis, that makes it idempotent. - [Filling a PDF from a Python Lambda with no dependencies to package](https://pdfops.dev/blog/fill-pdf-python-lambda-stdlib) (2026-08-27): A worked example: build the multipart request by hand with Python's standard library, so a PDF-filling Lambda needs no requests package, no layer, and nothing to test against Amazon Linux. - [Bulk-filling PDFs from a CSV, and the 429 you shouldn't retry](https://pdfops.dev/blog/bulk-fill-pdfs-from-csv) (2026-08-20): A CSV roster, a PDF template, and a script that checks your quota before starting a run it can't finish. Plus why retrying this API's 429 mid-batch is the wrong move, and what to do instead. - [Automating PDF fills, merges, and invoices in n8n without a Function node](https://pdfops.dev/blog/pdf-automation-in-n8n) (2026-08-13): A verified n8n community node (n8n-nodes-pdfops) does what most PDF workflows fake with a Code node and hand-built multipart requests: inspect, fill, merge, and generate invoice PDFs as real n8n operations, with typed binary in and out. - [Filling encrypted government PDFs: what actually works, measured](https://pdfops.dev/blog/filling-encrypted-government-pdfs) (2026-08-07): Some government blanks open fine in any viewer but crash every PDF library at load. Why an empty user password does that, and a measured matrix of what fills CA FL-100 and CRA TD1: pdftk-java natively, qpdf --decrypt for everything else. - [Merging PDFs in a Bun server: page order, quota, and the 429 you'll eventually hit](https://pdfops.dev/blog/merge-pdfs-bun-server) (2026-08-06): A Bun.serve() endpoint that merges uploaded PDFs in order via PDFops, checks quota before it burns the last request, and handles the 429 that shows up the day traffic actually arrives. - [PDFops v0.10: fill a PDF by email, make any PDF fillable](https://pdfops.dev/blog/pdfops-v0-10) (2026-08-06): Release notes for PDFops v0.10 — fill@pdfops.dev turns email into an API call, /tools/make-fillable turns flat PDFs into AcroForm templates in the browser, and every fill is now byte-deterministic with an optional flatten step. - [Snapshot-testing generated PDFs in CI with a hash instead of a diff](https://pdfops.dev/blog/snapshot-testing-pdfs-in-ci) (2026-07-30): A headless-browser PDF renderer stamps a new timestamp and reorders internal objects every run, so byte-diffing its output in CI always fails. A deterministic fill API fixes that, with a worked GitHub Actions example. - [Migrating off wkhtmltopdf on AWS Lambda — the unpatched CVE and the form-fill swap](https://pdfops.dev/blog/migrating-off-wkhtmltopdf-on-lambda) (2026-07-23): wkhtmltopdf is archived, its last release carries an unpatched CVSS 9.8 SSRF, and it still ships as a Lambda Layer binary. If what you render is form-shaped — invoice, receipt, statement — here's the fill-form swap that drops the binary entirely. - [Assembling a KYC packet on Deno Deploy — inspect, fill, and merge with the pdfops-sdk](https://pdfops.dev/blog/kyc-packet-on-deno-deploy) (2026-07-21): Discover a template's fields at request time, fill it, and merge in the applicant's ID scan — one PDF packet, chaining PDFops' three primitives with the new pdfops-sdk on Deno Deploy. - [Fill a PDF form inside a Cloudflare Worker — no Chromium, no Lambda](https://pdfops.dev/blog/fill-pdf-in-a-cloudflare-worker) (2026-06-30): Headless Chrome doesn't run on Cloudflare Workers and a PDF render layer doesn't belong on Lambda. Here's the minimal Worker that fills an AcroForm PDF with one fetch — and why the substrate is the whole point. - [The case for deterministic PDF filling — PDFops](https://pdfops.dev/blog/deterministic-pdf-filling) (2026-06-09): AI can read almost any document now. But the write step — filling an official PDF — can't be a language model if an auditor might ever look at it. Why the AI-extraction wave needs a deterministic fill step. - [Fill and merge PDFs in your browser — free, no signup](https://pdfops.dev/blog/fill-and-merge-pdfs-in-your-browser) (2026-05-27): Fill a PDF form or merge several PDFs into one, right in your browser — free, no signup, no install. The same is one HTTP call away when you automate it. - [Signed contracts from a Tally webhook on Cloudflare Workers](https://pdfops.dev/blog/signed-contracts-from-tally-on-workers) (2026-05-23): Fill a contract template from a Tally form's answers and email back a counter-signed PDF — webhook, PDFops fill-form, R2, and email on one Cloudflare Worker. - [Generating audit-trail PDFs from a Next.js Server Action — PDFops](https://pdfops.dev/blog/audit-trail-pdfs-nextjs) (2026-05-20): Chain PDFops /fill-form and /merge in one Next.js Server Action to add a who/when/where cover page to any signed document — a built-in audit trail. - [Auto-fill a W-9 from an onboarding webhook on Vercel Edge](https://pdfops.dev/blog/fill-w9-from-webhook) (2026-05-18): When a contractor finishes onboarding, auto-fill their W-9 from the answers they already gave — webhook, PDFops fill-form, and Vercel Blob on Vercel Edge. - [Monthly PDF bundles on a Cloudflare Cron Trigger — PDFops](https://pdfops.dev/blog/monthly-pdf-bundles-on-cron) (2026-05-14): Build monthly per-customer statement bundles — sections from templates, merged into one PDF, stored and emailed — on a Cloudflare Worker fired by Cron. - [Invoice PDFs from Stripe webhooks on Cloudflare Workers](https://pdfops.dev/blog/stripe-invoice-pdf-workers) (2026-05-13): When a Stripe payment succeeds, generate an invoice PDF from a template and store it — webhook handler, PDFops fill-form, and R2 on one Cloudflare Worker. - [Getting started with PDFops — PDFops](https://pdfops.dev/blog/getting-started) (2026-05-12): 30 seconds to a working PDF API call. Two curl commands for fill-form and merge, the per-IP rate limit, and what's coming next. - [Why I built PDFops — PDFops](https://pdfops.dev/blog/why-pdfops) (2026-05-11): An operator's account of why a hosted PDF API for edge runtimes exists — and why DocRaptor, PDFShift, and self-hosting don't fit modern edge-deployed stacks. ## Form compatibility registry (`/registry`) - [PDF form compatibility registry](https://pdfops.dev/registry): Real public forms (IRS W-9, W-4, 1040, 2848; OPM OF-306; AO 440; California FL-100) tested on their unmodified upstream blanks against the pdfops API, pdf-lib and pypdf. Per form: full field schema, XFA/encryption quirks, provenance sha256, per-tool verdicts, and the exact tested request. Machine-readable export at https://pdfops.dev/registry/registry.json. Re-verified when upstream revs. ## Comparisons (`/vs`) - [Compare landing](https://pdfops.dev/vs/): Index of side-by-side comparisons against DocRaptor, PDFShift, and PDFMonkey. CollectionPage with ItemList of all three; every claim cited to vendor URL. - [PDFops vs DocRaptor](https://pdfops.dev/vs/docraptor): DocRaptor alternative for template-based PDF form-fill and merge. Side-by-side table, migration story, FAQ on pricing / primitives / SLA. Honest framing: DocRaptor still fits HTML-to-PDF; PDFops fits AcroForm template-fill at ~10× lower per-document cost. - [PDFops vs PDFShift](https://pdfops.dev/vs/pdfshift): PDFShift alternative. Same shape as docraptor comparison but framed against headless-Chromium pricing + latency. - [PDFops vs PDFMonkey](https://pdfops.dev/vs/pdfmonkey): PDFMonkey alternative for fixed-layout PDFs: fill existing AcroForm PDFs, merge, and JSON invoices with no template, vs PDFMonkey's HTML+Liquid renderer. September 2026 pricing and per-document cost table (similar cost at a few thousand docs/month; PDFops has a bigger free tier but no overage), MCP servers on both sides, when to stay on PDFMonkey. ## API reference (`/docs`) - [Docs landing](https://pdfops.dev/docs/): Index of endpoints, shared contract (API-key auth, tier quotas, rate limits, response shapes), starter fillable PDF link. - [POST /api/fill-form](https://pdfops.dev/docs/fill-form): Fill AcroForm fields in a PDF you upload. Full request schema, field-type mapping (PDFTextField / PDFCheckBox / PDFDropdown / PDFRadioGroup / PDFOptionList), complete error code table, curl example, FAQ. - [POST /api/merge](https://pdfops.dev/docs/merge): Merge ≥2 PDFs into one. Multipart with repeated `pdf` fields, ordering semantics, AcroForm-not-preserved caveat, error codes, FAQ. - [POST /api/inspect](https://pdfops.dev/docs/inspect): List a PDF's AcroForm fields — names, types, options, current values — plus a paste-ready fillTemplate for /api/fill-form. The natural first call: inspect → fill → merge. - [POST /api/invoice](https://pdfops.dev/docs/invoice): Generate a complete invoice PDF from structured JSON — no template needed. Deterministic output; free interactive generator at https://pdfops.dev/tools/invoice. - [POST /api/signup](https://pdfops.dev/docs/signup): Self-serve free API key. JSON `{email}` in; the key (250 requests/month) is emailed, never returned in the response body. Re-signup rotates the key. - [GET /api/usage](https://pdfops.dev/docs/usage): Quota introspection for keyed callers. Returns tier, limit, used, remaining, period, reset date. - [POST /api/waitlist](https://pdfops.dev/docs/waitlist): Roadmap-feedback form endpoint. JSON body schema (useCase, currentSolution, expectedVolume, payRange, email, message), error codes, curl + fetch examples. ## Use cases (`/use-cases`) - [Use cases index](https://pdfops.dev/use-cases/): Three vertical-shaped scenarios where AcroForm fill + merge lands cleanly, with cross-links to companion walkthrough blog posts. - [Invoice PDFs at scale](https://pdfops.dev/use-cases/invoices): Billing-webhook → fill invoice template → store + email. SaaS billing, e-commerce, freelance, marketplaces. - [Contract PDFs from onboarding flows](https://pdfops.dev/use-cases/contracts): Onboarding-form-submit → fill NDA / MSA / W-9 / contractor agreement → optionally feed into e-signature. - [Report PDFs at scale](https://pdfops.dev/use-cases/reports): Cron-driven monthly statements, webhook-driven inspection reports, audit summaries, KPI digests. ## API endpoints - `POST /api/inspect` — multipart `pdf` file. Returns JSON `{count, fields, fillTemplate}` describing every AcroForm field (name, type, options, current value) + a paste-ready fields object for fill-form. No form = `{count: 0}`, not an error. Optional `X-API-Key` header applies your tier quota. - `POST /api/fill-form` — multipart `pdf` file + `fields` JSON. Returns the filled PDF as `application/pdf`. Field names must match the source PDF's AcroForm field names. Optional `X-API-Key` header applies your tier quota. - `POST /api/merge` — multipart with N repeated `pdf` fields. Returns the concatenated PDF as `application/pdf`. Page order preserved. Optional `X-API-Key` header applies your tier quota. - `POST /api/invoice` — JSON `{from, to, items[], invoice_number?, date?, due?, currency?, tax_rate?, notes?}`. Returns a complete, professionally laid-out invoice PDF — no template needed. DETERMINISTIC: same request → byte-identical PDF (safe to re-render idempotently from webhooks/crons). Free-tier output carries a small pdfops.dev footer; paid tiers render clean. Optional `X-API-Key`. - `POST /api/signup` — JSON `{email}`. Issues a free API key (250 requests/month) and EMAILS it (never in the response body). Re-signup rotates: old key revoked, new key issued. - `GET /api/usage` — requires `X-API-Key` header. Returns `{tier, limit, used, remaining, period, resets_at}`. - `POST /api/waitlist` — JSON with `useCase`, `currentSolution`, `expectedVolume`, `payRange`, `email`, optional `message`. Returns `{ ok: true }`. Roadmap-feedback: tell the operator what to build next / enterprise volume needs. ## Pricing tiers - **Anonymous**: 100 requests / month / IP. No auth, no signup. All endpoints. The try-before-key affordance. - **Free key**: 250 requests / month. $0, no card. Emailed via `POST /api/signup` or the form at https://pdfops.dev/pricing. - **Indie**: 4,000 requests / month. $16/mo. Stripe checkout at https://pdfops.dev/pricing. - **Pro**: 25,000 requests / month. $79/mo. Stripe checkout at https://pdfops.dev/pricing. - **Enterprise**: Custom volume + SLA. Email hello@pdfops.dev. - Quotas are hard stops (HTTP 429 with Retry-After) until the monthly UTC reset. Usage introspection: `GET /api/usage` or https://pdfops.dev/dashboard. ## Machine-readable API description - [OpenAPI 3.1 spec](https://pdfops.dev/openapi.json): full request/response schemas for every endpoint — usable for codegen, AI agents/tools, and integration nodes. - Typed client on npm: `npm install pdfops-sdk` (edge-safe: Cloudflare Workers, Vercel Edge, Deno, Bun, Node 18+, browsers). - Python client on PyPI: `pip install pdfops` (zero dependencies, Python 3.9+, typed; inspect / fill_form / merge / invoice / usage). https://pypi.org/project/pdfops/ · source https://github.com/pdfops/pdfops-python - MCP server: AI agents (Claude Code, Claude Desktop, Cursor, any MCP client) can run `npx -y pdfops-mcp` for local stdio tools — pdf_inspect, pdf_fill, pdf_merge, pdf_invoice, pdfops_usage. Tools take file paths, so PDF bytes never enter model context. Listed in the [official MCP registry](https://registry.modelcontextprotocol.io/v0/servers?search=pdfops) as `dev.pdfops/pdfops-mcp`; source at https://github.com/pdfops/pdfops-mcp. - Remote (hosted) MCP: the same five tools over Streamable HTTP at `https://pdfops.dev/api/mcp`. Connecting, tools/list and the first 100 tool calls per IP per month need no key; after that a free API key (250 req/mo, no card: https://pdfops.dev/pricing#free-key) is needed, sent as `Authorization: Bearer `, `X-API-Key`, or `?key=` for URL-only clients. Claude Code: `claude mcp add --transport http pdfops https://pdfops.dev/api/mcp --header "Authorization: Bearer $PDFOPS_API_KEY"`. Remote tools take https:// URLs or data:application/pdf;base64 URIs (no local paths) and return PDFs inline; each tool call is metered to the key (or, keyless, to your IP) like a REST call. Cursor and VS Code config: https://pdfops.dev/mcp#remote ## What's NOT this - Not a self-hosted library — the `pdfops` npm package is a thin typed client; PDF processing runs on the hosted service. - Not an anonymous testbench — per-IP and per-key rate limits enforced. - Not free-forever at volume — the free tiers are real but capped; paid tiers are live. ## Technical wedge Edge-runtime hosting (Cloudflare Workers / Vercel Edge / Deno Deploy / Bun) is roughly an order of magnitude cheaper per request than Lambda-class compute once cold starts are accounted for. The incumbent hosted PDF APIs (DocRaptor, PDFShift, PDFMonkey) priced their margins on top of the more expensive substrate they were built on years ago. PDFops runs on the newer substrate; the savings pass through to live per-document pricing ($16 for 4,000 calls ≈ $0.004/call).