filealloy

How to merge PDF files via API in Python and Node.js

A merge job should preserve a deliberate file order and return one PDF that your application can store, stream, or attach to an email. FileAlloy does that through one multipart request, without adding a PDF engine to your backend.

How the merge endpoint works

Send two or more PDFs to POST /v1/merge. Every upload uses the same multipart field name, file. The API combines the documents in the order they appear in the request and returns the result as application/pdf.

Order is the part worth making explicit. If a cover page must come first and an appendix must come last, build the input list in that sequence rather than relying on a directory listing.

Get an API key

Create a key at app.filealloy.com and send it in the X-API-Key header. Keep the value in an environment variable or secret manager rather than committing it to source control.

Merge PDFs with curl

This request combines three files. Repeating -F "file=@..." is required because the endpoint expects an array under the singular field name file.

curl -X POST https://api.filealloy.com/v1/merge \
  -H "X-API-Key: ${FILEALLOY_API_KEY}" \
  -F "[email protected]" \
  -F "[email protected]" \
  -F "[email protected]" \
  -o merged.pdf

curl writes the binary response to merged.pdf. Remove -o only if another process is prepared to consume raw PDF bytes from standard output.

Merge PDFs in Python

The requests library accepts a list of multipart tuples, which lets the same field name appear more than once. ExitStack keeps every input open for the request and closes them together afterward.

from contextlib import ExitStack
from pathlib import Path
import requests

inputs = [Path("cover.pdf"), Path("report.pdf"), Path("appendix.pdf")]

with ExitStack() as stack:
    files = [
        ("file", (path.name, stack.enter_context(path.open("rb")), "application/pdf"))
        for path in inputs
    ]
    response = requests.post(
        "https://api.filealloy.com/v1/merge",
        headers={"X-API-Key": "fa_live_your_key"},
        files=files,
        timeout=60,
    )

if not response.ok:
    try:
        detail = response.json()["error"]["message"]
    except (ValueError, KeyError, TypeError):
        detail = response.text or "Unknown API error"
    raise RuntimeError(f"Merge failed ({response.status_code}): {detail}")

Path("merged.pdf").write_bytes(response.content)
print(f"Saved merged.pdf ({len(response.content)} bytes)")

Merge PDFs in Node.js

Node.js 18 and newer include fetch, FormData, and Blob. Append each file in sequence and let fetch set the multipart boundary automatically.

import { readFile, writeFile } from "node:fs/promises";
import { basename } from "node:path";

const inputs = ["cover.pdf", "report.pdf", "appendix.pdf"];
const form = new FormData();

for (const path of inputs) {
  const bytes = await readFile(path);
  form.append("file", new Blob([bytes], { type: "application/pdf" }), basename(path));
}

const response = await fetch("https://api.filealloy.com/v1/merge", {
  method: "POST",
  headers: { "X-API-Key": "fa_live_your_key" },
  body: form,
});

if (!response.ok) {
  const body = await response.json().catch(() => null);
  const message = body?.error?.message ?? response.statusText;
  throw new Error(`Merge failed (${response.status}): ${message}`);
}

const pdf = Buffer.from(await response.arrayBuffer());
await writeFile("merged.pdf", pdf);
console.log(`Saved merged.pdf (${pdf.length} bytes)`);

Preserving the right order

  • Sort user uploads by an explicit position stored with each record.
  • Keep generated cover pages and appendices in named variables, then assemble one final array.
  • Do not sort filenames lexicographically unless names are padded, because page-10.pdf can sort before page-2.pdf.
  • Reject an input list with fewer than two files before making the request.

Handle failed requests before saving

A merge request can return 400 when fewer than two files are supplied, 413 when an upload exceeds the size limit, 415 when an input is not a valid PDF, or 422 when the documents cannot be processed. A 503 means the service is at capacity; read its Retry-After header before retrying. Quota exhaustion returns 429.

Errors use a JSON envelope with error.code and error.message. The Python and Node.js examples inspect that message and only write a PDF after a successful response, avoiding the common mistake of saving an error body with a .pdf extension.

Where merging fits in a document workflow

Merging is useful after generating an invoice, collecting signed forms, or producing per-section reports. Build each component independently, put the paths in the required business order, and merge only when every component is ready. That keeps retries local and makes the final document easier to reproduce.

Make the first request

Start with two small test PDFs, verify their order in the output, then move the same request into your job worker. You can create a key and test the endpoint at app.filealloy.com.

Related

Merge PDF onlineSplit PDFOrganize PDF pages