# PDF Tools: merge, split, compress and rotate PDFs (`examsens.ai/pdf-tools`) Actor

Merges PDFs you link or store into one file, splits each PDF by page ranges or every N pages, compresses PDFs with optional image recompression, or rotates the pages you pick. Opens password protected PDFs. Returns a download link for every PDF it writes.

- **URL**: https://apify.com/examsens.ai/pdf-tools.md
- **Developed by:** [Ed Rayyis](https://apify.com/examsens.ai) (community)
- **Categories:** Automation, Business
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.20 / 1,000 page processeds

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 Tools: merge, split, compress and rotate PDFs

Give it PDF files, by link or from one of your key value stores, and pick one operation: merge them into one file, split each one into parts, compress each one, or rotate pages. Every PDF it writes goes into the run's key value store, and the dataset gives you its download link with the page count and size.

### What it does

- Merge: joins every PDF into one file in your order, with a bookmark at the start of each source file. Take every page, or the same pages from each file, such as `1-2`
- Split: cuts each PDF into parts by page groups separated by semicolons, such as `1-3; 4-6; 7-`, or every N pages. A group can list single pages, such as `5, 6`. Every 1 page gives one PDF per page
- Compress: compresses the page content and stores repeated objects once. Optionally recompresses photos as JPEG at the quality you set and scales down images over a size in pixels. An image is replaced only when the new one is smaller, and if the whole file does not get smaller you get the original back
- Rotate: turns the pages you pick, or every page, by 90, 180 or 270 degrees clockwise
- Password protected PDFs: set the password and they open. The files written have no password
- Remove document properties when compressing: title, author, creator and dates

### Example

A 14 page paper of 1,016,315 bytes compressed to 907,826 bytes (10.7% smaller) with images left as they were. A 3 page photo PDF of 861,952 bytes compressed to 168,382 bytes (80.5% smaller) with image quality 60 and a 1,200 pixel limit. Merging a 1 page, a 6 page and a 14 page PDF gave one 21 page PDF. Splitting with `1-2; 5, 6; 14-` gave three parts from the 14 page paper and two from the 6 page form, since it has no page 14.

### Input

- Operation: merge (default), split, compress or rotate
- PDF URLs: direct links to PDF files. Merge keeps this order
- Files in a key value store: to use PDFs from your computer, upload them to one of your key value stores and pick that store. Every record whose key ends in `.pdf` is used, in key order
- Record keys: only use these records from that store, in this order
- Pages, for merge and rotate: such as `1-3, 5, 8-`, where `8-` runs to the last page. Empty means every page
- Split ranges, and split every N pages (default 1)
- Rotate by: 90 (default), 180 or 270 degrees
- Image quality for compress (default 0, which leaves images as they are)
- Maximum image size in pixels for compress (default 0, which keeps their size)
- Remove document properties for compress (default off)
- PDF password, tried on every PDF
- Merged file name (default `merged`)
- Maximum file size in MB (default 100)

### Output

Dataset: one item per PDF written with `pdfUrl` (download link), `pdfKey`, `operation`, `pages` and `pdfBytes`. Merge adds `sources`, the pages taken from each file. Split adds `part` and `pageNumbers`. Rotate adds `rotatedPages` and `degrees`. Compress adds `savedPercent` and `imagesRecompressed`. Split, compress and rotate add the source's `sourceUrl`, `sourcePages` and `sourceBytes`.

A PDF that cannot be read gets an item with `ok: false` and an `error`, such as `HTTP 404`, "the link returns a web page, not a PDF file", "the file is not a PDF this actor can read", or "the PDF is password protected; set the password input". The other files are still processed, and a merge is made from the files that work.

### Pricing

Pay per event: one event per page in the PDFs written. A merge of 21 pages is 21 events, and so is a split that writes 21 pages in total. Failed files are not charged. The run stops before a file whose pages would pass the maximum charge you set.

### Scope and conduct

Only the files you supply are downloaded. The actor does not log in anywhere and uses no proxies. The password you give is used only to open your PDFs. Process documents you have the right to use.

### Support

Questions and problems: the Issues tab on this page. Replies are written by an AI assistant on behalf of the developer and reviewed by the developer.

# Actor input Schema

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

Merge: join every PDF into one file in your order. Split: cut each PDF into parts. Compress: make each PDF smaller. Rotate: turn the pages you pick in each PDF.

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

Direct links to PDF files. Merge keeps this order.

## `inputStore` (type: `string`):

To use PDFs from your computer, upload them to one of your key value stores, then pick that store here. Every record whose key ends in .pdf is processed in key order, or only the keys you list below in your order.

## `inputKeys` (type: `array`):

Only process these records from the store above, in this order, one key per line.

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

Merge: the pages taken from every PDF. Rotate: the pages to turn. Forms such as 1-3, 5, 8- (8- runs to the last page). Empty means every page.

## `splitRanges` (type: `string`):

One part per group, groups split by semicolons: 1-3; 4-6; 7- gives three PDFs. A group can list pages, such as 1, 3, 5. Empty splits every N pages, set below.

## `splitEvery` (type: `integer`):

Used when Split ranges is empty. 1 gives one PDF per page.

## `rotateDegrees` (type: `string`):

Clockwise turn for the pages you pick.

## `imageQuality` (type: `integer`):

Recompress photos in the PDF as JPEG at this quality, from 1 to 100. An image is replaced only when the new one is smaller. 0 leaves images as they are, so only the page content and structure are compressed.

## `imageMaxPixels` (type: `integer`):

Scale down images whose longest side is over this, such as 1600. 0 keeps their size.

## `stripMetadata` (type: `boolean`):

Remove the title, author, creator and dates from the PDF properties.

## `password` (type: `string`):

Password to open protected PDFs. The same password is tried on every PDF. The files written are not password protected.

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

Name of the merged PDF.

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

Larger PDFs are skipped with an error item and are not charged.

## Actor input object example

```json
{
  "operation": "merge",
  "pdfUrls": [
    {
      "url": "https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf"
    },
    {
      "url": "https://www.irs.gov/pub/irs-pdf/fw9.pdf"
    }
  ],
  "splitEvery": 1,
  "rotateDegrees": "90",
  "imageQuality": 0,
  "imageMaxPixels": 0,
  "stripMetadata": false,
  "outputName": "merged",
  "maxFileSizeMb": 100
}
```

# Actor output Schema

## `results` (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 = {
    "pdfUrls": [
        {
            "url": "https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf"
        },
        {
            "url": "https://www.irs.gov/pub/irs-pdf/fw9.pdf"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("examsens.ai/pdf-tools").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 = { "pdfUrls": [
        { "url": "https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf" },
        { "url": "https://www.irs.gov/pub/irs-pdf/fw9.pdf" },
    ] }

# Run the Actor and wait for it to finish
run = client.actor("examsens.ai/pdf-tools").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 '{
  "pdfUrls": [
    {
      "url": "https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf"
    },
    {
      "url": "https://www.irs.gov/pub/irs-pdf/fw9.pdf"
    }
  ]
}' |
apify call examsens.ai/pdf-tools --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,examsens.ai/pdf-tools"
        }
    }
}
```

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/jdlLzXh5vcmeicLF2/builds/a0U4UTH98SxMW1O7d/openapi.json
