Merge PDFs

Combine multiple PDF documents into a single file.

Merge PDFs

Combine multiple PDF documents into a single file. Supports 2-100 PDFs per request, with input from base64-encoded data or URLs.

POST /v1/pdf/merge

Request Body

ParameterTypeRequiredDescription
pdfsarrayYesArray of PDF sources (2-100 items)
storageobjectNoStorage options for the merged PDF

PDF Source Object

Each item in the pdfs array must contain one of the following:

ParameterTypeDescription
datastringBase64-encoded PDF content
urlstringURL to fetch the PDF from

Storage Object

ParameterTypeDefaultDescription
enabledbooleanfalseStore the merged PDF for later retrieval
filenamestring"merged.pdf"Custom filename for storage

Code Examples

curl -X POST https://api.pdfapi.dev/v1/pdf/merge \
  -H "Authorization: Bearer sk_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "pdfs": [
      {"data": "JVBERi0xLjQKMSAwIG9iago8PAovVHlwZSAvQ2F0YWxvZwo..."},
      {"url": "https://example.com/document.pdf"},
      {"data": "JVBERi0xLjQKMiAwIG9iago8PAovVHlwZSAvUGFnZQo..."}
    ],
    "storage": {
      "enabled": true,
      "filename": "merged-report.pdf"
    }
  }' \
  --output merged.pdf
const response = await fetch('https://api.pdfapi.dev/v1/pdf/merge', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${process.env.PDFAPI_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    pdfs: [
      { data: pdf1Base64 },
      { url: 'https://example.com/document.pdf' },
      { data: pdf2Base64 }
    ],
    storage: {
      enabled: true,
      filename: 'merged-report.pdf'
    }
  }),
});

if (!response.ok) {
  const error = await response.json();
  throw new Error(error.error.message);
}

const buffer = await response.arrayBuffer();
// Save or process the merged PDF
import requests
import base64
import os

# Read local PDFs and encode as base64
with open('document1.pdf', 'rb') as f:
    pdf1_base64 = base64.b64encode(f.read()).decode('utf-8')

with open('document2.pdf', 'rb') as f:
    pdf2_base64 = base64.b64encode(f.read()).decode('utf-8')

response = requests.post(
    'https://api.pdfapi.dev/v1/pdf/merge',
    headers={
        'Authorization': f'Bearer {os.environ["PDFAPI_KEY"]}',
        'Content-Type': 'application/json',
    },
    json={
        'pdfs': [
            {'data': pdf1_base64},
            {'url': 'https://example.com/document.pdf'},
            {'data': pdf2_base64}
        ],
        'storage': {
            'enabled': True,
            'filename': 'merged-report.pdf'
        }
    }
)

if response.status_code == 200:
    with open('merged.pdf', 'wb') as f:
        f.write(response.content)
else:
    print(f"Error: {response.json()['error']['message']}")
package main

import (
    "bytes"
    "encoding/base64"
    "encoding/json"
    "io"
    "net/http"
    "os"
)

func main() {
    // Read and encode local PDF
    pdfContent, _ := os.ReadFile("document.pdf")
    pdfBase64 := base64.StdEncoding.EncodeToString(pdfContent)

    payload := map[string]interface{}{
        "pdfs": []map[string]string{
            {"data": pdfBase64},
            {"url": "https://example.com/document.pdf"},
        },
        "storage": map[string]interface{}{
            "enabled":  true,
            "filename": "merged-report.pdf",
        },
    }

    body, _ := json.Marshal(payload)
    req, _ := http.NewRequest("POST", "https://api.pdfapi.dev/v1/pdf/merge", bytes.NewBuffer(body))
    req.Header.Set("Authorization", "Bearer "+os.Getenv("PDFAPI_KEY"))
    req.Header.Set("Content-Type", "application/json")

    client := &http.Client{}
    resp, _ := client.Do(req)
    defer resp.Body.Close()

    pdf, _ := io.ReadAll(resp.Body)
    os.WriteFile("merged.pdf", pdf, 0644)
}

Response

Success (200)

Returns the merged PDF file directly:

Content-Type: application/pdf
Content-Disposition: attachment; filename="merged.pdf"

If storage is enabled, the response also includes headers:

X-File-ID: f47ac10b-58cc-4372-a567-0e02b2c3d479
X-File-URL: https://api.pdfapi.dev/v1/files/f47ac10b-58cc-4372-a567-0e02b2c3d479

Error Responses

Invalid PDF Count (400)

{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "PDFs array must contain between 2 and 100 items"
  }
}

Invalid PDF Source (400)

{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Each PDF must have either 'data' or 'url' field"
  }
}

URL Fetch Failed (400)

{
  "error": {
    "code": "URL_FETCH_ERROR",
    "message": "Failed to fetch PDF from URL: https://example.com/document.pdf"
  }
}

Invalid PDF Content (400)

{
  "error": {
    "code": "INVALID_PDF",
    "message": "Item at index 2 is not a valid PDF document"
  }
}

See Error Codes for complete reference.


Use Cases

Combining Report Sections

Merge separate report sections into a single document:

curl -X POST https://api.pdfapi.dev/v1/pdf/merge \
  -H "Authorization: Bearer $PDFAPI_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "pdfs": [
      {"url": "https://reports.example.com/cover-page.pdf"},
      {"url": "https://reports.example.com/executive-summary.pdf"},
      {"url": "https://reports.example.com/detailed-analysis.pdf"},
      {"url": "https://reports.example.com/appendix.pdf"}
    ]
  }' \
  --output complete-report.pdf

Combining User-Uploaded Documents

Merge documents uploaded by users in your application:

async function mergeUserDocuments(documents) {
  const pdfs = documents.map(doc => ({
    data: doc.base64Content
  }));

  const response = await fetch('https://api.pdfapi.dev/v1/pdf/merge', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${process.env.PDFAPI_KEY}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      pdfs,
      storage: { enabled: true, filename: 'user-documents.pdf' }
    }),
  });

  return response;
}

Limits

AspectLimit
Minimum PDFs2
Maximum PDFs100
Maximum total size100 MB
URL fetch timeout30 seconds

Notes

  • PDFs are merged in the order they appear in the pdfs array
  • All pages from each PDF are included in the merged document
  • PDF metadata from the first document is preserved
  • Password-protected PDFs are not supported
  • If any PDF is invalid or cannot be fetched, the entire operation fails