# Pdf Document Structured Json Extraction (`first_watch/pdf-document-structured-json-extraction`) Actor

Deprecated: use first_watch/pdf-table-extraction, which extracts and validates PDF tables with independently tested results and pays-per-accepted-row billing.

- **URL**: https://apify.com/first_watch/pdf-document-structured-json-extraction.md
- **Developed by:** [Jordan Nabbe](https://apify.com/first_watch) (community)
- **Categories:** Automation, Developer tools, Business
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $10.00 / 1,000 result returneds

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 Document Structured JSON Extraction

Deterministically extracts and validates structured JSON records from **inline** documents against a customer-supplied JSON Schema (a 2020-12 subset). The Actor never makes a network request: it reads only the `INPUT` you provide and writes to the dataset and the `OUTPUT` key.

### Input

Set `mode` to `inline` (the only supported value) and provide `jsonSchema` and `documents` (1-100). Each document has an `id`, a `contentType` and exactly one of `text`, `json`, `records` or `base64`.

- `text/plain`, `text/html`, `text/csv`: `Label: value` lines (or the first CSV row) are matched to schema property names or your `fieldConfig` aliases.
- `application/json`: keys are mapped directly onto schema properties (case-insensitive, aliases supported).
- `application/vnd.pdf-layout+json`: a `records` array of `{ text, page, boundingBox }` pre-extracted from a PDF; page and bounding box are carried into the evidence.
- `application/pdf` (base64): **not parsed in this build**; returned as `UNSUPPORTED`. Supply already-extracted text, json or records instead. OCR is not available.
- Other options: `fieldConfig`, `includeEvidence`, `minimumConfidence`, `failOnInvalid`. `validationOnly` and `templateId` are accepted but informational only.

Supported schema keywords: type, properties, required, additionalProperties (boolean), enum, const, format (date, date-time), pattern, string/numeric/array limits, items, uniqueItems, anyOf, oneOf, allOf.

### Output

Each document yields one dataset row: `documentId`, `fileName`, `status` (`SUCCEEDED`, `INVALID`, `UNSUPPORTED`, `FAILED`), `record`, `schemaValid`, `validationErrors`, `missingFields`, `ambiguousFields`, `fieldEvidence` (confidence, sourceText, method, page, boundingBox), `warnings`, `processing`. The `OUTPUT` record holds `{ total, succeeded, invalid, unsupported, failed, items }`, where `items` equals the dataset rows.

### Pricing

No pay-per-event pricing is configured in this build; you pay standard Apify platform compute and storage. Work is in-memory string/JSON processing with no browser, proxy or external API, so typical runs are very cheap.

### Limits

- Max 100 documents, 100 MB total input, 25 MB per document, 256 KB serialized schema.
- Nesting depth 8, 100 keys per object, 1000 items per array (applied to schema, fieldConfig, json and records).
- Zero network requests.

### Failure behaviour

- Invalid input (wrong mode, missing or oversized schema, malformed documents, unknown properties) fails validation before processing and exits non-zero; see `sample-invalid.json`.
- One bad document (malformed base64/JSON, missing content) becomes a `FAILED` row and does not stop the others. PDFs become `UNSUPPORTED` rows.
- Missing, ambiguous or type-invalid fields make the row `INVALID` with details. The run fails only when `failOnInvalid` is true, after all results are stored.

# Actor input Schema

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

Only 'inline' is supported in this offline build.

## `documents` (type: `array`):

Inline documents (max 100). Each needs id, contentType and exactly one of text, json, records or base64. application/pdf is returned as UNSUPPORTED.

## `jsonSchema` (type: `object`):

JSON Schema (2020-12 subset) every record must satisfy. Max 256 KB serialized.

## `fieldConfig` (type: `object`):

Optional per-property aliases and descriptions (max 100 keys).

## `includeEvidence` (type: `boolean`):

Include per-field confidence and source text.

## `minimumConfidence` (type: `number`):

Fields below this confidence produce a warning.

## `failOnInvalid` (type: `boolean`):

If true, exit non-zero after storing results when any document is INVALID.

## `validationOnly` (type: `boolean`):

Reserved; informational only.

## `templateId` (type: `string`):

Optional caller bookkeeping identifier.

## Actor input object example

```json
{
  "mode": "inline",
  "documents": [
    {
      "id": "invoice-1001",
      "contentType": "text/plain",
      "fileName": "invoice-1001.txt",
      "text": "Invoice Number: INV-1001\nInvoice Date: 2026-09-20\nSupplier: Northwind Parts Ltd\nTotal Due: 1240.50\nCurrency: GBP"
    }
  ],
  "jsonSchema": {
    "type": "object",
    "additionalProperties": false,
    "required": [
      "invoiceNumber",
      "invoiceDate",
      "supplier",
      "totalDue",
      "currency"
    ],
    "properties": {
      "currency": {
        "type": "string",
        "enum": [
          "GBP",
          "EUR",
          "USD"
        ]
      },
      "invoiceDate": {
        "type": "string",
        "format": "date"
      },
      "invoiceNumber": {
        "type": "string"
      },
      "supplier": {
        "type": "string"
      },
      "totalDue": {
        "type": "number",
        "minimum": 0
      }
    }
  },
  "fieldConfig": {
    "invoiceNumber": {
      "aliases": [
        "Invoice Number",
        "Invoice No"
      ],
      "description": "Unique invoice identifier"
    },
    "totalDue": {
      "aliases": [
        "Total Due",
        "Amount Due"
      ],
      "description": "Final amount payable"
    }
  },
  "includeEvidence": true,
  "minimumConfidence": 0.7,
  "failOnInvalid": false,
  "validationOnly": false
}
```

# Actor output Schema

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

Every result row stored in the default dataset.

## `summary` (type: `string`):

Summary written to the OUTPUT record.

# 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": "inline",
    "documents": [
        {
            "id": "invoice-1001",
            "contentType": "text/plain",
            "fileName": "invoice-1001.txt",
            "text": "Invoice Number: INV-1001\nInvoice Date: 2026-09-20\nSupplier: Northwind Parts Ltd\nTotal Due: 1240.50\nCurrency: GBP"
        }
    ],
    "jsonSchema": {
        "type": "object",
        "additionalProperties": false,
        "required": [
            "invoiceNumber",
            "invoiceDate",
            "supplier",
            "totalDue",
            "currency"
        ],
        "properties": {
            "currency": {
                "type": "string",
                "enum": [
                    "GBP",
                    "EUR",
                    "USD"
                ]
            },
            "invoiceDate": {
                "type": "string",
                "format": "date"
            },
            "invoiceNumber": {
                "type": "string"
            },
            "supplier": {
                "type": "string"
            },
            "totalDue": {
                "type": "number",
                "minimum": 0
            }
        }
    },
    "fieldConfig": {
        "invoiceNumber": {
            "aliases": [
                "Invoice Number",
                "Invoice No"
            ],
            "description": "Unique invoice identifier"
        },
        "totalDue": {
            "aliases": [
                "Total Due",
                "Amount Due"
            ],
            "description": "Final amount payable"
        }
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("first_watch/pdf-document-structured-json-extraction").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": "inline",
    "documents": [{
            "id": "invoice-1001",
            "contentType": "text/plain",
            "fileName": "invoice-1001.txt",
            "text": """Invoice Number: INV-1001
Invoice Date: 2026-09-20
Supplier: Northwind Parts Ltd
Total Due: 1240.50
Currency: GBP""",
        }],
    "jsonSchema": {
        "type": "object",
        "additionalProperties": False,
        "required": [
            "invoiceNumber",
            "invoiceDate",
            "supplier",
            "totalDue",
            "currency",
        ],
        "properties": {
            "currency": {
                "type": "string",
                "enum": [
                    "GBP",
                    "EUR",
                    "USD",
                ],
            },
            "invoiceDate": {
                "type": "string",
                "format": "date",
            },
            "invoiceNumber": { "type": "string" },
            "supplier": { "type": "string" },
            "totalDue": {
                "type": "number",
                "minimum": 0,
            },
        },
    },
    "fieldConfig": {
        "invoiceNumber": {
            "aliases": [
                "Invoice Number",
                "Invoice No",
            ],
            "description": "Unique invoice identifier",
        },
        "totalDue": {
            "aliases": [
                "Total Due",
                "Amount Due",
            ],
            "description": "Final amount payable",
        },
    },
}

# Run the Actor and wait for it to finish
run = client.actor("first_watch/pdf-document-structured-json-extraction").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": "inline",
  "documents": [
    {
      "id": "invoice-1001",
      "contentType": "text/plain",
      "fileName": "invoice-1001.txt",
      "text": "Invoice Number: INV-1001\\nInvoice Date: 2026-09-20\\nSupplier: Northwind Parts Ltd\\nTotal Due: 1240.50\\nCurrency: GBP"
    }
  ],
  "jsonSchema": {
    "type": "object",
    "additionalProperties": false,
    "required": [
      "invoiceNumber",
      "invoiceDate",
      "supplier",
      "totalDue",
      "currency"
    ],
    "properties": {
      "currency": {
        "type": "string",
        "enum": [
          "GBP",
          "EUR",
          "USD"
        ]
      },
      "invoiceDate": {
        "type": "string",
        "format": "date"
      },
      "invoiceNumber": {
        "type": "string"
      },
      "supplier": {
        "type": "string"
      },
      "totalDue": {
        "type": "number",
        "minimum": 0
      }
    }
  },
  "fieldConfig": {
    "invoiceNumber": {
      "aliases": [
        "Invoice Number",
        "Invoice No"
      ],
      "description": "Unique invoice identifier"
    },
    "totalDue": {
      "aliases": [
        "Total Due",
        "Amount Due"
      ],
      "description": "Final amount payable"
    }
  }
}' |
apify call first_watch/pdf-document-structured-json-extraction --silent --output-dataset

```

## MCP server setup

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

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/axpwyz6JOqaSUfhFc/builds/MiyZhhUwoK9p9Llk3/openapi.json
