# Merge PDFs

Use `/mergePdfs` to combine PDFs in request order. The API appends every page of one document before moving to the next.

## Endpoint

```
POST /mergePdfs
```

## Authentication

Send a valid API key or OAuth token in the `Authorization` header:

```
Authorization: Bearer YOUR_API_KEY
```

See [Authentication](https://pdf-mcp.io/docs/authentication) for details.

---

## Request body

**Content-Type:** `application/json` or `multipart/form-data`

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `pdfs_base64` | array | Conditional | List of base64-encoded PDF files |
| `pdfs_url` | array | Conditional | List of URLs to fetch PDF files from |
| `files` | file[] | Conditional | Multiple PDF file uploads (multipart only) |
| `filename` | string | No | Output filename (default: "merged.pdf") |
| `storage` | object | No | Storage options for the merged PDF (see below) |
| `return_binary` | boolean | No | Return PDF binary even when using storage (default: `false`) |

Provide at least one of `pdfs_base64`, `pdfs_url` (for JSON requests), or `files` (for multipart requests). You can combine `pdfs_base64` and `pdfs_url`; the API processes base64 PDFs first and then URL PDFs.

### Field details

**pdfs_base64** (conditional, JSON only)

An array of base64-encoded PDF files. Each string in the array should be a complete PDF file encoded in base64. The encoded strings should not include the data URI prefix.

Example array structure:
```json
{
  "pdfs_base64": [
    "JVBERi0xLjcK...",  // First PDF (base64)
    "JVBERi0xLjQK...",  // Second PDF (base64)
    "JVBERi0xLjUK..."   // Third PDF (base64)
  ]
}
```

**pdfs_url** (conditional, JSON only)

An array of URLs pointing to PDF files. Each URL is fetched server-side (follows redirects). URLs are validated against SSRF before fetching.

Example array structure:
```json
{
  "pdfs_url": [
    "https://example.com/cover.pdf",
    "https://example.com/chapter1.pdf",
    "https://example.com/appendix.pdf"
  ]
}
```

**files** (conditional, multipart only)

Multiple PDF file uploads via multipart form data. Each file should be a valid PDF document. Files are merged in the order they are received.

**filename** (optional)

The filename for the output merged PDF. Defaults to "merged.pdf". The `.pdf` extension is automatically added if not provided.

### Storage options

Control how the merged PDF is stored and returned.

| Field | Type | Required | Default | Description |
|-------|------|----------|---------|-------------|
| `storage.mode` | string | No | `memory` | Storage mode: `memory`, `default`, or `byob` |
| `storage.filename` | string | No | endpoint default | Custom filename (auto-suffixed with timestamp if duplicate) |
| `storage.expires_in` | integer | No | 3600 | Signed URL expiry in seconds (60-604800) |
| `storage.retention_days` | integer | No | 14 | Auto-delete document after N days (1-365) |

**Storage modes:**

- **memory**: Return PDF bytes directly in response (no persistence). This is the default for REST API calls.
- **default**: Store in pdf-mcp S3 bucket, return JSON with document metadata and signed URL.
- **byob**: Store in your own S3 bucket (requires [BYOB configuration](https://pdf-mcp.io/docs/storage-modes)).

---

## Request examples

### Base64 PDFs in JSON

```bash
# First, encode your PDFs to base64
PDF1_BASE64=$(base64 -i document1.pdf)
PDF2_BASE64=$(base64 -i document2.pdf)
PDF3_BASE64=$(base64 -i document3.pdf)

curl -X POST https://api.pdf-mcp.io/mergePdfs \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "pdfs_base64": ["'"$PDF1_BASE64"'", "'"$PDF2_BASE64"'", "'"$PDF3_BASE64"'"],
    "filename": "combined-document.pdf"
  }' \
  --output combined-document.pdf
```

### Merge two PDFs

```bash
curl -X POST https://api.pdf-mcp.io/mergePdfs \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "pdfs_base64": ["'"$(base64 -i cover.pdf)"'", "'"$(base64 -i content.pdf)"'"]
  }' \
  --output merged.pdf
```

### PDF URLs

```bash
curl -X POST https://api.pdf-mcp.io/mergePdfs \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "pdfs_url": [
      "https://example.com/cover.pdf",
      "https://example.com/chapter1.pdf",
      "https://example.com/appendix.pdf"
    ],
    "filename": "combined-document.pdf"
  }' \
  --output combined-document.pdf
```

### Base64 and URL inputs

```bash
curl -X POST https://api.pdf-mcp.io/mergePdfs \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "pdfs_base64": ["'"$(base64 -i cover.pdf)"'"],
    "pdfs_url": ["https://example.com/chapter1.pdf"],
    "filename": "mixed-sources.pdf"
  }' \
  --output mixed-sources.pdf
```

### Multipart file upload

```bash
curl -X POST https://api.pdf-mcp.io/mergePdfs \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -F "files=@document1.pdf" \
  -F "files=@document2.pdf" \
  -F "files=@document3.pdf" \
  --output merged.pdf
```

### File upload with a filename

```bash
curl -X POST https://api.pdf-mcp.io/mergePdfs \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -F "files=@chapter1.pdf" \
  -F "files=@chapter2.pdf" \
  -F "files=@chapter3.pdf" \
  -F "filename=complete-book.pdf" \
  --output complete-book.pdf
```

### Persistent storage

```bash
curl -X POST https://api.pdf-mcp.io/mergePdfs \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "pdfs_base64": ["'"$(base64 -i cover.pdf)"'", "'"$(base64 -i content.pdf)"'"],
    "filename": "complete-document.pdf",
    "storage": {
      "mode": "default",
      "filename": "complete-document.pdf",
      "expires_in": 86400,
      "retention_days": 30
    }
  }'
```

---

## Response

### Success (memory mode)

When `storage.mode` is `memory` or not provided, returns the merged PDF as a binary file download.

**Response headers:**

| Header | Description |
|--------|-------------|
| `Content-Type` | `application/pdf` |
| `Content-Disposition` | `attachment; filename="merged.pdf"` (or custom filename) |

### Success (with storage)

When `storage.mode` is `default` or `byob`, returns JSON with document metadata and a signed download URL:

```json
{
  "success": true,
  "document_id": "550e8400-e29b-41d4-a716-446655440000",
  "url": "https://s3.eu-central-1.amazonaws.com/...",
  "filename": "complete-document.pdf",
  "file_size_bytes": 125678,
  "page_count": 15,
  "storage_mode": "default",
  "expires_at": "2024-02-15T10:30:00Z",
  "signed_url_expires_at": "2024-01-15T12:00:00Z"
}
```

| Field | Type | Description |
|-------|------|-------------|
| `success` | boolean | Always `true` on success |
| `document_id` | string | UUID of the stored document |
| `url` | string | Presigned URL for downloading the PDF |
| `filename` | string | Document filename |
| `file_size_bytes` | integer | Size of the PDF in bytes |
| `page_count` | integer | Number of pages in the merged PDF |
| `storage_mode` | string | Storage mode used (`default` or `byob`) |
| `expires_at` | string | Auto-deletion timestamp (ISO 8601), if retention is set |
| `signed_url_expires_at` | string | Expiration time of the signed URL (ISO 8601) |

### Error

```json
{
  "error": "Failed to merge PDFs",
  "message": "Error description"
}
```

**Status codes:**

| Code | Description |
|------|-------------|
| 200 | Success - PDF merged and returned or stored |
| 400 | Bad Request - Invalid input or BYOB not configured |
| 401 | Unauthorized - Missing or invalid Authorization header |
| 402 | Payment Required - Insufficient credits |
| 403 | Forbidden - Invalid API key or OAuth token |
| 500 | Internal Server Error - PDF merge failed |
| 502 | Bad Gateway - Storage operation failed (S3 upload or URL generation) |

---

## Merge behavior

### Process

The API uses PyPDF to combine PDF documents:

1. Each input PDF is validated and parsed
2. PDFs are appended in the exact order provided
3. All pages from the first PDF are added, then all pages from the second, and so on
4. The merged document is returned as a single PDF file

### Preservation

When merging PDFs, the following are preserved:
- Page dimensions and orientations
- Text content and formatting
- Images and graphics
- Internal links within each original document

### Considerations

- Page numbering is recalculated for the merged document
- Cross-document links are not automatically updated
- Bookmarks/outlines from source PDFs may need manual handling

---

## Examples

### Combine report sections

Merge separately generated sections into a complete report:

```bash
curl -X POST https://api.pdf-mcp.io/mergePdfs \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -F "files=@cover-page.pdf" \
  -F "files=@table-of-contents.pdf" \
  -F "files=@executive-summary.pdf" \
  -F "files=@main-content.pdf" \
  -F "files=@appendices.pdf" \
  -F "filename=annual-report-2024.pdf" \
  --output annual-report-2024.pdf
```

### Assemble an invoice package

Combine invoice with supporting documents:

```bash
curl -X POST https://api.pdf-mcp.io/mergePdfs \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -F "files=@invoice.pdf" \
  -F "files=@receipt.pdf" \
  -F "files=@terms-and-conditions.pdf" \
  -F "filename=invoice-package.pdf" \
  --output invoice-package.pdf
```

### Combine scanned documents

Merge multiple scanned pages into a single document:

```bash
curl -X POST https://api.pdf-mcp.io/mergePdfs \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -F "files=@scan-page1.pdf" \
  -F "files=@scan-page2.pdf" \
  -F "files=@scan-page3.pdf" \
  -F "filename=complete-scan.pdf" \
  --output complete-scan.pdf
```

### Merge extracted pages

Combine with [Extract Pages](https://pdf-mcp.io/docs/api-extract-pages) to build custom documents:

```bash
# Step 1: Extract specific pages from source documents
# (Creates extracted-from-doc1.pdf and extracted-from-doc2.pdf)

# Step 2: Merge the extracted pages
curl -X POST https://api.pdf-mcp.io/mergePdfs \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -F "files=@extracted-from-doc1.pdf" \
  -F "files=@extracted-from-doc2.pdf" \
  -F "filename=custom-compilation.pdf" \
  --output custom-compilation.pdf
```

---

## Implementation notes

### Choose an input method

- Supply URLs in JSON for PDFs already hosted online; no download or encoding is needed
- Upload local files as multipart form data without encoding them
- Use base64 in JSON when PDFs are already in memory or come from another API call
- Combine `pdfs_base64` and `pdfs_url` to merge PDFs from several sources

### File order

PDFs are merged in the exact order provided:
- For multipart: Order of `-F "files=@..."` parameters
- For JSON: Order of items in the `pdfs_base64` array

Check the input order before sending the request because it determines the document structure.

### Limit processing

- Keep the number of files reasonable (merging 100+ files may be slow)
- For very large documents, consider merging in batches
- Use file upload when possible to avoid base64 encoding overhead

### Handle errors

- Validate each PDF before merging to identify problematic files
- If merge fails, try merging files one at a time to identify the corrupted file
- Ensure all input files are valid PDFs (not images or other formats)

### Common patterns

```bash
# Merge two documents
-F "files=@doc1.pdf" -F "files=@doc2.pdf"

# Add cover page to existing document
-F "files=@cover.pdf" -F "files=@main.pdf"

# Append appendix to document
-F "files=@main.pdf" -F "files=@appendix.pdf"

# Combine multiple chapters
-F "files=@ch1.pdf" -F "files=@ch2.pdf" -F "files=@ch3.pdf"
```

---

## Related endpoints

- [Extract pages](https://pdf-mcp.io/docs/api-extract-pages): select pages before merging
- [HTML to PDF](https://pdf-mcp.io/docs/api-html-to-pdf): generate PDFs to merge
- [Page count](https://pdf-mcp.io/docs/api-page-count): count pages before merging

## Credit usage

Each request costs **0.01 credits**, regardless of page count.
