Merge PDF API
Several PDFs in, one PDF out, in a single call. No temporary files, no
pdftk in your container, no upload form. This page is one real merge measured against
the live service on 30 August 2026.
The case this exists for #
Merging is almost never the job by itself. It is the last step of a job that already generated documents: an invoice plus your terms plus a signed annex; a monthly statement pack; a shipment folder with the delivery note, the customs form and the photos. You have just rendered two or three PDFs from an API and now you need them to be one attachment on one email.
Doing it in-process means adding a PDF library to whatever runtime you are in — and in a
workflow tool like n8n, Make or Zapier there is no runtime to add it to. That is the gap: those
platforms can call an HTTP endpoint and cannot easily run qpdf.
One call, two input shapes #
Each entry in files is either a public URL string or an object with
base64. You can mix them in one request.
curl -X POST https://pdf.mintapis.com/v1/merge \
-H "Authorization: Bearer $PDFMINT_KEY" -H "content-type: application/json" \
-d '{
"files": [
{"base64": "JVBERi0xLjcK…"},
"https://example.com/terms-2026.pdf",
{"base64": "JVBERi0xLjcK…"}
],
"filename": "pack.pdf",
"metadata": {"title": "August pack"}
}' --output pack.pdf
The base64 shape is what makes this work as the step after a render: ask
/v1/pdf for "output": "base64" and hand the string straight to
/v1/merge without the bytes ever touching a disk. The URL shape is fetched by our
servers, so it is subject to the same rule as
URL rendering — a private or loopback address is refused,
and a link behind a login will not be reachable.
Measured #
Three PDFs produced earlier the same session — the one-page invoice from the invoice page, the one-page Markdown report from the Markdown page, and a one-page probe — merged as base64:
| Measure | Value |
|---|---|
| Round trip from Germany | 268 ms for 3 files |
| Output | 3 pages, 135,506 bytes |
| Sum of the three inputs | 153,781 bytes |
| Credits | 1, regardless of how many files |
The result is smaller than its parts because shared resources are written once rather
than three times. Read back with pdftotext, page 1 is the invoice and page 3
is the probe — input order is output order. A merge is one credit whether it joins two files
or fifty, which is the sane way round: the expensive operation is rendering, not concatenating.
Order, metadata and the file name #
- Order is exactly the array order. Nothing is sorted, nothing is deduplicated.
metadatasets the document title, author and subject on the result — otherwise a merged pack inherits nothing useful and shows up in a viewer’s tab as the file name.filenameis what the download is called, and.pdfis appended if you leave it off.outputworks as everywhere else: binary body,base64, orurlfor a temporary link.
What merging does not do: it does not renumber pages, rebuild a table of contents, or reconcile page sizes. An A4 document merged with a Letter document keeps both sizes, page by page, which is correct and occasionally surprising. If a pack needs continuous numbering, put the number in a footer at render time, before the merge.
What a bad input looks like #
The realistic failure is not a corrupt file — it is a URL that returns an HTML error page with a PDF-looking name. That is why the message says what it says. Measured live:
{"files": [{"base64": "aGVsbG8="}, {"base64": "d29ybGQ="}]}
→ HTTP 400 invalid_pdf
"Input 1 is not a readable PDF."
hint: "Each input must be a PDF. Check that the URL returns a PDF and not an HTML error page."
The failing input is named, so you do not have to bisect a fifty-file array by hand. Fewer than
two files is refused as invalid_input rather than silently returning the one file back.
Neither rejection costs a credit, and a merge that fails after the credit was taken
has it refunded — verified on the account used for this page, which ended the session with
exactly one credit spent per document produced.
Fifty files, and what to do past that #
Fifty inputs per merge is the limit, and it is stated in the error rather than discovered as a timeout. Past fifty, merge in batches and then merge the batches — the output of a merge is a perfectly good input to the next one, and each batch is one credit.
Very large packs are better assembled with "output": "url" for the intermediate
steps than by passing megabytes of base64 back and forth: base64 is a third larger than the bytes it
carries, and in a workflow tool that difference is memory in a place you cannot tune.
When not to use us #
- You are in a runtime that can do it.
pypdf,pdf-libandqpdfmerge PDFs locally, for free, without a network hop. If you are in Python or Node and the files are already local, use them. This endpoint earns its place where there is no runtime — a workflow tool — or where the parts are being rendered by an API anyway. - You need to split, rotate, stamp, redact or reorder pages. This endpoint concatenates. It does not edit.
- The documents must not leave your network. Then they must not be POSTed anywhere, including here.
- You need PDF/A output or signatures preserved through the merge. We do not emit PDF/A, and merging invalidates an existing digital signature — that is inherent to changing the file, not a limitation of this implementation.
What we cannot claim #
No outside customer has paid for PDFMint yet. The free tier is 10 documents a month. One region, one service, no SLA, no SOC 2. The 268 ms above is a single merge of three small one-page files on one day; a pack of fifty scanned documents will not look like that. We have not measured merge behaviour on encrypted or malformed PDFs beyond the rejection shown above.
Try it #
Merge the real pack — the one where one of the parts is a scan and another is landscape. Mixed page sizes and a scanned page are what separate a merge that works from a demo that works.
Get an API key Merge reference Live latency and success rate