Documents and media

PDF to JSON with Your Own Schema: Invoices, Forms, Quotes

Pass document URLs and a JSON Schema describing the object you want (invoice number, line items, totals, form fields, dates) and get back one JSON object per document in exactly that shape, with a confidence score per field and a list of fields that were not found.

Open the Actor on Apify Store

PDF & Document to JSON Extractor (AI) — Invoices, Forms. Pay per result; see the Store page for current pricing.

The source and its limits

Documents are read from public URLs, your own key-value stores (kvs://) or data: URLs: PDF with a text layer, DOCX, HTML, Markdown and plain text. They go through a layout engine first (headings, tables, lists and page markers), so table rows stay rows and labels stay next to their values.

The schema input is a JSON Schema (draft-07 subset): type: object at the top, field types string, number, integer, boolean, array and object, with description, format (date, date-time, email, uri) and enum. Nested objects and arrays of objects work; $ref, oneOf and anyOf do not.

Three extractors

  • rules: no language model. It collects label/value pairs, tables, numbered form fields, dates, amounts, emails, phone numbers and URLs and matches them to your field names and descriptions, coercing types. Every value is verbatim from the document.
  • byok: Claude fills the schema with the Anthropic API key you supply, on a fast, balanced or best tier. Items record tokens used.
  • hosted: Claude on the Actor's own key. This is what an empty extractor means when the Actor's model key is configured. If the model cannot be reached, the document gets the rules extraction instead, marked rules-fallback.

In model modes, document text is sent to the Anthropic API only to produce the extraction. Each mode is priced differently; see the Store page.

Limits worth knowing

  • Scanned PDFs without a text layer return a NO_TEXT error item. Convert them first with the PDF to Markdown Actor with OCR on, save the Markdown to a key-value store, and pass the kvs:// reference.
  • Schemas can be up to 6 levels deep and 200 fields. Model modes cut documents at maxInputTokens (at most 40,000, about 60 pages) and cap output at 4,000 tokens per document, so very long line-item tables may be cut.
  • The rules extractor's date and amount parsing is tuned to English-language business documents.
  • URLs that resolve to private or local network addresses are rejected.

Fields you get

FieldWhat it holds
dataThe filled object, in the shape of your schema; fields not found are null
confidence, overallConfidenceA score per field and for the whole document
filledFields, totalFields, missingFieldsWhat was and was not found
extractionMethod, extractionNotesrules, byok, hosted or rules-fallback, and where each value came from
lowConfidenceFieldsModel modes: fields the model was unsure about
url, filename, mimeType, pages, title, authorThe source document
textPreview, markdownA 1,000-character preview; the full text with includeMarkdown
needsOcrPages, warningsPages without text and conversion notes

Example output

IRS Form W-9 extracted with the rules extractor and the quick-start schema (from the Actor's README; shortened):

{
  "recordType": "document",
  "url": "https://www.irs.gov/pub/irs-pdf/fw9.pdf",
  "filename": "fw9.pdf",
  "pages": 6,
  "extractionMethod": "rules",
  "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. …"
      },
      {
        "label": "Business name/disregarded entity name, if different from above",
        "description": null
      },
      {
        "label": "City, state, and ZIP code",
        "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": [],
  "fetchedAt": "2026-10-17T10:22:02.464Z"
}

Use cases

Invoices to line items

Describe vendor, invoice number, dates, totals and a lineItems array once, then send every invoice through the same schema. Field descriptions guide both the rules matcher and the model.

Batch form fields

Run several government forms through one schema to get each form's number, revision date, issuer and numbered fields as JSON.

Supplier quotes and specifications

The Actor's task examples turn a supplier quote into line items and a roofing specification PDF into a takeoff of the roof system and materials.

How to run it

You need an Apify account and its API token (Console → Settings → API & Integrations). Each run is billed per result to your account; set a maximum total charge for the run to cap spend.

With curl

The sync endpoint starts a run, waits up to 300 seconds and returns the dataset items. Two IRS form PDFs through one small schema with the rules extractor (no model):

export APIFY_TOKEN=<your Apify API token>

curl -sS -X POST \
  "https://api.apify.com/v2/acts/owlabee~pdf-to-json/run-sync-get-dataset-items" \
  -H "Authorization: Bearer ${APIFY_TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{
  "urls": [
    "https://www.irs.gov/pub/irs-pdf/fw9.pdf",
    "https://www.irs.gov/pub/irs-pdf/fw4.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"
      }
    }
  },
  "extractor": "rules",
  "includeMarkdown": false
}'

From an AI agent (MCP)

Apify hosts an MCP server. This config exposes only this Actor to the agent:

{
  "mcpServers": {
    "owlabee-pdf-to-json": {
      "url": "https://mcp.apify.com/?tools=owlabee/pdf-to-json",
      "headers": {
        "Authorization": "Bearer ${APIFY_TOKEN}"
      }
    }
  }
}

Then ask, for example: Extract the invoice number, invoice date, vendor, total and line items from these three PDF invoices as JSON.

Python, Node.js and more examples

Runnable curl.sh, python.py, node.mjs and mcp.json files for this Actor are in examples/pdf-to-json on GitHub.

FAQ

Which extractor should I use?

Use rules for well-labeled documents with a known layout, bulk first passes, and data that must not go to a model. Use a model mode for fields that need reading comprehension or long, messy documents.

How do I write the schema?

As ordinary JSON Schema. Descriptions matter in every mode: the rules extractor matches them against labels, and the model reads them as instructions. Use format: "date" for ISO dates and enum for fixed choices.

Is my Anthropic key safe in byok mode?

It is stored as an encrypted secret input, sent only to api.anthropic.com, and never logged or written to the dataset.

What happens if the model fails?

The document gets the rules extraction, marked rules-fallback with the reason, so a run does not fail because of the model.

Does it work on scanned documents?

Not directly. Run them through the PDF to Markdown Actor with OCR first, then pass the saved Markdown by kvs:// reference.