# PDF Toolkit API – Merge, Split, Compress, PDF to Image (`fast_data_asap/pdf-toolkit-merge-split-compress`) Actor

Merge, split, compress, rotate and extract pages from PDFs by URL, convert PDF to PNG/JPG images or read PDF info (pages, metadata, encryption, text layer). Download links for every file. No API key, no uploads to third parties. Pay per output file.

- **URL**: https://apify.com/fast_data_asap/pdf-toolkit-merge-split-compress.md
- **Developed by:** [Fast Data](https://apify.com/fast_data_asap) (community)
- **Categories:** Developer tools, Automation
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$3.00 / 1,000 output files

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## PDF Toolkit API – Merge, Split, Compress, Rotate, Extract Pages, PDF to Images

Do the everyday PDF jobs in one API call: **merge PDFs**, **split a PDF**, **extract pages**, **compress / shrink a PDF**, **rotate pages**, **convert PDF to PNG or JPG**, or **read PDF info** (page count, size, encryption, metadata, text layer). Give it PDF URLs; you get one dataset row per result file, each with a direct download link.

- No API key, no account at a third-party PDF service, no file uploads to "free PDF" websites.
- Runs on CPU in an isolated Apify container with PyMuPDF. Your files stay in your own Apify storage.
- Pay only for files that were actually produced: **$0.003 per output file**. 404s, broken, too-large and password-protected PDFs are free and reported with a status.

### Who it's for

- **n8n / Make / Zapier automations** that receive PDFs from e-mail, forms, CRMs or cloud drives.
- **Back-office teams** merging invoices, splitting scanned batches, shrinking attachments.
- **Developers and AI agents** that need a simple PDF API without running Ghostscript, Poppler or a server.

### Operations

| operation | What it does | Output |
|---|---|---|
| `merge` | Combines all `pdfUrls` into one PDF, in the given order | 1 PDF |
| `split` | One PDF every `everyNPages` pages, or one PDF per range in `ranges` (`"1-3,4-6,7-"`) | 1 PDF per part |
| `extract` | Keeps only `pages` (`"1-3,5,last"`) in a new PDF | 1 PDF per source |
| `compress` | Downsamples and recompresses images, subsets fonts, removes unused/duplicate objects, thumbnails and XMP, packs object streams. `compressionLevel`: `low` (smallest), `medium`, `high` (print-friendly). If the result would be bigger, the original is kept. | 1 PDF per source + `savedPercent` |
| `rotate` | Rotates `pages` (empty = all) by `degrees` (90/180/270, -90) | 1 PDF per source |
| `toImages` | Renders pages to PNG or JPG at `dpi` (36–400), up to `maxPages` per PDF, optional `pages` filter | 1 image per page |
| `info` | Pages, PDF version, size, encrypted?, password required?, text layer?, likely scanned?, page size (A4/Letter...), form fields, bookmarks, title/author/producer/dates | 1 row per PDF, no file |

### Why this actor

- **One actor for all PDF basics** instead of one actor (and one price) per operation.
- **Cheap and predictable**: $0.003 per file, about 3x cheaper than comparable PDF actors charging $0.01 per merge/compress.
- **Robust**: a bad URL or encrypted PDF never breaks the run; in a merge, the other PDFs are still merged and the bad one gets an error row.
- **Flat, AI-friendly output**: same fields for every operation, ready for spreadsheets, n8n and LLM tools.
- **Size cap** (`maxFileSizeMb`, default 100 MB) protects you from surprise downloads.

### Input example

```json
{
  "operation": "merge",
  "pdfUrls": [
    "https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf",
    "https://raw.githubusercontent.com/mozilla/pdf.js/master/examples/learning/helloworld.pdf"
  ],
  "outputName": "invoices-october"
}
```

More examples:

```json
{ "operation": "split", "ranges": "1-3,4-", "outputName": "paper", "pdfUrls": ["https://example.com/report.pdf"] }
{ "operation": "compress", "compressionLevel": "medium", "pdfUrls": ["https://example.com/brochure.pdf"] }
{ "operation": "toImages", "dpi": 150, "imageFormat": "jpg", "maxPages": 10, "pdfUrls": ["https://example.com/deck.pdf"] }
{ "operation": "rotate", "degrees": 90, "pages": "2,4", "pdfUrls": ["https://example.com/scan.pdf"] }
```

### Output example

Real rows from test runs:

```json
{
  "operation": "merge",
  "status": "ok",
  "fileName": "merged.pdf",
  "url": "https://api.apify.com/v2/key-value-stores/LRlb1h92peZhGvvRh/records/merged.pdf?signature=...",
  "pages": 2,
  "sizeBytes": 12459,
  "originalSizeBytes": 13942,
  "inputFiles": 2,
  "sourceUrls": ["https://www.w3.org/.../dummy.pdf", "https://raw.githubusercontent.com/.../helloworld.pdf"]
}
```

```json
{
  "operation": "compress",
  "status": "ok",
  "fileName": "tracemonkey-compressed.pdf",
  "url": "https://api.apify.com/v2/key-value-stores/fsFGXB74KKBsaO2NG/records/tracemonkey-compressed.pdf?signature=...",
  "pages": 14,
  "sizeBytes": 815272,
  "originalSizeBytes": 1016315,
  "savedPercent": 19.8,
  "qualityLevel": "low"
}
```

```json
{
  "operation": "info",
  "status": "ok",
  "fileName": "dummy.pdf",
  "sizeBytes": 13264,
  "pages": 1,
  "pdfVersion": "1.4",
  "encrypted": false,
  "passwordRequired": false,
  "hasTextLayer": true,
  "likelyScanned": false,
  "pageSize": "A4",
  "author": "Evangelos Vlachogiannis",
  "producer": "OpenOffice.org 2.1",
  "creationDate": "2007-02-23T17:56:37"
}
```

Failed items look like `{"operation": "info", "status": "not_found", "error": "HTTP 404: the file does not exist at this URL.", "sourceUrls": [...]}`. Possible statuses: `ok`, `not_found`, `access_denied`, `not_a_pdf`, `too_large`, `encrypted`, `invalid_pdf`, `page_out_of_range`, `invalid_input`, `download_failed`.

Files are stored in the run's default key-value store; the `url` field is a ready-to-use download link. Text-heavy PDFs (like the test paper above) shrink less than PDFs with large photos or scans, where 60–90 % savings are typical.

### Use cases (n8n, Make, Zapier)

- **Merge invoices**: collect the month's invoice PDFs from Gmail / Drive and merge them into one file for your accountant.
- **Split scans**: a scanner produces one long PDF; split it every 2 pages or by ranges and file each document in the right folder.
- **Shrink attachments before e-mail**: compress PDFs over 10 MB so they pass mail-server limits.
- **Extract the signature page** of a contract or the first page of each report.
- **Fix sideways scans** with `rotate`.
- **Thumbnails and previews**: `toImages` with `maxPages: 1` for a cover image; 300 DPI PNGs as input for OCR or vision LLMs.
- **Triage incoming PDFs** with `info`: is it encrypted, scanned (no text layer), how many pages?

### Pricing

Pay per event: **$0.003 per output file** (each merged/split/extracted/compressed/rotated PDF, each page image, or each PDF analysed with `info`). Failed, missing, too-large and encrypted PDFs are not charged. Example: merging 20 invoices = $0.003; converting a 10-page PDF to images = $0.03. Apify platform usage is billed by Apify as usual and is very low (a few seconds of CPU for typical files).

### API usage

```bash
curl -X POST "https://api.apify.com/v2/acts/fast_data_asap~pdf-toolkit-merge-split-compress/run-sync-get-dataset-items?token=YOUR_APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"operation":"compress","compressionLevel":"medium","pdfUrls":["https://example.com/file.pdf"]}'
```

The response is the list of result rows; download each file from its `url`.

**n8n / Make**: use the Apify node/app, action "Run Actor and get dataset items" with actor `fast_data_asap/pdf-toolkit-merge-split-compress`, pass the input JSON, then use an HTTP Request module on each row's `url` to fetch the file.

**MCP (Claude, Cursor, AI agents)**: add `https://mcp.apify.com?tools=fast_data_asap/pdf-toolkit-merge-split-compress` as an MCP server so your agent can merge, split or compress PDFs on demand.

### Limits and notes

- Input must be a direct link that returns the PDF (not an HTML viewer page). Private links need to be signed/shared download links.
- Password-protected PDFs (user password) cannot be opened and return `status: encrypted` for free. PDFs with only an owner password (printing/copy restrictions) are processed.
- Default file-size cap is 100 MB per PDF (`maxFileSizeMb`). For very large PDFs or many 300-DPI images, increase run memory.
- Compression works best on image-heavy PDFs; vector/text-only PDFs are already compact.

# Actor input Schema

## `operation` (type: `string`):

What to do with the PDFs. merge = combine all PDFs into one file (in the given order). split = one file per N pages or per range. extract = one new PDF with the selected pages. compress = make PDFs smaller. rotate = turn pages. toImages = one PNG/JPG per page. info = page count, size, encryption, metadata and text layer check (no file output).

## `pdfUrls` (type: `array`):

Direct links to PDF files (public URLs, signed S3/Drive download links, Apify key-value store records...). For merge, the order here is the page order of the result.

## `outputName` (type: `string`):

Optional base name of the result file(s), without .pdf. Default: 'merged' for merge, otherwise the source file name.

## `everyNPages` (type: `integer`):

split: create one file every N pages (1 = one PDF per page). Ignored when 'Split: ranges' is set.

## `ranges` (type: `string`):

split: comma-separated page ranges, one output file per range, e.g. "1-3,4-6,7-" (7- = page 7 to the end, 'last' = last page).

## `pages` (type: `string`):

extract: pages to keep in one new PDF, e.g. "1-3,5,last". rotate: pages to rotate (empty = all pages). toImages: pages to render (empty = all, up to Max pages).

## `degrees` (type: `integer`):

rotate: clockwise rotation, 90, 180 or 270 (-90 = counter-clockwise).

## `compressionLevel` (type: `string`):

compress: low = smallest file (images downsampled to 72 dpi, JPEG quality 45), medium = balanced for e-mail (110 dpi, quality 65), high = light compression, print-friendly (150 dpi, quality 80). All levels also remove unused objects, duplicate resources, thumbnails and XMP metadata, subset fonts and pack objects into compressed object streams.

## `dpi` (type: `integer`):

toImages: resolution. 72 = screen thumbnail, 150 = sharp preview, 300 = print / OCR.

## `imageFormat` (type: `string`):

toImages: PNG (lossless, best for text) or JPG (smaller, best for photos/scans).

## `maxPages` (type: `integer`):

toImages: maximum number of pages rendered per PDF (each image is one billed file).

## `maxFileSizeMb` (type: `integer`):

Larger PDFs are skipped with status too_large (not charged). Raise memory for very large files.

## Actor input object example

```json
{
  "operation": "merge",
  "pdfUrls": [
    "https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf",
    "https://raw.githubusercontent.com/mozilla/pdf.js/master/examples/learning/helloworld.pdf"
  ],
  "everyNPages": 1,
  "degrees": 90,
  "compressionLevel": "medium",
  "dpi": 150,
  "imageFormat": "png",
  "maxPages": 50,
  "maxFileSizeMb": 100
}
```

# Actor output Schema

## `results` (type: `string`):

No description

## `files` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "operation": "merge",
    "pdfUrls": [
        "https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf",
        "https://raw.githubusercontent.com/mozilla/pdf.js/master/examples/learning/helloworld.pdf"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("fast_data_asap/pdf-toolkit-merge-split-compress").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "operation": "merge",
    "pdfUrls": [
        "https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf",
        "https://raw.githubusercontent.com/mozilla/pdf.js/master/examples/learning/helloworld.pdf",
    ],
}

# Run the Actor and wait for it to finish
run = client.actor("fast_data_asap/pdf-toolkit-merge-split-compress").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "operation": "merge",
  "pdfUrls": [
    "https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf",
    "https://raw.githubusercontent.com/mozilla/pdf.js/master/examples/learning/helloworld.pdf"
  ]
}' |
apify call fast_data_asap/pdf-toolkit-merge-split-compress --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,fast_data_asap/pdf-toolkit-merge-split-compress"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/Vo3v9TOkNhtFL589W/builds/KwTLjTHgjmkDnRaV8/openapi.json
