# PDF AcroForm Field Extractor (`automation-lab/pdf-acroform-extractor`) Actor

Extract PDF form field names, values, types, options, flags, pages, and widget coordinates from supplied public PDFs.

- **URL**: https://apify.com/automation-lab/pdf-acroform-extractor.md
- **Developed by:** [Stas Persiianenko](https://apify.com/automation-lab) (community)
- **Categories:** Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per event

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/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.

- **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 AcroForm Field Extractor

Extract **PDF form fields** as structured JSON without OCR or visual guessing. Give the Actor one or more public PDF URLs and receive field names, current/default values, normalized types, choice options, required and read-only flags, page numbers, and exact widget rectangles.

The Actor reads source-native AcroForm annotations. It is designed for document ingestion, fillable-form inventories, migration planning, data-pipeline mapping, and recurring form-schema QA.

### What does PDF AcroForm Field Extractor do?

For every accessible PDF, the Actor:

1. validates that the URL resolves to a public HTTP(S) destination;
2. downloads the file under a configurable timeout and size limit;
3. verifies the PDF file signature;
4. reads Widget annotations directly from the PDF;
5. groups widgets with the same field name and type;
6. writes one typed dataset item per field.

A field that appears on multiple pages stays one record. Its `widgets` array preserves every annotation ID, page, rectangle, export value, visibility flag, and rotation.

### Who is it for?

- **Document automation teams** mapping existing forms into a new workflow.
- **Data engineers** loading interactive PDF schemas into a warehouse or catalog.
- **QA teams** comparing scheduled extracts to detect changed field names or flags.
- **Developers** who need machine-readable coordinates for filling, validation, or form overlays.
- **Operations teams** inventorying public agency, tax, HR, or application forms.

This Actor extracts existing AcroForm definitions. It does not create or edit a PDF form.

### Why use source-native AcroForm extraction?

OCR and page-text extraction answer what a page looks like or says. AcroForm extraction answers what software can fill.

The output retains details that rendered text commonly loses:

| Data | Why it matters |
| --- | --- |
| Fully qualified field name | Stable mapping key for filling and ingestion |
| Current and default values | Identify prefilled or selected controls |
| Field type | Distinguish text, checkbox, radio, choice, button, and signature controls |
| Export/display options | Map dropdown and button values correctly |
| Required/read-only flags | Validate writeability and completion rules |
| Page numbers | Route a field to the right page |
| Rectangle coordinates | Position overlays and QA highlights |
| Widget count | Detect fields represented in more than one location |

No browser, OCR model, screenshot, or residential proxy is used.

### Getting started

1. Open the Actor input page.
2. Add one to 20 direct public PDF URLs in **Public PDF URLs**.
3. Keep `maxFields` small for an initial inspection.
4. Optionally filter `fieldTypes` or include hidden widgets.
5. Start the run.
6. Open the **AcroForm fields** dataset view or export JSON, CSV, Excel, or XML.

A working first input is:

```json
{
  "urls": ["https://www.irs.gov/pub/irs-pdf/fw9.pdf"],
  "maxFields": 150,
  "includeHidden": false
}
```

### Input parameters

| Field | Type | Default | Description |
| --- | --- | --- | --- |
| `urls` | string\[] | required | One to 20 direct public HTTP(S) PDF URLs |
| `maxFields` | integer | `500` | Global output limit, from 1 to 5,000 fields |
| `fieldTypes` | string\[] | all | Optional normalized type filter |
| `includeHidden` | boolean | `false` | Include PDF widgets marked hidden |
| `maxFileSizeMb` | number | `25` | Per-file download limit, from 1 to 100 MB |
| `timeoutSecs` | integer | `60` | Per-file download timeout, from 5 to 180 seconds |

Supported normalized field types are `text`, `checkbox`, `radio`, `push-button`, `choice`, `signature`, and `unknown`.

Duplicate input URLs are processed once. Only records that pass the selected filters are charged and stored.

### Output example

This shortened record reflects the Actor's current extraction shape for the public IRS W-9 PDF:

```json
{
  "recordType": "acroform-field",
  "sourceUrl": "https://www.irs.gov/pub/irs-pdf/fw9.pdf",
  "finalUrl": "https://www.irs.gov/pub/irs-pdf/fw9.pdf",
  "fileName": "fw9.pdf",
  "documentIndex": 1,
  "pageCount": 6,
  "fieldName": "topmostSubform[0].Page1[0].f1_01[0]",
  "alternateName": null,
  "fieldType": "text",
  "value": "",
  "defaultValue": null,
  "options": [],
  "required": false,
  "readOnly": false,
  "hidden": false,
  "multiline": false,
  "password": false,
  "comb": false,
  "maxLength": 0,
  "widgetCount": 1,
  "pageNumbers": [1],
  "widgets": [
    {
      "annotationId": "935R",
      "pageNumber": 1,
      "rectangle": { "x": 58.6, "y": 659.97, "width": 517.4, "height": 14 },
      "exportValue": null,
      "hidden": false,
      "rotation": 0
    }
  ],
  "extractedAt": "2026-01-15T12:00:00.000Z"
}
```

Coordinates use PDF points and a bottom-left origin. `alternateName`, values, lengths, export values, and annotation IDs can be null when a PDF does not define them.

### How much does it cost to extract PDF form fields?

Pricing has two parts: a **$0.005 start event** and one `field` event for each dataset record produced. Failed downloads, filtered fields, duplicate URLs, and PDFs with no AcroForm fields do not create field-event charges.

| Subscription tier | Price per field |
| --- | ---: |
| FREE | $0.00322 |
| BRONZE | $0.0028 |
| SILVER | $0.002184 |
| GOLD | $0.00168 |
| PLATINUM | $0.00112 |
| DIAMOND | $0.000784 |

At BRONZE pricing, 25 fields cost **$0.075** including the start event, while 100 fields cost **$0.285**. The Console applies the active rate for your subscription tier.

Use `maxFields` and `fieldTypes` to bound a production run to the records you actually need.

### Form inventory and schema QA workflows

**One-time inventory:** extract every field from a public form and export the dataset to Excel for review.

**Ingestion mapping:** use `fieldName` as the source key, `fieldType` to choose a destination type, and `options` to populate allowed values.

**Recurring QA:** schedule the Actor against stable form URLs, then compare the latest dataset with a prior run. Changes to field names, page numbers, flags, or rectangles can indicate a publisher revision.

**Multi-document catalog:** submit related PDF URLs together. `sourceUrl`, `fileName`, and `documentIndex` preserve document provenance for every record.

### API usage with cURL

Set `APIFY_TOKEN` in your environment rather than placing it in source code:

```bash
curl -X POST \
  "https://api.apify.com/v2/acts/automation-lab~pdf-acroform-extractor/runs?token=$APIFY_TOKEN&waitForFinish=300" \
  -H "Content-Type: application/json" \
  -d '{
    "urls": ["https://www.irs.gov/pub/irs-pdf/fw9.pdf"],
    "maxFields": 150
  }'
```

Read dataset items from the `defaultDatasetId` returned by the completed run.

### JavaScript API example

```javascript
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('automation-lab/pdf-acroform-extractor').call({
    urls: ['https://www.irs.gov/pub/irs-pdf/fw9.pdf'],
    fieldTypes: ['text', 'checkbox'],
    maxFields: 150,
});

const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items.map(({ fieldName, fieldType, pageNumbers }) => ({ fieldName, fieldType, pageNumbers })));
```

### Python API example

```python
import os
from apify_client import ApifyClient

client = ApifyClient(os.environ['APIFY_TOKEN'])
run = client.actor('automation-lab/pdf-acroform-extractor').call(run_input={
    'urls': ['https://www.irs.gov/pub/irs-pdf/fw9.pdf'],
    'maxFields': 150,
})

for field in client.dataset(run['defaultDatasetId']).iterate_items():
    print(field['fieldName'], field['fieldType'], field['pageNumbers'])
```

### Use with MCP and AI agents

Add the Apify MCP server to Claude Code:

```bash
claude mcp add --transport http apify \
  "https://mcp.apify.com?tools=automation-lab/pdf-acroform-extractor"
```

#### Claude Desktop, Cursor, and VS Code setup

Claude Desktop, Cursor, and VS Code can use this equivalent remote MCP configuration:

```json
{
  "mcpServers": {
    "apify": {
      "url": "https://mcp.apify.com?tools=automation-lab/pdf-acroform-extractor"
    }
  }
}
```

Example prompts:

- "Extract all fields from this public PDF and group their names by page."
- "Return only checkbox and radio fields, including export values."
- "Compare the field names and required flags in these two public form URLs."

### Limits and failure behavior

- URLs must resolve to public IP addresses; loopback, link-local, and private-network targets are rejected.
- Embedded URL credentials are rejected.
- Redirect destinations receive the same public-address validation, up to five redirects.
- The response must begin with the PDF signature `%PDF-`.
- Scanned, flattened, or XFA-only documents may contain no AcroForm Widget annotations.
- Encrypted PDFs that cannot be opened without a password are unsupported.
- JavaScript actions embedded in a PDF are not executed.
- A malformed PDF can expose incomplete metadata even when a reader displays it.

Failures are isolated per URL. If at least one PDF is processed, successful fields remain available and failed URLs are reported in logs. The run fails when every supplied PDF fails to download or parse.

### Legality and responsible use

Only process documents you are authorized to access and store. Public availability does not remove copyright, confidentiality, contractual, retention, or privacy obligations.

Do not place secrets in a PDF URL. The Actor rejects URL user-info but query parameters may still appear in logs or output provenance. Prefer stable public URLs without access tokens.

The Actor treats document content as untrusted data. It parses field metadata but does not execute PDF JavaScript, launch embedded links, or interpret field values as code.

### Troubleshooting

**The run returns zero records.** The document may be flattened, scanned, XFA-only, or simply have no interactive AcroForm widgets. Confirm that a desktop PDF reader can focus individual fields.

**The download fails with HTTP 403.** Use a direct public file URL that permits automated download. The Actor does not silently switch to paid proxies.

**A field appears once but has several locations.** Inspect `widgetCount`, `pageNumbers`, and `widgets`. Widgets sharing the same field name and type are intentionally grouped.

**Coordinates look upside down in a web canvas.** PDF coordinates start at the bottom-left. Browser canvases commonly start at the top-left, so convert Y using the page height.

**Some labels are missing.** Human-readable labels are optional in PDFs. `fieldName` is the source-native identifier; `alternateName` is null when no tooltip/alternate label exists.

### Related Automation Lab Actors

- [PDF Text Extractor](https://apify.com/automation-lab/pdf-text-extractor) extracts document text, per-page text, and metadata when the content—not the form schema—is the target.
- [PDF Structured Table Extractor](https://apify.com/automation-lab/pdf-structured-table-extractor) detects table-like rows and cells with page provenance.

Combine these Actors when a workflow needs form controls, readable page content, and tables as separate typed datasets.

### FAQ

#### Does it fill or modify PDF forms?

No. It is an extractor and inventory tool. It never writes values back into the source PDF.

#### Does it use OCR?

No. OCR cannot reliably recover source-native names, export values, flags, or widget identities. Use PDF Text Extractor for page text and an OCR-specific tool for scanned images.

#### Can it process multiple PDFs?

Yes. Submit up to 20 public URLs. Results share the default dataset and retain source/document provenance.

#### Are checkbox options included?

Yes. Widget export values are normalized into `options` and retained on each widget where available.

#### Can I schedule field-change monitoring?

Yes. Schedule the same input in Apify and compare run datasets downstream. The Actor provides stable field names and structural metadata; it does not itself send alerts or compute diffs.

# Actor input Schema

## `urls` (type: `array`):

One to 20 direct, public HTTP(S) URLs. Each response must contain a PDF file; private-network and credential-bearing URLs are rejected.

## `maxFields` (type: `integer`):

Maximum AcroForm field records written across all PDFs. Processing stops before this output limit is exceeded.

## `fieldTypes` (type: `array`):

Optional field-type filter. Leave empty to return every supported and unknown AcroForm widget type.

## `includeHidden` (type: `boolean`):

Include widgets marked hidden in the PDF. Hidden fields are excluded by default.

## `maxFileSizeMb` (type: `number`):

Reject each PDF that exceeds this download size. The limit applies before parsing and protects run memory.

## `timeoutSecs` (type: `integer`):

Maximum time allowed for each PDF download, including response-body transfer.

## Actor input object example

```json
{
  "urls": [
    "https://www.irs.gov/pub/irs-pdf/fw9.pdf"
  ],
  "maxFields": 500,
  "includeHidden": false,
  "maxFileSizeMb": 25,
  "timeoutSecs": 60
}
```

# Actor output Schema

## `dataset` (type: `string`):

Dataset containing one record per AcroForm field name and type, including all associated widgets.

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

// Run the Actor and wait for it to finish
const run = await client.actor("automation-lab/pdf-acroform-extractor").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 = { "urls": ["https://www.irs.gov/pub/irs-pdf/fw9.pdf"] }

# Run the Actor and wait for it to finish
run = client.actor("automation-lab/pdf-acroform-extractor").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 '{
  "urls": [
    "https://www.irs.gov/pub/irs-pdf/fw9.pdf"
  ]
}' |
apify call automation-lab/pdf-acroform-extractor --silent --output-dataset

```

## MCP server setup

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

```

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/23FDUghPkzt87Av9t/builds/5UlxvnpybZf5XmyQc/openapi.json
