To merge PDFs with an API on RelayPDF, POST /v1/pdf/merge with a files array of two to twenty existing PDFs. Each item is a public url or a file payload (base64 in JSON; the SDKs accept bytes). Order in the array is page order in the output. The merge pdf api job is a PDF tool: it concatenates finished files. It does not render HTML. Product page: https://relaypdf.com/pdf-tools/merge. Hub: https://relaypdf.com/pdf-tools. Endpoint list: https://relaypdf.com/docs.
A typical packet is cover letter, invoice, appendix. Generate the invoice with POST /v1/pdf, convert a Word letter with POST /v1/convert, then merge those PDFs with the appendix. The merge itself is one tools debit at the live card rate of $0.005. The render and the convert are separate jobs at their own rates.
What this endpoint is
POST /v1/pdf/merge is listed on the docs hub as “Merge 2–20 PDFs.” OpenAPI names the body MergePdfRequest and requires files. The array has minItems 2 and maxItems 20. Each FileSource has optional url (URI) and optional file (base64, optional data-URI prefix). filename, response, and callbackUrl sit on the request, not on each source. Mixing html into this body is not a documented option. Office, Markdown, and live pages are not merge inputs.
Convert Office first. The merge product page says that in one line. POST /v1/convert (LibreOffice by default) returns a PDF; you then pass that file or a 24-hour url into files. Chromium print stays on POST /v1/pdf. Screenshots stay on POST /v1/images. Images-to-PDF is POST /v1/pdf/from-images, a different tool.
| You have | Send |
|---|---|
| Two to twenty finished PDFs | POST /v1/pdf/merge files[] |
| A public HTTPS PDF | files[i].url |
| PDF bytes you already hold | files[i].file (base64 / SDK bytes) |
| A Word cover letter | POST /v1/convert first, then merge |
| Invoice HTML or a template | POST /v1/pdf first, then merge |
| One HTML string you want printed | POST /v1/pdf — not merge |
Limits and price (live, 23 Aug 2026 ET)
Count and price are published. A megabyte cap is not. Pricing lists “PDF tools /v1/pdf/* · /v1/barcodes · /v1/zip” at $0.005 per successful operation. Wallet docs repeat the tools rate and name merge in the list. Failed jobs, validation errors, 429s, and 402s do not debit. New accounts still get the $5 trial on /pricing. Trial rate limit is 20/min; funded or auto-reload is 60/min; burst is 5 / 10s (SDK READMEs).
The SEO outline mentioned 15 MB. That figure is not on https://relaypdf.com/pricing, https://relaypdf.com/docs, https://relaypdf.com/pdf-tools/merge, https://relaypdf.com/openapi.json, or the Node/Python error tables as of this fetch. Oversized HTML or files return HTTP 413 payload_too_large. Do not hard-code 15 MB in application copy until the docs publish a number. If a request is too large, shrink the JSON (prefer url over inlined base64) or split the pack.
| Constraint | Published value | Source |
|---|---|---|
| File count | 2–20 | OpenAPI MergePdfRequest; docs table; /pdf-tools/merge |
| Input kinds | url or file (base64) | FileSource; product page |
| Merge price | $0.005 per successful job | /pricing; /docs/wallet |
| Render price (if you generate first) | $0.015 | /pricing |
| LibreOffice convert (if you convert first) | $0.04 | /pricing |
| Byte cap | Not published; 413 payload_too_large | /docs/errors |
| Private / loopback / metadata URLs | url_not_allowed | /docs/errors |
| callbackUrl | https only | /docs |
| Output modes | binary | url | async | docs hub; OpenAPI ResponseMode |
| url-mode download | GET /v1/files/:id, 24 hours, no key | docs hub |
Cover + invoice + appendix
Keep generation and merge as two steps. Print the invoice (html or templateId). Convert the cover if it is still a .docx. Point the appendix at a stored public PDF or at bytes you already have. Then POST merge with three FileSource objects in reading order: cover, invoice, appendix. filename on the merge request names the packet (must be a PDF name if you set one; the public generate contract requires .pdf on POST /v1/pdf filenames — use the same habit here).
curl -X POST https://api.relaypdf.com/v1/pdf/merge
-H "Authorization: Bearer $RELAYPDF_API_KEY"
-H "Content-Type: application/json"
-d '{
"files": [
{ "url": "https://example.com/cover.pdf" },
{ "url": "https://example.com/invoice-inv-1042.pdf" },
{ "url": "https://example.com/appendix.pdf" }
],
"filename": "packet-inv-1042.pdf"
}'
--output packet-inv-1042.pdf
Default response is binary: PDF bytes plus x-relaypdf-id, x-relaypdf-size, and content-disposition. Set response to url if the next hop should fetch GET /v1/files/:id for 24 hours without a key. Set response to async for HTTP 202 and poll GET /v1/jobs/:id, or supply callbackUrl (https). Same three modes as every other generating endpoint on the hub.
Node and Python
Node (@relaypdf/sdk 0.1.x) uses client.pdf.merge({ files }). Python (relaypdf 0.1.2) uses client.pdf.merge(files=[...]). REST field names stay camelCase. file may be bytes in both SDKs; the client base64-encodes before POST. The Node docs sample merges a cover url with convert.fromPath bytes. That is the cover-letter path.
import { RelayPDF } from "@relaypdf/sdk";
const client = new RelayPDF({
apiKey: process.env.RELAYPDF_API_KEY!,
});
const cover = await client.convert.fromPath("./cover.docx", {
to: "pdf",
});
const invoice = await client.pdf.fromHtml(invoiceHtml, {
filename: "invoice-inv-1042.pdf",
options: { format: "letter", printBackground: true },
});
const pack = await client.pdf.merge({
files: [
{ file: cover.kind === "binary" ? cover.bytes : new Uint8Array() },
{ file: invoice.kind === "binary" ? invoice.bytes : new Uint8Array() },
{ url: "https://example.com/appendix.pdf" },
],
filename: "packet-inv-1042.pdf",
});
await pack.save("packet-inv-1042.pdf");
import os
from relaypdf import RelayPDF
client = RelayPDF(api_key=os.environ["RELAYPDF_API_KEY"])
from_word = client.convert.from_path("cover.docx", to="pdf")
invoice = client.pdf.from_html(invoice_html, filename="invoice.pdf")
pack = client.pdf.merge(
files=[
{"file": from_word.bytes},
{"file": invoice.bytes},
{"url": "https://example.com/appendix.pdf"},
],
filename="packet-inv-1042.pdf",
)
pack.save("packet-inv-1042.pdf")
CLI is the same operation: relaypdf merge a.pdf b.pdf --out pack.pdf. Paths or https URLs. MCP exposes merge as a tool on the stdio server. Do not ask anyone to paste a key; run npx @relaypdf/cli setup.
Errors and what is not billed
Failures are { error: { code, message } }. SDKs throw RelayPDFError. Branch on code. invalid_request covers a files array that is missing, shorter than two, longer than twenty, or otherwise invalid. url_not_allowed covers a private, loopback, or metadata host, or a non-https callbackUrl. payload_too_large is 413. processing_failed is a 502 on a tool job. payment_required is an empty wallet. rate_limited includes Retry-After. None of those debit. Only a successful merge (HTTP 200, or a completed async job) takes $0.005.
A merge of three files is one tools job, not three. Generating the invoice is $0.015. Converting the cover is $0.04. Stamping DRAFT after the merge is another $0.005. Protecting the packet is another $0.005. Zip of several packets is another $0.005. Add those only when you actually call those endpoints.
After the packet exists
Merge does not watermark, password-protect, extract, or zip. Chain documented tools on the bytes or on the 24-hour url: POST /v1/pdf/stamp, /protect, /extract, /compress, /info, /v1/zip. Sibling how-tos will cover stamp and extract; the tool pages are already live under /pdf-tools/*. Invoice layout itself belongs on POST /v1/pdf and https://relaypdf.com/use-cases/invoices — not on merge.
If you need more than twenty PDFs, the public contract does not offer a batch flag. Split into packets of at most twenty, then merge those outputs in a second pass (still two to twenty inputs per call). That is two tools jobs. There is no published page-count price; billing is per successful operation, not per page.
Ship it
Use the merge pdf api when the sources are already PDFs and you need a single file in a fixed order. Two to twenty inputs. $0.005 on success. Cover, invoice, appendix is three FileSource rows. Start at https://relaypdf.com/pdf-tools/merge. Copy the OpenAPI shape from https://relaypdf.com/openapi.json. Auth is a bearer key on https://api.relaypdf.com.