# PDF Batch Suite (`moonweil/pdf-batch-suite`) Actor

Batch PDF automation in one Actor: merge, split, rotate, watermark, compress, page/Bates numbering, metadata sanitize, and password protect/decrypt. Pass a list of PDF URLs or files, pick one operation, get every processed file back with a download link. Self-contained, no external API.

- **URL**: https://apify.com/moonweil/pdf-batch-suite.md
- **Developed by:** [Aleksandr Jelohhin](https://apify.com/moonweil) (community)
- **Categories:**
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $15.00 / 1,000 file processeds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## PDF Batch Suite — bulk merge, split, rotate, watermark, number, sanitize & password‑protect PDFs

### What is PDF Batch Suite?

**PDF Batch Suite is a batch PDF processor in a single Apify Actor.** Give it a list of
PDF files — as `https://` URLs, key‑value store keys or base64 data URIs — pick one
operation, and get every processed PDF back with a direct download link.

It is built for the PDFs you **assemble and send**: contracts, invoices, payslips,
statements, court bundles, board packs. Nine operations:

**merge · split · rotate · watermark · compress · page / Bates numbering · sanitize ·
password‑protect · decrypt.**

Every file in the list is processed in the same run, and one bad file never breaks the batch.

- 🧰 **One Actor, nine PDF operations** — no separate tool or subscription per task
- 📦 **Built for batches** — pass 1 or 500 files, same call, one failed row instead of a crash
- 🔐 **The only PDF Actor on Apify that does encryption** — real 40‑/128‑/256‑bit password
  protection and decryption, plus one‑pass **sanitize** to strip scripts and hidden metadata
- 🔒 **Private by design** — PDFs are processed inside the Actor; nothing is sent to a
  third‑party service, and **no watermark, branding or tracking is ever added to your output**
- 🤖 **Automation‑native** — call it from the Apify API, schedule it, or wire it into
  n8n, Make, Zapier and AI agents
- 💸 **Pay per file, not per seat** — no monthly SaaS licence

The PDF engine is bundled and runs fully self‑contained inside the Actor — your files
never leave it.

> **Not a PDF‑to‑text scraper.** This Actor changes PDFs (assemble, secure, stamp) — it
> does not extract text, tables or run OCR. The Apify Store already has strong Actors for
> that; pair them with this one.

***

### What can PDF Batch Suite do?

| `operation` | What it does | Output |
|---|---|---|
| `merge` | Combine **all** input files into one PDF, in list order | one PDF |
| `split` | Break each PDF into parts — by page numbers or every N pages | one ZIP per input |
| `rotate` | Turn every page 90°, 180° or 270° (fix sideways scans) | one PDF per input |
| `watermark` | Stamp a diagonal text watermark on every page | one PDF per input |
| `compress` | Shrink file size while keeping the PDF readable | one PDF per input |
| `pageNumbers` | Stamp page numbers or **Bates numbers** on every page | one PDF per input |
| `sanitize` | Strip JavaScript, embedded files and hidden metadata | one PDF per input |
| `protect` | Add a password / 40‑, 128‑ or 256‑bit encryption | one PDF per input |
| `decrypt` | Remove a **known** password | one PDF per input |

#### Typical use cases

- **Merge** a month of invoices or receipts into one PDF for accounting
- **Bates‑number** a batch of documents for discovery / legal disclosure (`ABC‑000001…`)
- Add **page numbers** to a stack of reports before they go to print or a board
- **Sanitize** contracts and offer letters — remove author names, editing history,
  embedded files and scripts before they leave the building
- Stamp `CONFIDENTIAL` or `DRAFT` on a batch of contracts before sending
- **Compress** scanned documents so they fit an upload limit or email
- **Rotate** a batch of sideways or upside‑down scans back to portrait
- **Password‑protect** payslips, statements or reports before delivery
- **Split** a long report into per‑chapter files
- **Decrypt** a batch of locked files you own and know the password to

> **Coming soon:** `redact`, `flatten form`, `convert to Word / Excel`, `HTML to PDF`,
> `image / logo watermark`. OCR and text extraction stay out of scope by design.

***

### Why use this instead of iLovePDF, Smallpdf or a PDF SaaS?

| | Consumer PDF tools | This Actor |
|---|---|---|
| Batch (array of files in one call) | ✋ mostly one file at a time | ✅ built for it |
| Bates numbering / batch page numbers | ⚠️ rare, desktop‑only | ✅ built in |
| Metadata / script sanitizing | ⚠️ hidden in menus | ✅ one operation |
| Real password encryption in the same tool | ⚠️ separate feature / paywall | ✅ `protect` + `decrypt` |
| Fits an automation (n8n / Make / agents) | ⚠️ external API, separate billing | ✅ native Apify Actor |
| Forced watermark / branding on output | ⚠️ common on free tiers | ❌ never |
| Files shared with a third party | ⚠️ yes | ❌ processed in‑Actor |
| Pricing | monthly seat subscription | pay per file processed |

If your workflow already runs on Apify, you no longer have to bolt on a PDF SaaS just to
merge invoices, Bates‑number a bundle or watermark a batch of contracts.

***

### Input

| Field | Type | Required | Notes |
|---|---|---|---|
| `operation` | enum | ✅ | one of the nine above |
| `files` | string\[] | ✅ | each item: an `https://` URL, a `data:application/pdf;base64,…` URI, a bare key in this run's own default key‑value store, or `kvs://STORE_ID/KEY` for a store this run can access (use an `https://` URL for files that live elsewhere) |
| `operationParams` | object | – | per‑operation options, see below (the same params are applied to every file) |
| `outputFormat` | enum | – | `auto` (default) · `pdf` · `zip` |
| `maxFileSizeMb` | integer | – | default `100`; larger inputs fail gracefully and are not charged |
| `perFileTimeoutSecs` | integer | – | default `120` |

#### `operationParams` by operation

```jsonc
// merge
{ "sortType": "orderProvided", "removeCertSign": true }
//   sortType: orderProvided | byFileName | byDateModified | byDateCreated | byPDFTitle

// split — by explicit cut points
{ "mode": "pages", "pageNumbers": "1,5,10-12" }
// split — every N pages
{ "mode": "everyN", "everyNPages": 25 }

// rotate  (angle is required)
{ "angle": 90 }          // 90 | 180 | 270  (negative = counter-clockwise)

// watermark  (text is required)
{ "text": "CONFIDENTIAL", "fontSize": 30, "rotation": 45, "opacity": 0.5,
  "widthSpacer": 50, "heightSpacer": 50, "customColor": "#d3d3d3" }

// compress  (optimizeLevel 1–9; expectedOutputSize optional, e.g. "2MB")
{ "optimizeLevel": 6, "grayscale": false }

// pageNumbers
{ "customText": "{n}", "position": 8, "startingNumber": 1, "zeroPad": 0,
  "pagesToNumber": "all", "fontSize": 12, "fontType": "helvetica",
  "fontColor": "#000000", "customMargin": "medium" }
//   customText tokens: {n} current page · {total} page count · {filename}
//   Bates numbering:  { "customText": "ABC-{n}", "zeroPad": 6 }  ->  ABC-000001
//   position 1-9 = top-left, top-center, top-right … bottom-right

// sanitize  (every flag defaults as shown)
{ "removeJavaScript": true, "removeEmbeddedFiles": true, "removeXMPMetadata": true,
  "removeMetadata": true, "removeLinks": false, "removeFonts": false }

// protect  (password is required)
{ "password": "S3cret", "ownerPassword": "", "keyLength": 256,
  "preventPrinting": false, "preventModify": false, "preventExtractContent": false }

// decrypt  (password is required)
{ "password": "S3cret" }
```

If a required parameter is missing (for example `watermark` without `text`, or `rotate`
without `angle`), the run **fails immediately with a clear message and nothing is charged**.

***

### Output

#### Dataset — one record per output file

```json
{
  "operation": "pageNumbers",
  "inputFile": "https://example.com/contract-a.pdf",
  "inputFiles": ["https://example.com/contract-a.pdf"],
  "outputFileName": "OUTPUT-1-pageNumbers-contract-a.pdf",
  "outputKey": "OUTPUT-1-pageNumbers-contract-a.pdf",
  "outputFileUrl": "https://api.apify.com/v2/key-value-stores/<id>/records/OUTPUT-1-pageNumbers-contract-a.pdf",
  "pageCount": 12,
  "sizeBytes": 348201,
  "status": "success",
  "errorMessage": null
}
```

- `status` is `success`, `failed`, or `skipped` (your run cost limit was reached mid‑run).
- Failed rows carry a human‑readable `errorMessage` and are **never charged**.
- `merge` produces exactly one record; every other operation produces one per input file.

#### Key‑value store

Each processed file is stored under its `outputKey`, and `outputFileUrl` is a direct
download link an automation or agent can fetch without re‑running the Actor.

***

### Examples

#### Merge a batch of invoices into one PDF

```json
{
  "operation": "merge",
  "files": [
    "https://files.example.com/invoice-jan.pdf",
    "https://files.example.com/invoice-feb.pdf",
    "https://files.example.com/invoice-mar.pdf"
  ]
}
```

#### Bates‑number a disclosure bundle

```json
{
  "operation": "pageNumbers",
  "files": [
    "https://files.example.com/bundle-1.pdf",
    "https://files.example.com/bundle-2.pdf"
  ],
  "operationParams": { "customText": "SMITH-{n}", "zeroPad": 6, "position": 9 }
}
```

#### Sanitize contracts before sending them out

```json
{
  "operation": "sanitize",
  "files": [
    "https://files.example.com/offer-letter.pdf",
    "https://files.example.com/msa-draft.pdf"
  ]
}
```

#### Rotate a batch of sideways scans

```json
{
  "operation": "rotate",
  "files": ["https://files.example.com/scan-1.pdf", "https://files.example.com/scan-2.pdf"],
  "operationParams": { "angle": 270 }
}
```

#### Split a report into 20‑page chunks

```json
{
  "operation": "split",
  "files": ["https://files.example.com/annual-report.pdf"],
  "operationParams": { "mode": "everyN", "everyNPages": 20 }
}
```

#### Watermark a batch of contracts

```json
{
  "operation": "watermark",
  "files": [
    "https://files.example.com/contract-1.pdf",
    "https://files.example.com/contract-2.pdf"
  ],
  "operationParams": { "text": "CONFIDENTIAL", "opacity": 0.35, "rotation": 45 }
}
```

#### Compress scanned PDFs

```json
{
  "operation": "compress",
  "files": ["https://files.example.com/scan-1.pdf", "https://files.example.com/scan-2.pdf"],
  "operationParams": { "optimizeLevel": 7 }
}
```

#### Password‑protect payslips

```json
{
  "operation": "protect",
  "files": ["https://files.example.com/payslip-0423.pdf"],
  "operationParams": { "password": "employee-dob", "keyLength": 256 }
}
```

#### Remove a known password

```json
{
  "operation": "decrypt",
  "files": ["https://files.example.com/locked.pdf"],
  "operationParams": { "password": "employee-dob" }
}
```

From the CLI, wrap any of these as `apify call moonweil/pdf-batch-suite --input '{ … }'`.

***

### Using PDF Batch Suite from n8n, Make, Zapier or an AI agent

1. Add the **Apify** node / connector and choose **Run Actor** → `moonweil/pdf-batch-suite`.
2. Map your file URLs into `files` and set `operation` + `operationParams`.
3. In the next step, read `outputFileUrl` from the dataset — then download it, email it,
   or upload it to Drive / S3 / Dropbox.

Because every processed file also lands in the key‑value store, agents can fetch results
by `outputKey` at any time without re‑running the Actor. The Actor also works as an
[MCP](https://docs.apify.com/platform/actors/development/actor-definition/actor-json)
tool for AI agents via the Apify MCP server.

***

### Pricing

PDF Batch Suite uses **pay‑per‑event** pricing — you pay for what you process, with no
monthly fee:

| Event | Price | Charged |
|---|---|---|
| Actor start | $0.02 | once per run |
| File processed | $0.019 | once per **successful** output file |

- Corrupt, unreachable, oversized or wrong‑password files cost **$0.00** — they still get
  a dataset row so you know what happened.
- `merge` of 20 files counts as **one** "file processed" event. `split` counts as one
  event per input (the ZIP).
- Example: Bates‑numbering a 50‑file batch ≈ `$0.02 + 50 × $0.019 = $0.97`.

| What you run | Successful outputs | You pay |
|---|---|---|
| Merge 30 invoices → 1 PDF | 1 | $0.039 |
| Split 1 report | 1 | $0.039 |
| Sanitize a 10‑file batch | 10 | $0.21 |
| Compress a 100‑file batch | 100 | $1.92 |
| 50‑file batch, 5 files corrupt | 45 | $0.875 (5 failures free) |

***

### Limits & behaviour

- **Batch isolation** — one bad file never aborts the run; you get 49 good outputs and
  1 failed row.
- Default max input size is 100 MB per file (raise it with `maxFileSizeMb`, hard ceiling
  500 MB).
- Very large PDFs (hundreds of pages) work but take longer — raise `perFileTimeoutSecs`
  and the Actor's memory if needed.
- **Cold start:** the embedded PDF engine takes ~30–45 s to boot on the first operation
  of a run. This happens once per run.
- `compress` results depend on the source — an already‑optimised PDF may not shrink, and
  the Actor returns the original rather than a larger file.
- `pageNumbers` and `rotate` are applied to the whole document (or the pages you name in
  `pagesToNumber`); `sanitize` keeps fonts and links by default so the PDF still renders.

***

### FAQ

#### Which PDF operations does this Actor support?

`merge`, `split`, `rotate`, `watermark`, `compress`, `pageNumbers` (page or Bates
numbering), `sanitize` (strip scripts + hidden metadata), `protect` (add password) and
`decrypt` (remove a known password). Redaction, form flattening and Office conversion are
on the roadmap. OCR and text extraction are intentionally out of scope.

#### Can it process many PDFs in one run?

Yes — that's the point. Pass an array of files and every one is processed with the same
settings. One corrupt or unreachable file produces a `failed` row instead of aborting the
run, and failed files are never charged.

#### Can it do Bates numbering?

Yes. Use `pageNumbers` with `customText` containing `{n}` and a `zeroPad` width, e.g.
`{ "customText": "ABC-{n}", "zeroPad": 6 }` stamps `ABC-000001`, `ABC-000002`, … Each
file in a batch starts again at `startingNumber` — run one file per call, or set
`startingNumber` per run, if you need a single continuous range across documents.

#### What does `sanitize` remove?

By default: embedded JavaScript, embedded file attachments, XMP metadata and the document
information dictionary (author, producer, creation/modification dates). Link removal and
font stripping are available but off by default. Use it before sending a document outside
your organisation.

#### Are my files sent anywhere?

No. The PDF engine runs inside the Actor's own container. Nothing is uploaded to a
third‑party service, and no watermark, branding or tracking is added to the output.

#### How much does it cost?

`$0.02` per run plus `$0.019` per successfully processed output file. A 50‑file batch is
about `$0.97`. See [Pricing](#pricing) for a full table.

#### What input formats can I use for `files`?

A public `https://` URL, a `data:application/pdf;base64,…` URI, a bare key in the run's own
default key‑value store, or `kvs://STORE_ID/KEY` for a key‑value store this run can access.
For files stored anywhere else, pass an `https://` URL.

#### Can I call it from the API, a schedule, or an automation tool?

Yes. Use the standard Apify [Run Actor](https://docs.apify.com/api/v2) API endpoint,
Apify Schedules, or the Apify connector in n8n, Make and Zapier. Read `outputFileUrl`
from the dataset in the next step.

#### Is the watermark or protection permanent?

The watermark and page numbers are drawn onto every page and stay with the PDF. `protect`
applies real PDF encryption — keep the password safe, as `decrypt` needs the correct one
to remove it.

***

### Privacy & security

The PDF engine is bundled inside the Actor and runs with no external network calls, no
login and no telemetry. Your files are read, processed and written back to your own Apify
storage — they are never uploaded to a third‑party service, and nothing (no watermark, no
branding, no tracking) is added to the output. Input files and results follow your Apify
account's [data‑retention settings](https://docs.apify.com/platform/storage) and can be
deleted at any time.

***

### Support

Found a bug, need another operation, or want a tailored version? Open an issue from the
Actor's **Issues** tab on Apify, or contact the developer through the
[developer profile](https://apify.com/moonweil). Feature requests for the roadmap
operations are welcome.

# Actor input Schema

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

Which PDF operation to run on every input file in this run.

- **merge** – combine ALL input files into one PDF (order = order of the list)
- **split** – split each input PDF into multiple PDFs (returned as one ZIP per input)
- **watermark** – stamp a text watermark onto every page of each input
- **compress** – reduce the file size of each input PDF
- **rotate** – rotate every page of each input by 90/180/270°
- **pageNumbers** – stamp page numbers or Bates numbers onto every page
- **sanitize** – strip JavaScript, embedded files and hidden metadata before sharing
- **protect** – add a password / encryption to each input PDF
- **decrypt** – remove a known password from each input PDF

## `files` (type: `array`):

PDF files to process. Each item can be:

- a public **https:// URL** to a PDF
- a **data URI** `data:application/pdf;base64,...`
- a bare **`KEY`** for a record in this run's own default key-value store
- a **key-value store reference** `kvs://STORE_ID/KEY` — only for stores this run can access (its own, or one shared with it in an Apify workflow); for files that live elsewhere use an https:// URL

For `merge`, all files are combined into a single output (in list order). For every other operation each file is processed independently and produces its own output + dataset row.

## `operationParams` (type: `object`):

Options for the selected operation. Same params apply to every file in the batch. Leave empty to use defaults.

- **merge**: `{ "sortType": "orderProvided|byFileName|byDateModified|byDateCreated|byPDFTitle", "removeCertSign": true }`
- **split**: `{ "mode": "pages", "pageNumbers": "1,3,5-10" }` or `{ "mode": "everyN", "everyNPages": 5 }`
- **watermark**: `{ "text": "CONFIDENTIAL", "fontSize": 30, "rotation": 45, "opacity": 0.5, "widthSpacer": 50, "heightSpacer": 50, "customColor": "#d3d3d3" }` (text is required)
- **compress**: `{ "optimizeLevel": 5, "expectedOutputSize": "2MB", "grayscale": false }` (optimizeLevel 1-9; expectedOutputSize optional)
- **rotate**: `{ "angle": 90 }` (angle is required: 90, 180 or 270; negative = counter-clockwise)
- **pageNumbers**: `{ "customText": "{n}", "position": 8, "startingNumber": 1, "zeroPad": 0, "pagesToNumber": "all", "fontSize": 12, "fontType": "helvetica", "fontColor": "#000000", "customMargin": "medium" }` — `customText` supports `{n}` (current page), `{total}` (page count) and `{filename}`; for Bates numbering use e.g. `{ "customText": "ABC-{n}", "zeroPad": 6 }`. `position` 1-9 = top-left … bottom-right.
- **sanitize**: `{ "removeJavaScript": true, "removeEmbeddedFiles": true, "removeXMPMetadata": true, "removeMetadata": true, "removeLinks": false, "removeFonts": false }` (all default as shown)
- **protect**: `{ "password": "secret", "ownerPassword": "", "keyLength": 256, "preventPrinting": false, "preventModify": false, "preventExtractContent": false }` (password is required)
- **decrypt**: `{ "password": "secret" }` (password is required)

## `outputFormat` (type: `string`):

Format of the produced file. `auto` = PDF for every operation except `split` (which always returns a ZIP).

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

Input files larger than this are skipped with a failed row (and never charged).

## `perFileTimeoutSecs` (type: `integer`):

Maximum time to wait for the PDF engine to process a single file / operation before marking it failed.

## Actor input object example

```json
{
  "operation": "watermark",
  "files": [
    "https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf"
  ],
  "operationParams": {
    "text": "CONFIDENTIAL",
    "rotation": 45,
    "opacity": 0.5
  },
  "outputFormat": "auto",
  "maxFileSizeMb": 100,
  "perFileTimeoutSecs": 120
}
```

# Actor output Schema

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

Dataset — one record per output file with operation, status, page count, size and a direct outputFileUrl. Failed/skipped rows carry an errorMessage and are never charged.

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

Key-value store holding every produced PDF/ZIP under its outputKey.

# 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": "watermark",
    "files": [
        "https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf"
    ],
    "operationParams": {
        "text": "CONFIDENTIAL",
        "rotation": 45,
        "opacity": 0.5
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("moonweil/pdf-batch-suite").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": "watermark",
    "files": ["https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf"],
    "operationParams": {
        "text": "CONFIDENTIAL",
        "rotation": 45,
        "opacity": 0.5,
    },
}

# Run the Actor and wait for it to finish
run = client.actor("moonweil/pdf-batch-suite").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": "watermark",
  "files": [
    "https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf"
  ],
  "operationParams": {
    "text": "CONFIDENTIAL",
    "rotation": 45,
    "opacity": 0.5
  }
}' |
apify call moonweil/pdf-batch-suite --silent --output-dataset

```

## MCP server setup

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

```

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/YSeWF6zrtzCInWiUV/builds/4clZEdQDkKpi0twEn/openapi.json
