# PDF & Document to JSON Extractor (AI) — Invoices, Forms (`owlabee/document-to-json-extractor`) Actor

Turn PDF, DOCX and HTML documents into JSON that matches your own schema: invoices, contracts, forms, statements. Free rules mode, or Claude with your key or ours (fast, balanced, best tiers). Pay per document and page, no browser, no proxies.

- **URL**: https://apify.com/owlabee/document-to-json-extractor.md
- **Developed by:** [Owlabee](https://apify.com/owlabee) (community)
- **Categories:** AI, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $20.00 / 1,000 document extracted (hosted, fast tier)s

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 to JSON Extractor (AI) — Invoices, Forms

### What it does

AI Document to JSON Extractor turns documents into structured data that matches **a schema you define**. Give it the URLs of PDF, DOCX, HTML, Markdown or text files and a JSON Schema of the object you want (vendor, invoice number, line items, totals; parties, effective date, termination clause, governing law; form title, fields, revision date), and for each document it writes one dataset item with the filled object in `data`, a per-field `confidence` score, the list of fields it could not find, and the document's metadata (filename, pages, title, author). The documents are read with a real layout engine (headings, tables, lists, page markers from PDFs; DOCX structure; HTML), not a plain text dump, so tables become rows and labels stay next to their values.

It has three extractors, chosen with the `extractor` input and recorded on every item as `extractionMethod`. **AI extraction is the default**: when `extractor` is left empty the Actor uses the hosted Claude model; set `extractor=rules` for a free, model-free run, or `extractor=byok` to use your own key.

- **`hosted`** (default): Claude on the Actor's own key, tokens included, from $0.02 per document plus $0.002 per page. Nothing to set up. Pick a **model tier**: `fast` (Claude Haiku 5.5), `balanced` (Claude Sonnet 5.5) or `best` (Claude Opus 5.5). The model fills your schema through structured output, so the JSON always has your shape; fields the document does not state come back `null`, and the model lists the fields it was unsure about in `lowConfidenceFields`. If the model cannot be reached, the document gets the rules extraction instead, marked `extractionMethod: "rules-fallback"` with an `extractionWarning`, and is charged the rules price; a run never fails because of the model.
- **`byok`** (bring your own key): Claude fills the schema using the `anthropicApiKey` you supply (stored as a secret, sent only to api.anthropic.com), on the same three tiers. You pay Anthropic for the tokens and $0.01 per document here; each item records `tokensIn`, `tokensOut` and `modelCostUsdEstimate`.
- **`rules`**: a deterministic extractor with no language model. It collects label/value pairs, tables, numbered form fields, dates, amounts, emails, phone numbers and URLs from the document and matches them to your field names, titles and descriptions, coercing types (money text to numbers, dates to ISO, yes/no to booleans, table rows to arrays of objects). No key, runs in seconds, costs $0.004 per document. Right for well-labeled documents (invoices, forms, statements), for bulk first passes, and for checking what the model modes add.

Built for finance and legal operations (invoices, purchase orders, bank statements, contracts, NDAs, lease abstracts), procurement and compliance teams processing government forms, and AI agents that find a document and need it as JSON on demand. Results are available as JSON, CSV, Excel, through the API, webhooks and the Apify MCP server.

Construction estimators use it too: a roof takeoff schema run on drawing and spec PDFs with a text layer, and supplier quotes turned into line items for estimate reconciliation; see the task examples.

### How to use

1. Enter your **Document URLs** (one per line): public links to PDF, DOCX, HTML, Markdown or text files, `kvs://<storeId>/<key>` references to files in an Apify key-value store, or `data:` URLs.
2. Paste the **JSON Schema** of the object you want per document. Give every field a `description` so the extractor knows what to look for; use `format: "date"` for dates and `enum` for fixed choices. The prefilled schema describes a government form; replace it with yours.
3. Leave **Extractor** empty for hosted AI extraction and pick a **Model tier** (`fast` is the default); or choose `byok` and paste your **Anthropic API key**; or choose `rules` for a free, model-free run. Click **Start**. One item per document appears in the dataset as each extraction is ready.

#### Quick start input

The same input the Store runs in its daily automated test, so it is cheap and always available; paste it into the JSON tab of the input form or send it as the body of an API or MCP call:

```json
{
  "urls": [
    "https://www.irs.gov/pub/irs-pdf/fw9.pdf"
  ],
  "schema": {
    "type": "object",
    "properties": {
      "title": {
        "type": "string",
        "description": "Title of the form"
      },
      "formNumber": {
        "type": "string",
        "description": "The form number, e.g. W-9 or 1040"
      },
      "revisionDate": {
        "type": "string",
        "format": "date",
        "description": "Revision date of the form"
      },
      "issuer": {
        "type": "string",
        "description": "Agency or organization that issues the form"
      },
      "fields": {
        "type": "array",
        "description": "The numbered fields a filer completes",
        "items": {
          "type": "object",
          "properties": {
            "label": {
              "type": "string"
            },
            "description": {
              "type": "string"
            }
          }
        }
      }
    }
  },
  "extractor": "rules",
  "includeMarkdown": false
}
```

### Input

| Field | Type | Required | Description |
|---|---|---|---|
| `urls` | string\[] | yes | Document URLs, `kvs://<storeId>/<key>` references or `data:` URLs, one per line |
| `schema` | object | yes | JSON Schema (draft-07 subset) of the object to extract: `type: object` with `properties`; field types string, number, integer, boolean, array, object; `description`, `format` (date, date-time, email, uri), `enum`. Nested objects and arrays of objects are supported; `$ref`, `oneOf`/`anyOf` are not |
| `extractor` | string | no | `hosted`, `byok` or `rules`. Empty = `hosted` when the Actor's model key is configured, else `rules` |
| `anthropicApiKey` | string (secret) | for `byok` | Your Anthropic API key; used only in `byok` mode |
| `modelTier` | string | no | `fast` (default, Claude Haiku 5.5), `balanced` (Claude Sonnet 5.5) or `best` (Claude Opus 5.5); applies to `hosted` and `byok` |
| `instructions` | string | no | Extra guidance for the model (conventions, what to ignore, how to choose between candidates). Ignored by `rules` |
| `maxInputTokens` | integer | no | Per-document input cap for model modes, 2,000–40,000 (default 40,000, about 60 pages). Longer documents are cut and the item marked `inputTruncated: true` |
| `maxConcurrency` | integer | no | Documents processed in parallel, 1–5 (default 2) |
| `includeMarkdown` | boolean | no | Also write the document's full converted Markdown on the item (default false; a 1,000-character `textPreview` is always there) |
| `maxFileSizeMb` | integer | no | Files larger than this become a `TOO_LARGE` error item (default 25, max 50) |
| `downloadTimeoutSecs` | integer | no | Per-file download timeout (default 20, max 120) |
| `webhookUrl` | string | no | POST target for the run summary (documents, errors, pages, fields filled, token totals, charges) |

### Output example

An item produced in `rules` mode from the IRS Form W-9 with the prefilled schema (`title`, `formNumber`, `revisionDate`, `issuer`, `fields[]`):

```json
{
  "recordType": "document",
  "url": "https://www.irs.gov/pub/irs-pdf/fw9.pdf",
  "filename": "fw9.pdf",
  "mimeType": "application/pdf",
  "bytes": 140815,
  "pages": 6,
  "pagesProcessed": 6,
  "title": "Form W-9 (Rev. March 2024)",
  "author": "SE:W:CAR:MP",
  "creationDate": "2024-03-06T13:18:13.000Z",
  "characters": 38065,
  "words": 6351,
  "needsOcrPages": [],
  "warnings": ["4 rotated text item(s) skipped (watermarks or sideways labels)"],
  "extractionMethod": "rules",
  "extractionNotes": [
    "title: document metadata",
    "formNumber: \"Form W-9\" in the header",
    "revisionDate: date near \"(Rev. March 2024)\"",
    "issuer: organization name on the first page",
    "fields: 6 numbered form fields"
  ],
  "data": {
    "title": "Form W-9 (Rev. March 2024)",
    "formNumber": "W-9",
    "revisionDate": "2024-03",
    "issuer": "Department of the Treasury",
    "fields": [
      { "label": "Name of entity/individual", "description": "An entry is required. (For a sole proprietor or disregarded entity, enter the owner’s name on line 1, and enter the business/disregarded entity’s name on line 2.)" },
      { "label": "Business name/disregarded entity name, if different from above", "description": null },
      { "label": "Address (number, street, and apt. or suite no.)", "description": "See instructions. Requester’s name and address (optional)" },
      { "label": "City, state, and ZIP code", "description": null },
      { "label": "List account number(s) here (optional)", "description": null }
    ]
  },
  "confidence": { "title": 0.8, "formNumber": 0.6, "revisionDate": 0.6, "issuer": 0.4, "fields": 0.5 },
  "overallConfidence": 0.58,
  "filledFields": 5,
  "totalFields": 5,
  "missingFields": [],
  "textPreview": "Form W-9 Request for Taxpayer Give form to the (Rev. March 2024) Identification Number and Certification requester. Do not …",
  "processingMs": 2267,
  "fetchedAt": "2026-10-17T10:22:02.464Z"
}
```

`data` always has exactly the shape of your schema: every property is present, unfilled ones are `null` (nested objects keep their keys, each `null`). `confidence` has one number per top-level property (0 = not found, up to 0.9 for an exact label match in rules mode; in model modes it is the model's overall confidence, lowered to at most 0.5 for the paths it lists in `lowConfidenceFields`). `filledFields`, `totalFields` and `missingFields` count the fields a document can fill (each scalar, each array once).

Items written in `byok` or `hosted` mode carry the same `data` and `confidence` and additionally `modelTier`, `model`, `tokensIn`, `tokensOut`, `modelCostUsdEstimate` (USD at Anthropic list prices), `inputTruncated` (with `inputKeptRatio` and `inputTruncationNote` when the document was cut to the cap), `lowConfidenceFields`, `extractionNotes` (the model's remarks: unreadable tables, conflicting values) and `validationWarnings` (values the model returned that did not fit your types and were set to `null`). If a model call fails (rate limit, API error, timeout, refusal, invalid JSON) or the hosted model is not configured, the item falls back to the rules extraction with `extractionMethod: "rules-fallback"`, records the reason in `extractionWarning`, and is charged the rules price.

Error items look like `{ "recordType": "document", "url": "…", "error": "NOT_FOUND", "errorMessage": "HTTP 404 for …" }` with codes `INVALID_URL`, `DOWNLOAD_FAILED`, `NOT_FOUND`, `TIMEOUT`, `TOO_LARGE`, `UNSUPPORTED_TYPE`, `CORRUPT_FILE`, `PASSWORD_PROTECTED`, `PROCESSING_FAILED` and `NO_TEXT` (a scanned PDF without a text layer, see FAQ). They are charged at the rules price, because the work to discover the problem was done. An unusable `schema`, a `byok` run without a key, or a key Anthropic rejects fails the run with a clear message before any further document is charged.

If the output saves you time, a review on the Store page helps other buyers find this Actor; the Store invites you to leave one after you have run it a few times.

### Pricing

Pay per event. You only pay for what the Actor produces; set a **maximum cost per run** in the run options to cap spend.

| Event | Price | Notes |
|---|---|---|
| Document extracted (rules, no model) | $0.004 | Per document item in `rules` mode or on a `rules-fallback`; error items are charged at this price too |
| Document extracted (your Anthropic key) | $0.01 | Per document extracted by Claude in `byok` mode, any tier, any length; your tokens are billed by Anthropic, not here |
| Document extracted (hosted, fast tier) | $0.02 | Claude Haiku 5.5 on the Actor's key, tokens included, up to 40,000 input tokens per document |
| Page processed (hosted, fast tier) | $0.002 | Per chargeable page of a `fast` document, on top of the document event |
| Document extracted (hosted, balanced tier) | $0.15 | Claude Sonnet 5.5, same caps |
| Page processed (hosted, balanced tier) | $0.02 | Per page of a `balanced` document |
| Document extracted (hosted, best tier) | $0.30 | Claude Opus 5.5, same caps |
| Page processed (hosted, best tier) | $0.04 | Per page of a `best` document |
| Actor start | $0.00005 | Apify platform event |

A chargeable page is a physical page read (PDF; the declared page count for DOCX) or 3,000 characters of converted text, whichever is more, counting only the part of the document within the 40,000-token input cap: a dense 30-page contract that fills the cap is charged as the ~47 pages of text it contains, and a 500-page PDF is charged only for the pages the model reads. Each item records `pagesCharged` in hosted mode.

**Worked examples.** 100 three-page invoices on hosted `fast` (the default) ≈ **$2.60** (100 × $0.02 + 300 × $0.002), nothing else to pay; `balanced` ≈ **$21.00**; `best` ≈ **$42.00**. Bring your own key: the same 100 invoices ≈ **$1.00** here plus roughly $0.10 of Claude Haiku 5.5 tokens on your Anthropic bill (about 5,000 input and 800 output tokens per three-page document at $0.10 and $0.50 per million); about **$1.90** on Sonnet 5.5 ($2 and $10 per million) or **$3.80** on Opus 5.5 ($4 and $20 per million). Rules: 1,000 documents ≈ **$4.00**.

**Spending limit and model tiers.** The hosted model is called before its events are charged, so before every model call the Actor checks that your run's maximum total charge still covers the document event plus the per-page events for that document. When it does not, the model is not called: the document gets the rules extraction instead, charged at the rules price, with an `extractionWarning` that names the tier, the amount and the limit to set (the run's `SUMMARY` counts these as `modelSkippedForSpendingLimit`). When not even a rules item fits, the run stops cleanly with a log line and nothing more is charged. A run with no limit set behaves as before. In practice: set the maximum total charge to at least (document price + page price × pages) times the number of documents you want the model to handle.

**How the hosted prices are set.** Against the worst case, not the average. The document is capped at 40,000 input tokens (about 60 pages of text), every call carries about 1,700 fixed tokens of instructions and schema on top, and output is capped at 4,000 tokens per document, so the most one document can cost in tokens is: `fast` 41,700 × $0.10/M + 4,000 × $0.50/M ≈ **$0.0062**; `balanced` 41,700 × $2/M + 4,000 × $10/M ≈ **$0.12**; `best` 41,700 × $4/M + 4,000 × $20/M ≈ **$0.25**. A document that fills the cap holds about 140,000 characters, which is at least 47 chargeable pages whatever its physical layout, so it is charged at least $0.114, $1.09 and $2.18 respectively (document + 47 pages); Apify pays developers 80% of that, so every tier clears its worst-case token and compute cost even at the cap, by about 12×, 7× and 7× (a margin above 85% on every tier), and a typical three-page document costs a fraction of a cent in tokens on `fast`. The per-page events are what keep short documents cheap and long ones covered. Reasoning tokens are kept out of the output cap (thinking is turned off on Haiku and Sonnet; Opus runs at low effort). A model failure is never charged at the hosted price: the item falls back to the rules extraction at $0.004.

### Limits and fair use

- Documents are fetched with plain HTTP from Apify datacenter IPs (up to 25 MB each by default, 50 MB maximum, 20 s download timeout) and converted in memory; conversion of one document stops after 5 minutes and the item says so in `warnings` (`PROCESSING_TIMEOUT`). The Actor runs with 512–1024 MB of memory because the PDF engine caches pages and fonts; default run memory is 512 MB, and `maxConcurrency` should stay at 2 for large PDFs.
- **Time budget.** Each document gets its download time (`downloadTimeoutSecs`, a single deadline for the whole download that is not retried) plus 5 minutes of PDF conversion. A document that cannot be downloaded in time returns a `TIMEOUT` error item, charged at the rules rate like every error item, and a blocked or failing URL is never retried. A model call that takes longer than 60 seconds is cancelled without a retry and the document gets the rules extraction at the rules rate (`modelFallbacksToRules` in the summary counts these).
- Supported inputs: PDF (text layer), DOCX, HTML, Markdown and plain text. Scanned PDFs without a text layer produce a `NO_TEXT` error item and list their pages in `needsOcrPages`; run them through the PDF & DOCX to Markdown Converter (same account) with OCR on, save the Markdown to a key-value store, and pass the `kvs://` reference here. Password-protected PDFs produce `PASSWORD_PROTECTED`.
- Schema: `type: object` at the top, at most 6 levels deep and 200 fields; field types object, array, string, number, integer, boolean, null; `enum`, `description`, `title`, `format`. Numeric and string constraints (`minimum`, `pattern`, `maxLength`…) and `$ref`/`oneOf`/`anyOf` are not enforced; the former are ignored with a warning in the log, the latter are rejected before any charge. The model modes work with schemas of any size up to that cap: the schema is sent to the model in a form that stays within the Anthropic structured-output limits (16 union-typed and 24 optional parameters per request), so a 50- or 150-field schema is extracted by the model, not by the rules fallback.
- Model modes cut the document to `maxInputTokens` (at most 40,000 tokens, about 60 pages) before the call and say so on the item; fields that appear only later in the document are then `null`. Output is capped at 4,000 tokens per document, so very large arrays (hundreds of line items) may be cut: split such documents or use the `rules` extractor, which has no output cap, for the table and the model for the rest.
- The rules extractor is deterministic and language-agnostic in its mechanics (labels, tables, numbers) but its date and amount parsing is tuned to English-language business documents; day-first dates are read correctly only when the day is above 12 or the month is spelled out.
- Only public data is accessed: the URLs you give, your own key-value stores and, in model modes, the Anthropic API. Document and webhook URLs must point at public hosts: a URL (or a redirect, at most 5 hops) that resolves to a loopback, private, link-local or cloud-metadata address is rejected with `INVALID_URL`. Your `anthropicApiKey` is stored encrypted by Apify and is never logged or written to the dataset. Document text is sent to the Anthropic API only in model modes and only to produce the extraction.

### Related Actors

- *PDF & DOCX to Markdown Converter for AI Agents (same account): the same layout engine without a schema, with OCR for scanned pages and RAG chunks, $0.001 per page. Use it first for scans, then feed the Markdown here.*
- *Link 1–2 more sibling Actors from this account once published.*

### Use from AI agents (MCP)

Pin this Actor in the Apify MCP server with one link: `https://mcp.apify.com?tools=owlabee/document-to-json-extractor`, or let the agent find it with the `search-actors` tool. It is pay-per-event with limited permissions, so agents can discover it, inspect the input schema with `fetch-actor-details` and pay per run through agentic payments. Prompts that map directly onto the input:

1. "Extract vendor, invoice number, date, line items and total from these 20 invoice PDFs into JSON with the AI Document to JSON Extractor."
2. "Pull the parties, effective date, term, termination clause and governing law out of this NDA (DOCX link) as a JSON object."
3. "Turn this bank statement PDF into a list of transactions with date, description and amount, using my Anthropic key, and tell me which fields it was unsure about."

The agent receives the same dataset records shown in the output example above.

### FAQ

**Which extractor should I use?** The default, `hosted` on the `fast` tier, fills almost any schema from invoices, forms, statements and short contracts for $0.02 plus $0.002 per page with nothing to set up; move to `balanced` or `best` for long contracts, messy scans converted with OCR, or fields that need reading comprehension ("the party that bears the cost of…"). Use `byok` when you already have an Anthropic key and want to control the model and the token spend yourself. Use `rules` for bulk first passes over well-labeled documents, for the daily reconciliation of invoices whose layout you know, and when a model is not allowed on the data: it is free of model costs, needs no key, and every value it returns is verbatim from the document.

**Does this need proxies or an API key?** No proxies. Documents are downloaded over plain HTTP from Apify's standard infrastructure, and there is no third-party site to scrape. An API key is needed only for `byok` mode; `hosted` and `rules` need nothing.

**How is cost controlled?** Cost = documents × the price of the mode and tier you chose, plus pages × the page price in hosted mode. Keep documents short (split large files), lower `maxInputTokens` in `byok` mode to cut your own token bill, and set the run's maximum total charge; the Actor checks it before every model call and stops cleanly when it is reached. Every model-mode item records `tokensIn`, `tokensOut` and `modelCostUsdEstimate`, and the run's `SUMMARY` record in the key-value store totals them.

**How do I write the schema?** Like any JSON Schema: `{ "type": "object", "properties": { "invoiceNumber": { "type": "string", "description": "The invoice number, usually top right" }, "total": { "type": "number", "description": "Grand total including tax" }, "lineItems": { "type": "array", "items": { "type": "object", "properties": { "description": { "type": "string" }, "quantity": { "type": "integer" }, "amount": { "type": "number" } } } } } }`. Descriptions matter in every mode: the rules extractor matches them against labels, the model reads them as instructions. Use `format: "date"` to get `YYYY-MM-DD`, `enum` for fixed choices, nested objects for parties and addresses.

**What does a scanned PDF produce?** The converter finds no text layer, so the item is a `NO_TEXT` error with the scanned pages in `needsOcrPages`, charged at the rules price. Convert it with OCR using the PDF & DOCX to Markdown Converter (same account), write the Markdown to a key-value store, and pass `kvs://<storeId>/<key>` here; the extraction then works on the OCR text.

**Can an AI agent use this?** Yes; see "Use from AI agents (MCP)" above for the pin link and example prompts. Schedules and webhooks work the same way from the Console or the API.

**What happens on errors?** A document that cannot be downloaded, read or converted produces an error item with a clear code instead of failing the whole run; other documents are unaffected. A model call that fails falls back to the rules extraction (`extractionMethod: "rules-fallback"`, reason in `extractionWarning`) and is charged the rules price, so a hosted run never fails because of the model. Only an unusable schema or a `byok` run with a missing or rejected key stops the run, with a clear message and before any further charge.

**Can I try it for free?** Yes. The Apify free plan comes with monthly platform credit, and the quick-start input above costs a few cents per run, so you can test it on your own inputs before paying anything.

**How do I find this Actor again?** Bookmark it on the Store page (the bookmark icon next to the title); bookmarked Actors are listed in your Console, and the Store page shows new versions as they ship.

### Legal

Use the data in compliance with applicable law and the rights you hold in the documents. The Actor processes only the documents you supply (public URLs, your own key-value stores or inline data); you are responsible for having the right to process them. In model modes the document text is sent to the Anthropic API solely to produce the extraction; in `rules` mode nothing leaves Apify. Extracted values are machine-generated and may contain errors; verify amounts, dates and legal terms against the source before acting on them. The Actor is not a consumer reporting agency and its output must not be used for employment, credit, insurance, or tenant screening decisions.

# Actor input Schema

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

Documents to extract from, one per line: public http(s) URLs of PDF, DOCX, HTML, Markdown or plain-text files, Apify key-value-store references in the form kvs://<storeId>/<key> (use kvs://default/<key> for this run's own store), or data: URLs with inline base64 content. Each entry becomes one JSON object in the dataset. The prefilled PDF (IRS Form W-9) is the small public file used by Apify's daily automated test.

## `schema` (type: `object`):

A JSON Schema (draft-07 subset) describing the object you want per document: `type: object` at the top with `properties`; field types string, number, integer, boolean, array and object; `description` on each field tells the extractor what to look for; `format: date` normalizes dates to YYYY-MM-DD; `enum` restricts values. Fields not found in a document come back as null. The prefilled schema describes a government form; replace it with your own (invoice, contract, statement…).

## `extractor` (type: `string`):

Leave empty for the default (the Console prefills `rules` so Apify's daily automated test never calls a model; clear it or pick a mode): AI extraction by the hosted Claude model (`hosted`) when the Actor has its model key configured, otherwise the free rules extractor. Set `rules` for a free, model-free run (deterministic label matching, tables, dates and amounts; no key), or `byok` to use your own Anthropic API key (tokens billed by Anthropic to you). In hosted mode a model problem never fails the run: the document gets the rules extraction, `extractionMethod: "rules-fallback"` and is charged the rules price.

## `anthropicApiKey` (type: `string`):

Your Anthropic API key, used only when Extractor is `byok`. Stored encrypted as a secret and sent only to api.anthropic.com. Get one at console.anthropic.com.

## `modelTier` (type: `string`):

`fast` (default): Claude Haiku 5.5, cheapest and quick, fine for invoices, forms and statements. `balanced`: Claude Sonnet 5.5, better on long contracts and messy layouts. `best`: Claude Opus 5.5 for the hardest documents. In hosted mode the tier sets the per-document and per-page prices ($0.02 + $0.002/page, $0.15 + $0.02/page, $0.30 + $0.04/page); in byok mode the tier only changes what Anthropic bills you.

## `instructions` (type: `string`):

Extra guidance for the model, in plain language: conventions ("amounts in EUR without VAT", "dates are day-first"), what to ignore ("skip the cover letter"), or how to pick between candidates ("the vendor is the party at the top left"). Ignored by the rules extractor.

## `maxInputTokens` (type: `integer`):

Documents longer than this (estimated) are cut to the cap before the model call and the item is marked `inputTruncated: true`. 40,000 tokens is roughly 60 pages of text. Lower it to cut your own token cost in byok mode.

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

Documents processed in parallel (downloads, conversion and model calls). Large PDFs need memory: keep it at 2 on 1 GB unless the files are small.

## `includeMarkdown` (type: `boolean`):

Also write the document's full converted Markdown on each item as `markdown` (capped at 1,000,000 characters). Off by default; every item always carries a 1,000-character `textPreview`.

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

Files larger than this are rejected with a TOO_LARGE error item instead of being downloaded. Raise it up to 50 MB for large files; bigger files need more memory.

## `downloadTimeoutSecs` (type: `integer`):

Maximum time to download one file before it is recorded as a TIMEOUT error item.

## `webhookUrl` (type: `string`):

If set, the Actor POSTs a JSON summary of the run (documents, errors, pages, fields filled, model token totals, charges) to this URL when it finishes — 10 s timeout, 2 retries. Failures are logged, never fatal.

## Actor input object example

```json
{
  "urls": [
    "https://www.irs.gov/pub/irs-pdf/fw9.pdf"
  ],
  "schema": {
    "type": "object",
    "properties": {
      "title": {
        "type": "string",
        "description": "Title of the form"
      },
      "formNumber": {
        "type": "string",
        "description": "The form number, e.g. W-9 or 1040"
      },
      "revisionDate": {
        "type": "string",
        "format": "date",
        "description": "Revision date of the form"
      },
      "issuer": {
        "type": "string",
        "description": "Agency or organization that issues the form"
      },
      "fields": {
        "type": "array",
        "description": "The numbered fields a filer completes",
        "items": {
          "type": "object",
          "properties": {
            "label": {
              "type": "string"
            },
            "description": {
              "type": "string"
            }
          }
        }
      }
    }
  },
  "extractor": "rules",
  "modelTier": "fast",
  "maxInputTokens": 40000,
  "maxConcurrency": 2,
  "includeMarkdown": false,
  "maxFileSizeMb": 25,
  "downloadTimeoutSecs": 20
}
```

# Actor output Schema

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

One item per document in the default dataset (url, filename, pages, title, extractionMethod, modelTier, data, confidence, overallConfidence, filledFields, totalFields, missingFields, textPreview; error items carry error and errorMessage). Fields are described in dataset_schema.json.

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

SUMMARY record in the run's default key-value store: documents extracted, errors, pages, fields filled, model token totals and cost estimate, charges and webhook delivery status, written when the run finishes.

# 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"
    ],
    "schema": {
        "type": "object",
        "properties": {
            "title": {
                "type": "string",
                "description": "Title of the form"
            },
            "formNumber": {
                "type": "string",
                "description": "The form number, e.g. W-9 or 1040"
            },
            "revisionDate": {
                "type": "string",
                "format": "date",
                "description": "Revision date of the form"
            },
            "issuer": {
                "type": "string",
                "description": "Agency or organization that issues the form"
            },
            "fields": {
                "type": "array",
                "description": "The numbered fields a filer completes",
                "items": {
                    "type": "object",
                    "properties": {
                        "label": {
                            "type": "string"
                        },
                        "description": {
                            "type": "string"
                        }
                    }
                }
            }
        }
    },
    "extractor": "rules",
    "includeMarkdown": false
};

// Run the Actor and wait for it to finish
const run = await client.actor("owlabee/document-to-json-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"],
    "schema": {
        "type": "object",
        "properties": {
            "title": {
                "type": "string",
                "description": "Title of the form",
            },
            "formNumber": {
                "type": "string",
                "description": "The form number, e.g. W-9 or 1040",
            },
            "revisionDate": {
                "type": "string",
                "format": "date",
                "description": "Revision date of the form",
            },
            "issuer": {
                "type": "string",
                "description": "Agency or organization that issues the form",
            },
            "fields": {
                "type": "array",
                "description": "The numbered fields a filer completes",
                "items": {
                    "type": "object",
                    "properties": {
                        "label": { "type": "string" },
                        "description": { "type": "string" },
                    },
                },
            },
        },
    },
    "extractor": "rules",
    "includeMarkdown": False,
}

# Run the Actor and wait for it to finish
run = client.actor("owlabee/document-to-json-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"
  ],
  "schema": {
    "type": "object",
    "properties": {
      "title": {
        "type": "string",
        "description": "Title of the form"
      },
      "formNumber": {
        "type": "string",
        "description": "The form number, e.g. W-9 or 1040"
      },
      "revisionDate": {
        "type": "string",
        "format": "date",
        "description": "Revision date of the form"
      },
      "issuer": {
        "type": "string",
        "description": "Agency or organization that issues the form"
      },
      "fields": {
        "type": "array",
        "description": "The numbered fields a filer completes",
        "items": {
          "type": "object",
          "properties": {
            "label": {
              "type": "string"
            },
            "description": {
              "type": "string"
            }
          }
        }
      }
    }
  },
  "extractor": "rules",
  "includeMarkdown": false
}' |
apify call owlabee/document-to-json-extractor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,owlabee/document-to-json-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/lGDzgsmB8XYeU2NFi/builds/DE4CNhiGuqs3B3NMd/openapi.json
