# PDF to Image, Split, Merge, Rotate & Images to PDF (`maliksaid/pdf-toolkit`) Actor

PDF to image converter and toolkit: convert PDF pages to PNG, JPEG or WebP at any DPI, make thumbnails, split PDFs by page ranges, merge PDFs, rotate pages, and turn images into a PDF. Files from URLs or your key-value stores. Also reads PDF metadata.

- **URL**: https://apify.com/maliksaid/pdf-toolkit.md
- **Developed by:** [Malik Said](https://apify.com/maliksaid) (community)
- **Categories:** Developer tools, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 1,000 page images

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 to Image, Split, Merge, Rotate & Images to PDF

This Actor is a PDF toolkit. It converts PDF pages to PNG, JPEG or WebP images. It splits, merges and rotates PDFs. It turns images into one PDF. It also reads PDF metadata and page sizes. Files come from URLs or from your Apify key-value stores. Every output file gets a download link.

### What it does

Pick one **Operation** per run:

| Operation | Output |
|---|---|
| Pages to images | One image per page. PNG, JPEG or WebP, 36 to 300 DPI, optional maximum width for thumbnails |
| Split PDF | One PDF per page, one PDF every N pages, one PDF per page range, or one PDF with the chosen pages |
| Merge PDFs into one | One PDF with every input PDF, in input order |
| Rotate pages | One PDF per input, with the chosen pages turned 90, 180 or 270 degrees |
| Remove or reorder pages | One PDF per input with the pages you keep, in the order you set, for example `3, 1, 2, 5-`. Drop pages by number with Remove pages |
| Compress PDF | A smaller PDF from scans and photo-heavy files. JPEG images are re-encoded; text and drawings stay sharp. Three levels |
| Images to PDF | One PDF with one image per page. PNG, JPEG, WebP, GIF and BMP. Phone photos are turned upright from their EXIF data |
| PDF info and metadata | One record per PDF: page count, PDF version, title, author, dates, producer, form and tag flags, size of every page, file size and SHA-256 |

- **Pages** takes ranges such as `1-3, 5, 8-` or `last`. Leave it empty for every page.
- Files go to the run's key-value store. Each record has `outputUrl`, a signed link that works without an API token.
- The Actor runs pdf.js, pdf-lib and the Skia renderer inside the container. Your files are not sent to any outside service.

Need the text instead? Use [PDF to Text, Markdown & Tables](https://apify.com/maliksaid/pdf-to-text-markdown). It returns text, Markdown, tables and RAG chunks, with OCR for scanned pages.

### What you get

One dataset record per output file:

| Field | Meaning |
|---|---|
| `operation` | The operation of the run |
| `status` | `ok`, or `failed` with the reason in `error`. Failed inputs are free |
| `outputType` | `image`, `pdf` or `info` |
| `sourceUrl`, `sourceUrls` | The input file. Merge and images to PDF list every input |
| `sourceFileName`, `sourcePageCount` | Name and page count of the input |
| `page`, `pages` | The page an image shows, or the source pages in an output PDF. For rotate: the pages that were turned. For remove or reorder: the kept pages in output order |
| `outputKey`, `outputUrl` | Key of the file in the run's key-value store, and its download link |
| `contentType`, `fileSizeBytes`, `format` | File type and size |
| `width`, `height`, `dpi` | Image size in pixels and the resolution used |
| `outputPageCount` | Pages in an output PDF |
| `info` | PDF info record. Only for the PDF info operation |
| `originalSizeBytes`, `imagesFound`, `imagesRecompressed` | Compress only: input size, images found and images made smaller |

Record from a test run on IRS Publication 17, a US government work in the public domain. The signature in the link is shortened here:

```json
{
  "operation": "pdfToImages",
  "status": "ok",
  "outputType": "image",
  "sourceUrl": "https://www.irs.gov/pub/irs-pdf/p17.pdf",
  "sourceFileName": "p17.pdf",
  "sourcePageCount": 142,
  "page": 5,
  "outputKey": "p17-page-005.png",
  "outputUrl": "https://api.apify.com/v2/key-value-stores/261qv6vHLZs4WUs6D/records/p17-page-005.png?signature=...",
  "contentType": "image/png",
  "fileSizeBytes": 1234459,
  "format": "png",
  "width": 1275,
  "height": 1650,
  "dpi": 150
}
```

A split of the same file with **Split ranges** `1-60` and `61-70` gave two PDFs. The first record, with the page list shortened:

```json
{
  "operation": "split",
  "status": "ok",
  "outputType": "pdf",
  "sourceUrl": "https://www.irs.gov/pub/irs-pdf/p17.pdf",
  "sourcePageCount": 142,
  "pages": [1, 2, 3, "...", 60],
  "outputKey": "p17-pages-1-60.pdf",
  "contentType": "application/pdf",
  "fileSizeBytes": 807554,
  "outputPageCount": 60
}
```

A remove-and-reorder run on the same file with **Page order** `3, 1, 2, 5-10, 2` and **Remove pages** `7`:

```json
{
  "operation": "organize",
  "status": "ok",
  "outputType": "pdf",
  "sourceUrl": "https://www.irs.gov/pub/irs-pdf/p17.pdf",
  "sourcePageCount": 142,
  "pages": [3, 1, 2, 5, 6, 8, 9, 10, 2],
  "outputKey": "p17-organized.pdf",
  "contentType": "application/pdf",
  "fileSizeBytes": 240160,
  "outputPageCount": 9
}
```

### How to use it

1. Choose the **Operation**.
2. Paste the file links into **File URLs**. Each link must point straight to a file.
3. For files stored in Apify, pick the stores in **Files in your Apify key-value stores**. Add record keys, or leave the keys empty to use every PDF (or every image) in the stores, in key order.
4. Set **Pages** and the options for your operation.
5. Run the Actor. Open the files from `outputUrl`, or from the run's key-value store in Console. Download the records as JSON, CSV or Excel, or read them through the API.

### Input

| Field | Default | Meaning |
|---|---|---|
| `operation` | `pdfToImages` | `pdfToImages`, `split`, `merge`, `rotate`, `organize`, `compress`, `imagesToPdf` or `info` |
| `startUrls` | IRS Form W-9 | File links |
| `keyValueStores`, `keyValueStoreKeys` | none | Your Apify stores and record keys |
| `pages` | all pages | Page ranges for pages to images, rotate and split. For `organize`: the pages to keep when `pageOrder` is empty |
| `imageFormat` | `png` | `png`, `jpeg` or `webp` |
| `dpi` | 150 | 36 to 300. Very large pages are scaled down to 25 million pixels |
| `maxImageWidth` | 0 | Maximum image width in pixels for thumbnails. 0 keeps the DPI size |
| `imageQuality` | 85 | JPEG and WebP quality |
| `singleImage` | off | Join the chosen pages into one tall image, up to 20 pages, 30,000 pixels high and 20 million pixels. Each page still counts as one page image |
| `splitMode` | `everyPage` | `everyPage`, `everyN`, `ranges` or `selected` |
| `splitEvery`, `splitRanges` | 2, none | Pages per part, or one range per output PDF |
| `rotateDegrees` | `90` | `90`, `180` or `270`, clockwise |
| `pageOrder` | none | For `organize`: the new order, for example `3, 1, 2, 5-`. A page can repeat. `6-1` counts down. A page past the end fails that PDF |
| `removePages` | none | For `organize`: pages to drop, for example `2, 7-9, last` |
| `compressLevel` | `balanced` | For `compress`: `light`, `balanced` or `strong` |
| `watermarkText`, `watermarkOpacity` | none, 0.15 | Text drawn diagonally on every page of every output PDF. Latin text only |
| `outputFileName` | `merged.pdf` or `images.pdf` | Name of the merged PDF |
| `pageSize`, `marginPt` | `image`, 0 | Page size for images to PDF (`image`, `A4`, `Letter`) and margin in points |
| `pdfPassword` | none | Opens protected PDFs for pages to images and PDF info |
| `maxItems` | 100 | Maximum output files per run. 0 means no limit |
| `maxPagesPerPdf` | 500 | Pages used per PDF |
| `maxPagesPerRun` | 1,000 | Pages rendered, split, rotated or merged per run |
| `maxFileSizeMb` | 50 | Larger files are skipped. Hard limit 100 MB |

### Pricing

You pay per event:

| Event | Price |
|---|---|
| Actor start | $0.02 per run (per GB of memory) |
| Page image | $0.002 per image ($2 per 1,000 pages) |
| Output PDF or info record, first 50 pages included | $0.003 per file |
| Extra PDF page, above 50 pages in one output PDF | $0.0002 per page |

Failed inputs are free. Examples:

- 500 pages to PNG: $0.02 + $1.00 = $1.02.
- Split one 142-page PDF into single pages: $0.02 + 142 x $0.003 = $0.446.
- Merge 10 PDFs into one 300-page PDF: $0.02 + $0.003 + 250 x $0.0002 = $0.073.
- PDF info for 100 PDFs: $0.02 + $0.30 = $0.32.
- Compress one 40-page scan: $0.02 + $0.003 = $0.023.
- One tall image of 10 pages: $0.02 + 10 x $0.002 = $0.04 (each page counts as one page image).

The Actor stops before work it cannot charge for when your maximum cost per run is close.

### Limits and good to know

- Default memory is 1,024 MB. Peak memory is about 140 MB plus 10 MB per MB of PDF. At 1,024 MB a PDF, or a whole merge, can be up to 37 MB. At 2,048 MB it can be up to 88 MB. A larger file fails with a free error that names the memory to use.
- Rendering one page at 150 DPI took about 1.4 seconds on the platform at 1,024 MB. 300 DPI takes longer.
- Compress shrinks JPEG photos and scans only. Scans saved as JBIG2, JPEG 2000 or CCITT images, and PDFs that are mostly text, may not get smaller; then the original file is returned with the same size. On test scans at 300 DPI, Balanced made files about 74% smaller and Light about 35% smaller.
- Split, merge, rotate, remove or reorder, and compress need unencrypted PDFs. Encrypted PDFs still work for pages to images and PDF info, with the password if they have one.
- Output PDFs keep the pages, their fonts and images, and the title and author. Split and merge drop bookmarks. Form fields can stop working after a split or merge.
- Only fetch files you have the right to use. The Actor reads robots.txt before each download and skips blocked files. It refuses private network addresses. Each download is capped at 60 seconds and retried at most twice.
- Output files stay in the run's key-value store for the run retention of your plan. Save them elsewhere if you need them longer.

### FAQ

**Can I make thumbnails?** Yes. Set **Maximum image width** to 200 or 300 and pick JPEG or WebP.

**Can I turn photos from my phone into a PDF?** Yes. Use Images to PDF. JPEG photos are turned upright from their EXIF orientation. Set **Page size** to A4 or US Letter for printable pages.

**Can I delete pages or change their order?** Yes. Use Remove or reorder pages. Put the new order in **Page order**, for example `3, 1, 2, 5-`, or list pages to drop in **Remove pages**. Each input PDF gives one new PDF.

**Can I add a watermark?** Yes. Put text such as `DRAFT` or `CONFIDENTIAL` in **Watermark text**. It is drawn diagonally on every page of every output PDF. The built-in font covers Latin text only.

**Can I get all pages in one image?** Yes. Turn on **All pages in one image** with Pages to images. Up to 20 pages, 30,000 pixels high and 20 million pixels are joined, which is about 20 A4 or Letter pages at 100 DPI, or 9 at 150 DPI. Lower the DPI for more pages.

**In which order are files merged?** In the order of **File URLs**, then the store records. Records listed from a store are sorted by key, so `part-2` comes before `part-10`.

# Actor input Schema

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

What to do with the files. Pages to images renders each chosen page as PNG, JPEG or WebP. Split makes smaller PDFs. Merge joins all PDFs into one, in input order. Rotate turns pages. Remove or reorder pages makes one PDF per input with the pages you keep, in the order you set. Images to PDF makes one PDF with one image per page. Compress makes a smaller PDF by re-encoding its photos and scans. PDF info returns metadata and page sizes.

## `startUrls` (type: `array`):

Links to your PDF files, or to images for Images to PDF. Each link must point straight to a file. The Actor checks robots.txt before it downloads a file and skips files that robots.txt blocks. Private network addresses are refused.

## `keyValueStores` (type: `array`):

Pick key-value stores that hold your files. The run gets read access to these stores only. Use this for files you uploaded to Apify or saved from another Actor.

## `keyValueStoreKeys` (type: `array`):

Keys of the records to read from the chosen stores, for example report.pdf. Leave empty to use every PDF (or every image, for Images to PDF) in the stores, in key order.

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

Pages to use, for example 1-3, 5, 8-. 'last' is the last page and 'last-1' the one before it. Leave empty for every page. Used by Pages to images, Rotate, Split (every page, every N pages and chosen pages) and Remove or reorder pages (the pages to keep, in document order, when Page order is empty).

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

Format of the page images. PNG keeps text sharpest. JPEG and WebP make smaller files.

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

Dots per inch. 72 is screen size, 150 suits reading and OCR, 300 suits print. A US Letter page at 150 DPI is 1275 x 1650 pixels. Very large pages are scaled down to 25 million pixels.

## `maxImageWidth` (type: `integer`):

Scale images down to at most this width, for thumbnails or previews. For example 300. 0 keeps the size from the DPI.

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

Higher means sharper and larger files. PNG ignores this.

## `singleImage` (type: `boolean`):

Pages to images: join the chosen pages top to bottom in one tall image. Up to 20 pages, 30,000 pixels high and 20 million pixels (about 20 A4 or Letter pages at 100 DPI, or 9 at 150 DPI); above that, one image per page is saved. Each page still counts as one page image.

## `splitMode` (type: `string`):

How to split each PDF. Every page and every N pages use only the pages in Pages.

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

Number of pages in each part when Split mode is every N pages. The last part can be shorter.

## `splitRanges` (type: `array`):

One output PDF per range, when Split mode is one PDF per range. For example 1-2 and 3-last.

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

How far to turn the pages in Pages. Other pages stay as they are.

## `pageOrder` (type: `string`):

For Remove or reorder pages. The new page order, for example 3, 1, 2, 5-. Pages not listed are left out. A page can appear twice. 6-1 counts down, so 6-1 reverses a 6-page PDF. A page past the end fails that PDF with a clear error. Leave empty to keep the pages in Pages, in document order.

## `removePages` (type: `string`):

For Remove or reorder pages. Pages to drop, for example 2, 7-9, last. Applied after Page order or Pages. Leave empty to drop nothing.

## `compressLevel` (type: `string`):

Compress only. Light keeps image size at JPEG quality 80. Balanced (default) caps images at 2,000 pixels on the long side, quality 70, about 170 DPI on A4. Strong caps at 1,400 pixels, quality 55. Text and drawings are never changed.

## `watermarkText` (type: `string`):

Optional. Draws this text diagonally on every page of every output PDF (split, merge, rotate, remove or reorder, compress and images to PDF). Latin letters, digits and common symbols only, up to 100 characters.

## `watermarkOpacity` (type: `number`):

0.05 (faint) to 1 (solid). Default 0.15.

## `outputFileName` (type: `string`):

Name of the PDF that Merge or Images to PDF makes, for example contract-pack.pdf. Leave empty for merged.pdf or images.pdf.

## `pageSize` (type: `string`):

Same as the image makes each page the size of its picture at 96 pixels per inch. A4 and US Letter fit each picture on the page and turn the page to landscape for wide pictures.

## `marginPt` (type: `integer`):

White border around each image in Images to PDF. 72 points is one inch.

## `pdfPassword` (type: `string`):

Password to open protected PDFs for Pages to images and PDF info. Split, merge and rotate need unencrypted PDFs.

## `maxItems` (type: `integer`):

Stop after saving this many output files (images, PDFs or info records). The default keeps test runs short and cheap. Set 0 for no limit.

## `maxPagesPerPdf` (type: `integer`):

Use at most this many pages of each PDF. Larger PDFs are refused by Rotate and Merge. Set 0 for no limit.

## `maxPagesPerRun` (type: `integer`):

Stop the run after this many pages rendered, split, rotated or merged in total. Set 0 for no limit.

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

Skip files larger than this. The hard limit is 100 MB. Larger files need more memory.

## Actor input object example

```json
{
  "operation": "pdfToImages",
  "startUrls": [
    {
      "url": "https://www.irs.gov/pub/irs-pdf/fw9.pdf"
    }
  ],
  "keyValueStoreKeys": [],
  "pages": "",
  "imageFormat": "png",
  "dpi": 150,
  "maxImageWidth": 0,
  "imageQuality": 85,
  "singleImage": false,
  "splitMode": "everyPage",
  "splitEvery": 2,
  "splitRanges": [
    "1-2",
    "3-last"
  ],
  "rotateDegrees": "90",
  "pageOrder": "",
  "removePages": "",
  "compressLevel": "balanced",
  "watermarkText": "",
  "watermarkOpacity": 0.15,
  "outputFileName": "",
  "pageSize": "image",
  "marginPt": 0,
  "maxItems": 100,
  "maxPagesPerPdf": 500,
  "maxPagesPerRun": 1000,
  "maxFileSizeMb": 50
}
```

# 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 = {
    "startUrls": [
        {
            "url": "https://www.irs.gov/pub/irs-pdf/fw9.pdf"
        }
    ],
    "splitRanges": [
        "1-2",
        "3-last"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("maliksaid/pdf-toolkit").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 = {
    "startUrls": [{ "url": "https://www.irs.gov/pub/irs-pdf/fw9.pdf" }],
    "splitRanges": [
        "1-2",
        "3-last",
    ],
}

# Run the Actor and wait for it to finish
run = client.actor("maliksaid/pdf-toolkit").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 '{
  "startUrls": [
    {
      "url": "https://www.irs.gov/pub/irs-pdf/fw9.pdf"
    }
  ],
  "splitRanges": [
    "1-2",
    "3-last"
  ]
}' |
apify call maliksaid/pdf-toolkit --silent --output-dataset

```

## MCP server setup

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

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/USnpqxlq4F53Npajx/builds/gZPcjBcDfLXoWBHnB/openapi.json
