# PDF Form Filler (`ely_source/pdf-form-filler`) Actor

Discover the field schema of any fillable PDF as JSON, then merge data records into filled, flattened, ready-to-send PDFs in batch. Pure AcroForm processing: no OCR, no external services.

- **URL**: https://apify.com/ely\_source/pdf-form-filler.md
- **Developed by:** [Alexandre Leclerc](https://apify.com/ely_source) (community)
- **Categories:** Automation, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $30.00 / 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.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#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.

In JavaScript/TypeScript projects, use official [JavaScript/TypeScript client](https://docs.apify.com/api/client/js/docs.md):

```bash
npm install apify-client
```

In Python projects, use official [Python client library](https://docs.apify.com/api/client/python/docs.md):

```bash
pip install apify-client
```

In shell scripts, use [Apify CLI](https://docs.apify.com/cli/docs.md):

````bash
# MacOS / Linux
curl -fsSL https://apify.com/install-cli.sh | bash
# Windows
irm https://apify.com/install-cli.ps1 | iex
```bash

In AI frameworks, you might use the [Apify MCP server](https://docs.apify.com/integrations/mcp.md).

If your project is in a different language, use 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 Form Filler — describe, fill and flatten AcroForm PDFs

Turn any fillable PDF into an API. This Actor does two things, designed to be
called by automations (n8n, Make, Zapier) and AI agents (MCP):

1. **`describe`** — point it at a fillable PDF and get back the complete field
   schema as JSON: every field's name, type, page, position, options and the
   exact value that ticks each checkbox.
2. **`fill`** — send data records and get back filled, **flattened**,
   ready-to-send PDFs, one per record.

No OCR, no external services, no accounts to connect. The PDF and the data are
yours; the Actor is pure processing.

### Why `describe` matters

Real-world forms have field names no human would guess. The IRS W-9 calls its
name line `topmostSubform[0].Page1[0].f1_01[0]`, and its federal-classification
checkboxes turn on with the values `"1"` through `"7"` — not `true`, not
`"Yes"`. One `describe` call gives you (or your agent) the exact map:

```json
{
  "name": "topmostSubform[0].Page1[0].Boxes3a-b_ReadOrder[0].c1_1[0]",
  "type": "checkbox",
  "page": 0,
  "rect": [36.2, 594.9, 47.5, 606.2],
  "required": false,
  "on_value": "1"
}
````

### Filling and flattening done properly

Setting a form field's value is the easy part. Making the value actually
**render** is the part most tools skip: a PDF field whose appearance stream was
not regenerated shows up blank in Chrome, Preview and most non-Adobe viewers,
even though the data is technically there.

This Actor regenerates appearance streams with qpdf and then (by default)
flattens the document: the values become permanent page content that renders
identically everywhere, and the form can no longer be edited or tampered with.
Set `"flatten": false` if you want the fields to stay editable instead.

Hybrid XFA forms — the US government kind, W-9 included — are handled: the
AcroForm layer is filled and the stale XFA layer is removed, so Adobe Acrobat
shows your values too. Dynamic XFA forms (no AcroForm fields at all) are
reported as unsupported rather than silently mangled.

### Input

**Describe a form:**

```json
{
  "mode": "describe",
  "pdfUrl": "https://www.irs.gov/pub/irs-pdf/fw9.pdf"
}
```

**Fill it (one object per document to produce):**

```json
{
  "mode": "fill",
  "pdfUrl": "https://www.irs.gov/pub/irs-pdf/fw9.pdf",
  "records": [
    {
      "topmostSubform[0].Page1[0].f1_01[0]": "Marie Dupont",
      "topmostSubform[0].Page1[0].Boxes3a-b_ReadOrder[0].c1_1[0]": true
    }
  ],
  "flatten": true
}
```

Checkboxes accept `true`/`false` or the export value. Radio groups and
dropdowns accept one of the options listed by `describe`. Text fields accept
strings or numbers. Values that match no option are reported as warnings, never
as crashes — the rest of the record is still applied.

### Output

- **Dataset**: one row per described form or per filled record, with a
  machine-readable `status`, the list of `appliedFields`, any `warnings`, and
  the download link of the produced file.
- **Key-value store**: the filled PDFs themselves, `filled-0001.pdf` onwards.

A record that references a wrong field name, a password-protected template, an
unreachable URL — every failure mode is a dataset row with a `status` your
automation can branch on (`download_failed`, `password_protected`,
`xfa_not_supported`, `fill_failed`, …). The run itself succeeds.

### Typical uses

- Generate tax, onboarding, insurance or consent forms from CRM rows or
  spreadsheet data — hundreds per run.
- Let an AI agent fill government forms deterministically: `describe` gives it
  the schema, `fill` executes without hallucinated field names.
- Turn scraped or collected data into signed-ready documents at the end of an
  Apify pipeline.

### Limitations

- Dynamic XFA forms (Adobe LiveCycle, no AcroForm layer) are not supported;
  they are detected and reported as `xfa_not_supported`.
- Signature fields are listed by `describe` but cannot be filled — this Actor
  does not fake signatures.
- Password-protected PDFs are reported as `password_protected`; send the
  unlocked version.
- Values outside Latin-1 (CJK, Cyrillic, Arabic…) cannot be rendered by the
  standard PDF fonts, so such documents are returned **unflattened** with the
  correct values stored and `NeedAppearances` set: any capable viewer renders
  them properly. The row carries a warning. Flattening them would stamp `?`
  into the page forever — we refuse to do that.
- `fill` mode requires a real `pdfUrl`; the bundled sample form is only used
  by `describe`, so a broken pipeline variable can never bill you for filled
  demo forms (`missing_pdf_url`).
- Very large forms: the field schema row is truncated past 2 000 fields (the
  full schema is delivered in the key-value store, see `schemaUrl`), and forms
  beyond 5 000 fields are rejected as `form_too_large`.

### Pricing

Pay per event: a small fee per **successfully** described form (a PDF with no
form at all costs you nothing), and a per-document fee for each filled PDF —
charged only after the file is actually stored. Records skipped over your
run's spending cap are never billed, and invalid records never consume your
cap: every budget cent maps to a delivered document.

# Actor input Schema

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

'describe' lists every field of the form (name, type, options, position) so you know what to send. 'fill' merges the records below into filled PDFs. A typical workflow runs describe once, then fill many times.

## `pdfUrl` (type: `string`):

Direct link to the fillable PDF (the template). Must point at the PDF file itself, not at a viewer page. Leave empty to use the bundled sample form and see the output format.

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

Used in 'fill' mode: one object per document to produce, mapping field names (from 'describe') to values. Text fields take strings or numbers, checkboxes take true/false or the export value, radio and choice fields take one of their options.

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

When on (default), the filled values become permanent page content: the PDF is no longer editable and renders identically in every viewer. Turn off to keep the form fields editable.

## Actor input object example

```json
{
  "mode": "describe",
  "pdfUrl": "https://www.irs.gov/pub/irs-pdf/fw9.pdf",
  "records": [
    {
      "full_name": "Marie Dupont",
      "subscribe": true,
      "colour": "blue"
    }
  ],
  "flatten": true
}
```

# Actor output Schema

## `report` (type: `string`):

One item per described form or per filled record, with a machine-readable status, the applied fields and any warnings.

## `filledPdfs` (type: `string`):

The produced documents, one file per record, named filled-0001.pdf onwards.

# 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 = {
    "records": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("ely_source/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 = { "records": [] }

# Run the Actor and wait for it to finish
run = client.actor("ely_source/pdf-form-filler").call(run_input=run_input)

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

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

```

## CLI example

```bash
echo '{
  "records": []
}' |
apify call ely_source/pdf-form-filler --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=ely_source/pdf-form-filler",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "PDF Form Filler",
        "description": "Discover the field schema of any fillable PDF as JSON, then merge data records into filled, flattened, ready-to-send PDFs in batch. Pure AcroForm processing: no OCR, no external services.",
        "version": "0.1",
        "x-build-id": "mADSPCleWtn9QmAdy"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/ely_source~pdf-form-filler/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-ely_source-pdf-form-filler",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        },
        "/acts/ely_source~pdf-form-filler/runs": {
            "post": {
                "operationId": "runs-sync-ely_source-pdf-form-filler",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor and returns information about the initiated run in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/runsResponseSchema"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/acts/ely_source~pdf-form-filler/run-sync": {
            "post": {
                "operationId": "run-sync-ely_source-pdf-form-filler",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        }
    },
    "components": {
        "schemas": {
            "inputSchema": {
                "type": "object",
                "properties": {
                    "mode": {
                        "title": "Mode",
                        "enum": [
                            "describe",
                            "fill"
                        ],
                        "type": "string",
                        "description": "'describe' lists every field of the form (name, type, options, position) so you know what to send. 'fill' merges the records below into filled PDFs. A typical workflow runs describe once, then fill many times.",
                        "default": "describe"
                    },
                    "pdfUrl": {
                        "title": "PDF URL",
                        "type": "string",
                        "description": "Direct link to the fillable PDF (the template). Must point at the PDF file itself, not at a viewer page. Leave empty to use the bundled sample form and see the output format."
                    },
                    "records": {
                        "title": "Records to fill",
                        "type": "array",
                        "description": "Used in 'fill' mode: one object per document to produce, mapping field names (from 'describe') to values. Text fields take strings or numbers, checkboxes take true/false or the export value, radio and choice fields take one of their options."
                    },
                    "flatten": {
                        "title": "Flatten the output",
                        "type": "boolean",
                        "description": "When on (default), the filled values become permanent page content: the PDF is no longer editable and renders identically in every viewer. Turn off to keep the form fields editable.",
                        "default": true
                    }
                }
            },
            "runsResponseSchema": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "object",
                        "properties": {
                            "id": {
                                "type": "string"
                            },
                            "actId": {
                                "type": "string"
                            },
                            "userId": {
                                "type": "string"
                            },
                            "startedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "finishedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "status": {
                                "type": "string",
                                "example": "READY"
                            },
                            "meta": {
                                "type": "object",
                                "properties": {
                                    "origin": {
                                        "type": "string",
                                        "example": "API"
                                    },
                                    "userAgent": {
                                        "type": "string"
                                    }
                                }
                            },
                            "stats": {
                                "type": "object",
                                "properties": {
                                    "inputBodyLen": {
                                        "type": "integer",
                                        "example": 2000
                                    },
                                    "rebootCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "restartCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "resurrectCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "computeUnits": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "options": {
                                "type": "object",
                                "properties": {
                                    "build": {
                                        "type": "string",
                                        "example": "latest"
                                    },
                                    "timeoutSecs": {
                                        "type": "integer",
                                        "example": 300
                                    },
                                    "memoryMbytes": {
                                        "type": "integer",
                                        "example": 1024
                                    },
                                    "diskMbytes": {
                                        "type": "integer",
                                        "example": 2048
                                    }
                                }
                            },
                            "buildId": {
                                "type": "string"
                            },
                            "defaultKeyValueStoreId": {
                                "type": "string"
                            },
                            "defaultDatasetId": {
                                "type": "string"
                            },
                            "defaultRequestQueueId": {
                                "type": "string"
                            },
                            "buildNumber": {
                                "type": "string",
                                "example": "1.0.0"
                            },
                            "containerUrl": {
                                "type": "string"
                            },
                            "usage": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "integer",
                                        "example": 1
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "usageTotalUsd": {
                                "type": "number",
                                "example": 0.00005
                            },
                            "usageUsd": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "number",
                                        "example": 0.00005
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
