# PDF Form Filler: Fill PDF Forms from JSON & Extract Fields (`data-done-right/pdf-form-filler`) Actor

PDF form filler: fill AcroForm PDF fields from JSON in bulk (one template, many records), flatten the result, or list all PDF form fields with types, options and pages. Direct download links.

- **URL**: https://apify.com/data-done-right/pdf-form-filler.md
- **Developed by:** [Data Done Right](https://apify.com/data-done-right) (community)
- **Categories:** Automation, Developer tools
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $6.40 / 1,000 pdf filleds

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?

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 Form Filler: Fill PDF Forms from JSON and Extract PDF Form Fields

This **PDF form filler** fills AcroForm PDF fields from JSON and can list every **PDF form field** (name, type, options, current value, page) so you know what to fill. Give it a link to a fillable PDF and a list of records: you get one filled PDF per record with a signed download link, ready for the API, Make, n8n or Zapier. It supports text fields, checkboxes, radio buttons, dropdowns and multi-select lists, optional flattening, and file names built from a pattern.

> Password input is for a password you already know. This tool never guesses or cracks passwords. XFA-only (Adobe LiveCycle) forms are not supported, and the tool tells you so.

### How to find the field names of a PDF form (extract AcroForm fields)

1. Put the PDF link in **PDF form URLs**.
2. Keep **Mode** on `list_fields` and run.
3. The dataset has one row per field: `fieldName`, `fieldType` (text, checkbox, radio, dropdown, listbox, button, signature), `page`, current `value`, allowed `options`, `required`, `readOnly`, `maxLength`.

Use `fieldName` (or the shorter `shortName` if it is unique in the form) as the key when you fill. A `FIELDS` record in the key-value store lists the same names as a starter mapping.

### How to fill a PDF form from JSON

1. Set **Mode** to `fill`.
2. Add one or more objects to **Records**; each becomes one PDF. Keys are field names, values are what to write.
3. Optionally set **Output file name pattern** and **Flatten**.
4. Run. Every filled PDF appears in the dataset with a `url` that downloads without a token.

Value rules:

- **Text:** any string or number. Values longer than the field's max length are cut (and noted). Line breaks are kept only in multi-line fields.
- **Checkbox:** `true` / `false` (also yes/no, on/off, 1/0) or the checkbox's on-value.
- **Radio:** one of the field's `options` (case-insensitive); `"off"` clears it.
- **Dropdown / list:** an option value or its visible label; a list of values for multi-select lists. Editable dropdowns accept free text.
- `null` leaves the field as it is; an empty string clears it.

### How to fill many PDFs at once (bulk: one template, many records)

Put hundreds of objects in **Records** (or pick an Apify dataset in **Records from a dataset**). Name the files with **Output file name pattern**: `{template}`, `{index}` (001, 002...), `{n}`, or any record key, for example `invoice-{customer}-{index}`. Record keys that start with `_` are only for naming and are never treated as fields. If you give several templates, each is filled with every record.

### How to flatten a PDF form (make fields non-editable)

Turn on **Flatten**. The values are burned into the page and the form fields are removed, so recipients cannot edit them and every viewer shows the same result.

### Input example

```json
{
  "mode": "fill",
  "pdfUrls": ["https://www.irs.gov/pub/irs-pdf/fw9.pdf"],
  "records": [
    {"f1_01[0]": "Ada Lovelace", "f1_02[0]": "Analytical Engines Ltd", "c1_1[0]": true, "_customer": "ada"},
    {"f1_01[0]": "Grace Hopper", "_customer": "grace"}
  ],
  "fileNamePattern": "w9-{_customer}-{index}",
  "flatten": false
}
```

### Output example

```json
{
  "sourceUrl": "https://www.irs.gov/pub/irs-pdf/fw9.pdf",
  "templateName": "fw9.pdf",
  "fileName": "w9-ada-001.pdf",
  "url": "https://api.apify.com/v2/key-value-stores/.../records/00-w9-ada-001.pdf?signature=...",
  "mode": "fill",
  "recordIndex": 1,
  "status": "ok",
  "fieldsSet": 3,
  "fieldsInForm": 23,
  "pages": 6,
  "flattened": false,
  "warnings": null,
  "note": "removed the XFA layer so the AcroForm values display in every viewer"
}
```

A `list_fields` row looks like `{"fieldName": "topmostSubform[0].Page1[0].f1_01[0]", "fieldType": "text", "page": 1, "value": null, "options": null, "maxLength": null, ...}`.

Unknown field names and invalid values do not stop the run: they appear in `warnings` and the PDF is still delivered (turn on **Strict** to reject such records instead). Failed templates or records get a row with `status: "error"` and are listed in the `FAILED` record.

### Use cases

- Generate filled contracts, applications, onboarding or tax forms from a spreadsheet or CRM export.
- Pre-fill a form for each customer and send the flattened PDF.
- Discover the exact field names of a vendor's PDF form before wiring up an automation.
- Audit forms: which fields exist, which are required, which options a dropdown allows.

### How much does it cost

Pay per event: **$0.008 per filled PDF** (`pdf-filled`) and **$0.004 per PDF whose fields are listed** (`fields-listed`, however many fields it has). No start fee. Errors, PDFs without a form and skipped records are not charged. Use **Max output files** to cap spend.

### Limits

- Works with AcroForm PDFs. **XFA-only dynamic forms are not supported.** Hybrid forms (AcroForm plus XFA, like many government forms) work: the XFA layer is removed in the output so the values show everywhere.
- Flat or scanned PDFs have no fields, so you get a clear error. This tool does not draw text onto arbitrary pages.
- Appearances are generated with the form's own default font, so text in non-Latin scripts may not render in every viewer. The values are stored correctly and the output sets NeedAppearances so viewers rebuild them. Flatten only after checking your font works.
- Filling invalidates digital signatures.
- Templates up to 30 MB by default (max 100 MB); links must be public and direct.
- Encrypted PDFs need the correct password. PDFs with only owner restrictions that open without a password work as they are. Use only on files you own or may modify.

### FAQ

**How do I know which field names to use?** Run `list_fields` on the PDF. Use `fieldName`, or `shortName` when it is unique.

**Why does my checkbox not tick?** Use `true` / `false`, or the on-value shown in `options`. Wrong values appear in `warnings`.

**Does it work on XFA forms?** Not XFA-only forms. Hybrid forms work because the AcroForm layer is used and the XFA layer is dropped.

**Will the values show in Preview, Chrome and Acrobat?** Each value gets an appearance stream and NeedAppearances is set. Flatten if you want identical output everywhere.

**Can it open a password-protected PDF?** Only with the correct password. It never guesses passwords.

**Is the password logged?** No. It is a secret input and never written to logs or output.

### Related tools

- PDF Merger & Splitter
- PDF Watermark & Unlock PDF Tool
- PDF Compressor

# Actor input Schema

## `mode` (type: `string`):

list_fields = read every form field of the PDF (name, type, options, current value, page) so you know what to fill. fill = write your JSON values into the fields and return filled PDFs, one per record.

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

Direct links to fillable PDF forms (AcroForm), one per line. Example: https://www.irs.gov/pub/irs-pdf/fw9.pdf. Dropbox and Google Drive share links are converted to direct downloads. In fill mode every template is filled once per record.

## `records` (type: `array`):

Fill mode: a list of objects, one filled PDF each. Keys are field names from list_fields, values are text, numbers, true/false for checkboxes, an option for radio buttons and dropdowns, or a list for multi-select lists. Example: \[{"full_name": "Ada Lovelace", "agree": true, "plan": "pro"}].

## `recordsDatasetId` (type: `string`):

Fill mode: read records from an Apify dataset in addition to the list above. Each dataset item is one record. Up to 'Max output files' items are read.

## `fileNamePattern` (type: `string`):

Name of each filled PDF, without .pdf. Placeholders: {template} (source file name), {index} (001, 002, ...), {n} (1, 2, ...) and any record key such as {full_name}. Record keys starting with an underscore (for example \_customer) are only used for naming, not as form fields. Example: invoice-{customer}-{index}.

## `flatten` (type: `boolean`):

Burn the values into the page and remove the form fields, so the PDF can no longer be edited and looks the same everywhere. Leave off to keep the form editable.

## `strict` (type: `boolean`):

Off (default): unknown field names and invalid values are reported in 'warnings' and the PDF is still delivered. On: a record with any problem is not delivered and not charged.

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

The correct password if the PDF is encrypted. Not needed for PDFs that only restrict editing. This tool never guesses or cracks passwords. It is never logged.

## `maxFiles` (type: `integer`):

Hard cap on filled PDFs per run, which also caps your cost. Example: 100.

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

Templates larger than this are skipped with a clear error. Maximum 100. Example: 30.

## `maxConcurrency` (type: `integer`):

How many PDFs to process at the same time.

## `outputStoreName` (type: `string`):

Save the filled PDFs in a named key-value store that you keep, instead of the default store of the run. Example: my-filled-forms.

## `proxyConfiguration` (type: `object`):

Only needed if a file host blocks Apify IPs. Most PDF links work without it.

## Actor input object example

```json
{
  "mode": "list_fields",
  "pdfUrls": [
    "https://www.irs.gov/pub/irs-pdf/fw9.pdf"
  ],
  "records": [],
  "fileNamePattern": "{template}-filled-{index}",
  "flatten": false,
  "strict": false,
  "maxFiles": 100,
  "maxFileSizeMb": 30,
  "maxConcurrency": 3
}
```

# Actor output Schema

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

All result items of this run (the default dataset), as JSON, CSV, Excel and more.

## `records` (type: `string`):

Key-value store records: the run summary, notes on any problems, and any files.

# 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 = {
    "mode": "list_fields",
    "pdfUrls": [
        "https://www.irs.gov/pub/irs-pdf/fw9.pdf"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("data-done-right/pdf-form-filler").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 = {
    "mode": "list_fields",
    "pdfUrls": ["https://www.irs.gov/pub/irs-pdf/fw9.pdf"],
}

# Run the Actor and wait for it to finish
run = client.actor("data-done-right/pdf-form-filler").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 '{
  "mode": "list_fields",
  "pdfUrls": [
    "https://www.irs.gov/pub/irs-pdf/fw9.pdf"
  ]
}' |
apify call data-done-right/pdf-form-filler --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,data-done-right/pdf-form-filler"
        }
    }
}
```

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/0KalxeeSJE9BxIgII/builds/kSGYbXzoHxfPFOceT/openapi.json
