Release
PDFops v0.11: ten new PDF operations, in the API and in every client
v0.10 in August was the no-code on-ramps release. v0.11 is the operations release. The API had three PDF primitives, inspect, fill and merge, plus an invoice renderer. It now has fourteen PDF endpoints, and every client we ship, the n8n node, the MCP server, the TypeScript SDK and the Python package, carries the same set at the same version of the contract.
Ten new endpoints
Each one takes the multipart shape /api/fill-form
already uses: a pdf file, a few string fields, and a
PDF back. The limits are the same too, 4 MB and 2,000 pages
per input PDF, and every call counts against the same quota as a
fill or a merge.
extract-pageskeeps the pages you list, in the order you list them, so it also reorders and duplicates.delete-pagesremoves a page list and refuses to return an empty document.rotateturns all pages, or a list of them, by 90, 180 or 270 degrees clockwise.splitcuts one PDF into several by page ranges or by a fixed chunk size. One response cannot carry several files, so it returns JSON with each part as base64, up to 100 parts.watermarkstamps diagonal text with an opacity and a position. Standard Helvetica only, so Latin-1 characters.page-numbersprints a format such asPage {n} of {total}at a position of your choice, with a starting number.unlockre-saves a PDF without its encryption, given the password. It does not crack passwords it is not given.protectencrypts with a user password, an owner password, or both, and a list of the permissions that stay allowed.metadatareturns page count, per-page sizes, title, author, dates, the encryption flag and the form field count as JSON.set-metadatawrites title, author, subject, creator and keywords.
Splitting a packet into two-page parts, from a shell:
curl -X POST https://pdfops.dev/api/split \
-F "pdf=@packet.pdf" \
-F "every=2" \
-o parts.json
fill-form resolves field names forgivingly
An exact field name still wins, every time. When no field has the
exact name you sent, the key and each field name are compared in a
normalized form: Unicode-normalized, lowercased, with only letters
and digits kept and array indices like [0] removed,
against the full name and against its last dotted segment. The
field's label, the tooltip a form author typed in Acrobat, is
compared the same way. So F1 01 fills
f1_01, and First Name fills the field
whose tooltip says that, whatever its internal name is.
Only a unique match resolves. A key that matches two fields is a 422 that lists up to five candidates, and a key that matches none lists up to three similar names. The endpoint still refuses to skip unknown keys silently, because that is how wiring bugs reach a signed document. The full rules are under Field matching.
Encrypted templates open on their own
Most government blanks are owner-locked with an empty user
password: anyone can open them, and only edits are restricted. Until
October, every one of them came back as encrypted_pdf
with a pointer to qpdf. Now fill-form, inspect,
merge and all ten new endpoints open those files
themselves. A PDF with a real user password takes it in the
password form field, and unlock is there
when you want the file re-saved without encryption for good.
The same operations in every client
The point of a small API is that each wrapper can carry all of it. As of this week, all four do:
- n8n:
n8n-nodes-pdfops0.3.0 is on npm with fifteen operations. The node is verified by n8n, and the verified listing still shows 0.2.1 while n8n's review of the new version runs. If your Community Nodes panel shows the older version, update it there. - MCP:
pdfops-mcp0.4.0 serves fifteen tools, frompdf_inspectandpdf_fillthroughpdf_split,pdf_protectandpdf_set_metadata. Onenpxline in Claude Code, Claude Desktop, Cursor or VS Code. The same tools are served over Streamable HTTP athttps://pdfops.dev/api/mcp, and connecting, listing tools and the first 100 tool calls per IP each month need no key. - TypeScript:
pdfops-sdk0.5.0 addsextractPages,deletePages,rotate,split,watermark,pageNumbers,unlock,protect,metadataandsetMetadata, with the split parts decoded to bytes for you. - Python:
pdfops0.2.0 on PyPI adds the same ten assnake_casemethods, still with zero dependencies.
from pdfops import PdfOps
client = PdfOps() # reads PDFOPS_API_KEY from the environment
result = client.split("packet.pdf", every=2)
for i, part in enumerate(result.parts):
stamped = client.watermark(part.pdf, "COPY", opacity=0.2)
open(f"part-{i + 1}.pdf", "wb").write(stamped)
print(client.metadata("packet.pdf")["page_count"])
Fixed: a long multiline value could stall a fill
A multiline text field given tens of kilobytes of text with no
line breaks could run past the edge function's time limit and come
back as a 504. The cause was in the layout step underneath: for one
unbroken paragraph, pdf-lib's line breaking does work that grows
with the cube of the word count, so a value that filled a page in a
viewer took minutes to lay out on a server. Since 2026-11-08,
fill-form wraps any line over 300 characters that is
wider than its field before the layout runs. The stored value gains
newlines at those points; shorter lines, and lines that fit, are
stored exactly as sent. The 60 KB value that timed out now
returns in about two seconds.
Also since v0.10
- Merged output has no size cap. The edge runtime streams responses, so a merge is limited by its inputs, and the 4.5 MB request limit applies to the whole multipart body you send, never to what comes back.
- Rate limiting is documented end to end. A 429 carries
Retry-After,GET /api/usagetells you where you stand, and the retry post shows what a correct loop does with both. - Three guides on fills that return 200 and are still wrong: a calculated total field that comes back blank, a name that renders as ????, and a line item that does not fit a fixed-row template.
Try it
Watermark a draft, with no signup. The keyless trial allows 100 calls per IP a month:
curl -X POST https://pdfops.dev/api/watermark \
-F "pdf=@contract.pdf" \
-F "text=DRAFT" \
-F "opacity=0.2" \
-o draft.pdf
A free key from /docs/signup raises that to 250 calls a month, no card. Every endpoint has a reference page under /docs and a schema in openapi.json. A workflow these fourteen endpoints still do not cover is the most useful thing you can tell us.
← All posts · Docs · Pricing