# PDF to JSON Schema Extractor (`thomas.5fm/pdf-to-json-schema-extractor`) Actor

Extract typed, validated JSON from clean digital invoices, receipts, statements, and simple tables using a target JSON Schema - honest per-field validation, never fabricated values.

- **URL**: https://apify.com/thomas.5fm/pdf-to-json-schema-extractor.md
- **Developed by:** [Thomas](https://apify.com/thomas.5fm) (community)
- **Categories:** Developer tools, AI, MCP servers
- **Stats:** 1 total users, 1 monthly users, 0.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.

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

## What's an Apify Actor?

Actors are a software tools running on the Apify platform, for all kinds of web data extraction and automation use cases.
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.md):

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

In Python projects, use official [Python client library](https://docs.apify.com/api/client/python.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/platform/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 to JSON Schema Extractor

Extract **structured, typed JSON from your own PDF documents using a target JSON Schema**. You define the schema; the actor returns output that conforms to it — invoices, receipts, registers, agreements, filings, forms. Tables are mapped to **typed rows** (numbers as numbers, booleans as booleans), and every run includes an honest **validation report**: which fields matched, which are missing, which are low-confidence.

Built for RAG and automation pipelines: the output is a contract, not a text dump.

### What this handles — and what it doesn't

**Works well on:** clean, digitally-generated PDFs with printed label–value fields (colon-separated *or* column-positioned) and/or tables — invoices, receipts, order confirmations, registers, form summaries — including **structured fixed-width statements and ledgers** with dashed column separators (e.g., A/R royalty statements: data rows are extracted as typed objects, and interleaved subtotal/"Total for…" rows are automatically excluded). Ruled and unruled tables are both detected. Scanned pages get an OCR fallback.

**Not built for:** free-flowing prose where values are embedded in sentences; complex multi-section reports with nested or merged tables; poor-quality scans (OCR will flag low confidence, but garbage in limits what any tool can match). On these documents you get an honest result — the fields it can't find are listed as **missing** in the validation report.

**The promise either way:** this actor never invents a field it can't find. An out-of-scope document produces missing-field results, not fabricated data.

### What makes it different

- **A schema contract, not generic OCR.** Generic PDF extractors give you unstructured text or fixed layouts. Here *you* declare the output shape — typed fields, nested objects, arrays for table rows — and get validated JSON back.
- **Typed table extraction.** Detects ruled *and* unruled (text-aligned) tables, merges split columns, finds the real header row, and returns rows as typed objects matching your schema's array property.
- **Honest output, always.** Fields the document doesn't contain are reported as missing — never invented. Every matched field carries a confidence score and the page it came from.
- **Your documents stay yours.** First-party files only (your uploads, your URLs). The actor scrapes nothing, calls no external AI service, and your document content never leaves the run.
- **OCR fallback** for scanned/image-only pages, on by default.
- **Batch-friendly:** pass many document URLs; one output item per document; one bad file never sinks the batch (it becomes a structured error item instead).

### How to use

**Input is by URL, not drag-and-drop.** The actor reads your PDFs from links you provide — there is no file-picker upload in the input form. If your files aren't already at a URL, upload them once to an Apify key-value store (Storage → Key-value stores → upload, then copy each record's URL) or use a signed link from S3/Drive/Dropbox. For AI agents calling this actor as an MCP tool this is zero friction; for a human it's one extra step, shown below.

1. Put direct URLs to your own PDFs in **Document URLs** (signed S3/Drive/Dropbox links, or Apify key-value-store record URLs as above).
2. Paste your **Target JSON Schema**. Tips:
   - Give each property a `title` matching the label printed in the document (e.g. `"title": "Invoice Number"` for a line reading `Invoice Number: INV-2041`).
   - Use `"type": "number"`, `"integer"`, or `"boolean"` to get typed values (currency symbols and thousands separators are handled).
   - For tables, use an array property whose `items.properties` mirror the table's columns.
3. Run. Each dataset item contains `data` (your schema, filled), `validation` (matched / missing / low-confidence fields + JSON-Schema validation result), and page counts.

Works the same when called by an AI agent via Apify's MCP server — the input schema above is the tool contract.

### Example

Input document line: `Total Amount: $4,850.00` · Schema property: `"totalAmount": {"type": "number", "title": "Total Amount"}` → Output: `"totalAmount": 4850.0`, listed in `validation.matchedFields` with its confidence and page number.

### Pricing (pay per event)

Two charges, both flat and disclosed:

- **$0.01 per page processed** — charged only for pages that were actually processed successfully.
- **$0.005 actor-start fee per run** — this one is charged on every run start, including a run where nothing matches or every document errors. It is the only charge you pay on an unsuccessful run.

One worked example:

> A 12-page invoice batch (3 documents, 4 pages each) = 12 `page-processed` events at $0.01 = **$0.12**, plus the $0.005 actor-start fee = **$0.125 total**. A 500-page monthly filing run ≈ **$5.00**. Compare: subscription document-parsing SaaS starts at ~$39/month regardless of volume.

Use **Page range** to limit long documents. Failed/corrupt documents produce structured error items and are **never charged per-page**.

### Honest limits

- Works best on documents with **printed label–value pairs and/or tables** (invoices, receipts, registers, forms, agreement summaries). It is deterministic — it does not "read" free-flowing prose; a field buried in a paragraph will usually be reported as *missing* rather than guessed.
- OCR quality on poor scans limits what can be matched; low-confidence matches are flagged so you can decide.
- One table per array property (the best match is chosen; its column coverage is reported).

### Output shape

```json
{
  "status": "OK",
  "document": "https://…/invoice.pdf",
  "data": { "invoiceNumber": "INV-2041", "totalAmount": 4850.0, "lineItems": [ { "description": "…", "quantity": 2, "unitPrice": 1200.0 } ] },
  "validation": {
    "schemaValid": true,
    "matchedFields": [ { "field": "invoiceNumber", "confidence": 1.0, "page": 1 } ],
    "missingFields": [ "poNumber" ],
    "lowConfidenceFields": []
  },
  "pagesProcessed": 4,
  "ocrPages": 0
}
````

# Actor input Schema

## `documentUrls` (type: `array`):

Direct URLs to YOUR OWN PDF files (signed S3/Drive/Dropbox links, or files uploaded to an Apify key-value store). One output item is produced per document. This actor processes first-party documents only — it does not scrape websites. Leave empty to run the bundled sample document.

## `targetSchema` (type: `object`):

The JSON Schema your output must conform to. Scalar properties ('type': 'string' | 'number' | 'integer' | 'boolean') are matched against label–value pairs in the document; an array property whose items define 'properties' is matched against the best table and returned as typed rows. Use 'title' on each property to hint the label as printed in the document (e.g. 'Invoice Number'). Output contains ONLY fields actually found — missing fields are listed in the validation report, never invented.

## `ocrFallback` (type: `boolean`):

When a page has no extractable text layer (scanned/image-only PDFs), run OCR on it instead. Turn off to skip image-only pages.

## `pageRange` (type: `string`):

Optional 1-based page selection, e.g. '1-3' or '1,4,7-9'. Leave empty to process every page. You are charged only for pages processed.

## `tableMode` (type: `string`):

Auto detects ruled AND unruled (text-aligned) tables and maps the best match to your schema's array property. Off skips table detection.

## Actor input object example

```json
{
  "documentUrls": [],
  "targetSchema": {
    "type": "object",
    "properties": {
      "invoiceNumber": {
        "type": "string",
        "title": "Invoice Number"
      },
      "totalAmount": {
        "type": "number",
        "title": "Total Amount"
      },
      "lineItems": {
        "type": "array",
        "title": "Line Items",
        "items": {
          "type": "object",
          "properties": {
            "description": {
              "type": "string",
              "title": "Description"
            },
            "quantity": {
              "type": "integer",
              "title": "Quantity"
            },
            "unitPrice": {
              "type": "number",
              "title": "Unit Price"
            }
          }
        }
      }
    }
  },
  "ocrFallback": true,
  "tableMode": "auto"
}
```

# 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 = {
    "documentUrls": [],
    "targetSchema": {
        "type": "object",
        "properties": {
            "invoiceNumber": {
                "type": "string",
                "title": "Invoice Number"
            },
            "totalAmount": {
                "type": "number",
                "title": "Total Amount"
            },
            "lineItems": {
                "type": "array",
                "title": "Line Items",
                "items": {
                    "type": "object",
                    "properties": {
                        "description": {
                            "type": "string",
                            "title": "Description"
                        },
                        "quantity": {
                            "type": "integer",
                            "title": "Quantity"
                        },
                        "unitPrice": {
                            "type": "number",
                            "title": "Unit Price"
                        }
                    }
                }
            }
        }
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("thomas.5fm/pdf-to-json-schema-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 = {
    "documentUrls": [],
    "targetSchema": {
        "type": "object",
        "properties": {
            "invoiceNumber": {
                "type": "string",
                "title": "Invoice Number",
            },
            "totalAmount": {
                "type": "number",
                "title": "Total Amount",
            },
            "lineItems": {
                "type": "array",
                "title": "Line Items",
                "items": {
                    "type": "object",
                    "properties": {
                        "description": {
                            "type": "string",
                            "title": "Description",
                        },
                        "quantity": {
                            "type": "integer",
                            "title": "Quantity",
                        },
                        "unitPrice": {
                            "type": "number",
                            "title": "Unit Price",
                        },
                    },
                },
            },
        },
    },
}

# Run the Actor and wait for it to finish
run = client.actor("thomas.5fm/pdf-to-json-schema-extractor").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 '{
  "documentUrls": [],
  "targetSchema": {
    "type": "object",
    "properties": {
      "invoiceNumber": {
        "type": "string",
        "title": "Invoice Number"
      },
      "totalAmount": {
        "type": "number",
        "title": "Total Amount"
      },
      "lineItems": {
        "type": "array",
        "title": "Line Items",
        "items": {
          "type": "object",
          "properties": {
            "description": {
              "type": "string",
              "title": "Description"
            },
            "quantity": {
              "type": "integer",
              "title": "Quantity"
            },
            "unitPrice": {
              "type": "number",
              "title": "Unit Price"
            }
          }
        }
      }
    }
  }
}' |
apify call thomas.5fm/pdf-to-json-schema-extractor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=thomas.5fm/pdf-to-json-schema-extractor",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "PDF to JSON Schema Extractor",
        "description": "Extract typed, validated JSON from clean digital invoices, receipts, statements, and simple tables using a target JSON Schema - honest per-field validation, never fabricated values.",
        "version": "0.1",
        "x-build-id": "mtvY76rLkLBacVY7q"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/thomas.5fm~pdf-to-json-schema-extractor/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-thomas.5fm-pdf-to-json-schema-extractor",
                "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/thomas.5fm~pdf-to-json-schema-extractor/runs": {
            "post": {
                "operationId": "runs-sync-thomas.5fm-pdf-to-json-schema-extractor",
                "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/thomas.5fm~pdf-to-json-schema-extractor/run-sync": {
            "post": {
                "operationId": "run-sync-thomas.5fm-pdf-to-json-schema-extractor",
                "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": {
                    "documentUrls": {
                        "title": "Document URLs (your own files)",
                        "type": "array",
                        "description": "Direct URLs to YOUR OWN PDF files (signed S3/Drive/Dropbox links, or files uploaded to an Apify key-value store). One output item is produced per document. This actor processes first-party documents only — it does not scrape websites. Leave empty to run the bundled sample document.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "targetSchema": {
                        "title": "Target JSON Schema",
                        "type": "object",
                        "description": "The JSON Schema your output must conform to. Scalar properties ('type': 'string' | 'number' | 'integer' | 'boolean') are matched against label–value pairs in the document; an array property whose items define 'properties' is matched against the best table and returned as typed rows. Use 'title' on each property to hint the label as printed in the document (e.g. 'Invoice Number'). Output contains ONLY fields actually found — missing fields are listed in the validation report, never invented."
                    },
                    "ocrFallback": {
                        "title": "OCR fallback for scanned pages",
                        "type": "boolean",
                        "description": "When a page has no extractable text layer (scanned/image-only PDFs), run OCR on it instead. Turn off to skip image-only pages.",
                        "default": true
                    },
                    "pageRange": {
                        "title": "Page range",
                        "type": "string",
                        "description": "Optional 1-based page selection, e.g. '1-3' or '1,4,7-9'. Leave empty to process every page. You are charged only for pages processed."
                    },
                    "tableMode": {
                        "title": "Table extraction",
                        "enum": [
                            "auto",
                            "off"
                        ],
                        "type": "string",
                        "description": "Auto detects ruled AND unruled (text-aligned) tables and maps the best match to your schema's array property. Off skips table detection.",
                        "default": "auto"
                    }
                }
            },
            "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
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
