PDF to JSON Extractor — Tables, Fields & Structure avatar

PDF to JSON Extractor — Tables, Fields & Structure

Pricing

from $1.40 / 1,000 page extractions

Go to Apify Store
PDF to JSON Extractor — Tables, Fields & Structure

PDF to JSON Extractor — Tables, Fields & Structure

PDF to JSON API: tables as real rows and columns, headings, paragraphs in true reading order, metadata, form fields and key fields (invoice number, dates, totals, IBAN, VAT). Also outputs Markdown for RAG. Pay per page extracted, capped per document.

Pricing

from $1.40 / 1,000 page extractions

Rating

0.0

(0)

Developer

Power On Labs

Power On Labs

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

a day ago

Last modified

Share

Turn a PDF into structured JSON: tables as real rows and columns, headings and paragraphs in true reading order, document metadata, form fields, and the key values you actually want — invoice number, dates, totals, IBAN, VAT ID.

Most PDF extractors hand back a wall of text. That is fine if a human is going to read it. It is useless if a script has to find the total of an invoice, or if a retrieval system has to keep a financial table intact. This Actor keeps the structure.

{ "pdfUrls": ["https://example.com/invoice.pdf"] }

What comes back

Tables as rows and columnsEvery table becomes a 2-D array with a detected header row, plus the page and the bounding box it came from. Borderless tables are found too: detection reads how the text lines up, not the drawn rules — so the tables that line-based tools miss come through.
True reading orderA two-column paper, a newsletter, a report with a sidebar: the text comes out in the order a person reads it, not in the order the file happens to draw it. Recursive whitespace segmentation separates columns from full-width headings before anything is read.
Headings and paragraphsBlocks are typed — heading (with a level), paragraph, listItem — using the document's own type scale, so an 8 pt body with 11 pt titles is read as well as a 11 pt body with 24 pt titles.
Key fields, checkedInvoice and order numbers, dates, totals and amounts with their currency, emails, phones, IBANs, VAT IDs. IBANs are validated with the mod-97 checksum and Italian VAT numbers with their check digit, so an invalid one is dropped instead of returned.
Dates resolved, or flagged03/04/2026 is 3 April in Europe and 4 March in the US. If the document elsewhere shows its convention, the date is resolved and marked assumedOrder. If it does not, iso stays null and both readings are returned. A wrong due date is worse than a missing one.
Form fieldsAcroForm fields with names, types and filled-in values — for W-9s, tax forms, applications, contracts.
Metadata and bookmarksTitle, author, subject, creator, producer, creation and modification dates as ISO timestamps, page count, PDF version, encryption and signature flags, plus the bookmark tree.
Markdown for RAGThe same structure rendered as Markdown, headings and pipe tables included. A table pasted as flat text is noise inside a retrieval system; the same table in Markdown stays queryable.
Scanned PDFs are labelled, not fakedA page with no text layer is reported as such (isScanned, needsOcr) and is not charged. See the limits below.

Pricing — you pay for pages, and only the ones that worked

$0.002 per page extracted. Nothing else. No start fee, no per-document fee.

  • A page that has no text layer (a scan) is not charged.
  • A file that fails — dead link, wrong password, not a PDF — is not charged.
  • Pages skipped with firstPage / maxPages are not charged.
  • At most 40 pages are charged per document. A 500-page manual costs the same as a 40-page one: $0.08. Long documents stay predictable.

A five-page invoice costs one cent.

Quick start

Minimal input:

{ "pdfUrls": ["https://example.com/invoice.pdf"] }

With options:

{
"pdfUrls": [
{ "url": "https://example.com/statement.pdf", "password": "hunter2", "name": "march-statement" },
"https://example.com/report.pdf"
],
"detectTables": true,
"extractKeyFields": true,
"outputFormats": ["json", "markdown", "csv"],
"maxPages": 20
}

From the JavaScript client:

import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: '<APIFY_TOKEN>' });
const run = await client.actor('power_on/pdf-to-json-extractor').call({
pdfUrls: ['https://example.com/invoice.pdf'],
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items[0].fields.totals); // [{ label: 'total due', value: 1220, currency: 'EUR' }]
console.log(items[0].tables[0].rows); // [['Item', 'Qty', 'Price'], ['Widget', '2', '9.90']]

From Python:

from apify_client import ApifyClient
client = ApifyClient("<APIFY_TOKEN>")
run = client.actor("power_on/pdf-to-json-extractor").call(
run_input={"pdfUrls": ["https://example.com/invoice.pdf"]}
)
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
print(item["fields"], item["tables"])

It also works with no code at all through Apify's Zapier, Make and n8n integrations, or straight from the REST API.

Where the PDFs can come from

  • A public URL — the ordinary case.
  • A private endpoint — put an Authorization header in Extra HTTP headers.
  • A file you have on disk — upload it to an Apify key-value store and pass the record URL, or inline it as a base64 data URI: data:application/pdf;base64,JVBERi0xLjQK….

What comes out

One dataset row per input file, plus the files themselves in the key-value store. Row shape:

{
"url": "https://example.com/invoice.pdf",
"ok": true,
"pageCount": 3,
"pagesParsed": 3,
"pagesWithText": 3,
"pagesCharged": 3,
"isScanned": false,
"needsOcr": false,
"tableCount": 2,
"metadata": { "title": "Invoice 42", "author": "Acme", "createdAt": "2026-03-14T09:12:00.000Z", "pageCount": 3, "encrypted": false, "hasAcroForm": false },
"fields": {
"identifiers": { "invoiceNumber": "INV-2026-0042", "orderNumber": "PO-99812" },
"totals": [{ "label": "total due", "value": 1220, "currency": "EUR", "raw": "EUR 1.220,00" }],
"dates": [{ "raw": "03/04/2026", "iso": "2026-04-03", "assumedOrder": "day-first", "readings": ["2026-04-03", "2026-03-04"] }],
"ibans": ["IT60X0542811101000000123456"],
"emails": ["billing@acme.example"]
},
"tables": [
{
"page": 2,
"rowCount": 4,
"columnCount": 3,
"header": ["Item", "Qty", "Price"],
"rows": [["Item", "Qty", "Price"], ["Widget", "2", "9.90"]],
"bbox": { "x0": 72, "y0": 310, "x1": 523, "y1": 402 }
}
],
"pages": [
{
"page": 1,
"width": 595.3,
"height": 841.9,
"rotation": 0,
"hasTextLayer": true,
"text": "…",
"blocks": [{ "type": "heading", "level": 1, "text": "Invoice", "bbox": { "x0": 72, "y0": 60, "x1": 240, "y1": 84 } }],
"tables": []
}
],
"jsonUrl": "https://api.apify.com/v2/key-value-stores/…/records/001-invoice.json?signature=…",
"markdownUrl": "…"
}

rows always contains every row of the table, header included; header is a convenience copy of the header row when one was detected.

The file links are signed, so they open in a browser or from curl without a token.

When a file goes wrong

One broken PDF never takes down the run. Every input gets a row, and a failed one carries ok: false with an error and an errorCode:

errorCodeWhat happened
PASSWORD_REQUIREDThe PDF is encrypted and no password was given
PASSWORD_WRONGThe password given does not open it
INVALID_PDFTruncated, corrupt, or the URL returned an HTML page instead of a file
EXTRACTION_FAILEDNetwork failure, timeout, size limit, HTTP error

A page that fails on its own is recorded in pageErrors and the rest of the document is still extracted.

Known limits — read this before you buy

  • No OCR. A scanned PDF has no text layer, and this Actor does not run optical character recognition on it. It detects the situation, returns isScanned: true and needsOcr, and does not charge you for those pages. If your documents are scans, you need an OCR tool, not this one.
  • Formulas and code listings can occasionally be reported as small tables.
  • Tables split across a page break come back as two tables, one per page.
  • Right-to-left scripts are extracted but the reading order within a line follows the file's own text order.

These are stated on purpose. It is cheaper for both of us if you find out here rather than after a run.

Everything you can set

OptionDefaultWhat it does
pdfUrlsURLs, {url, password, name} objects, or base64 data URIs
detectTablestrueFind tables and return them as rows and columns
extractKeyFieldstrueInvoice numbers, dates, totals, IBAN, VAT, emails, phones
includeFormFieldstrueAcroForm fields with their values
includeLinkstrueHyperlink annotations per page
includeLinesfalseEvery text line with its bounding box
outputFormats["json","markdown"]Also text and csv
password""Applied to files that carry no password of their own
firstPage / maxPages1 / 0Read a slice of the document; skipped pages are free
inlineFullResult"auto"Embed the full structure in the dataset row, or link to it
concurrency3Documents in parallel
retries2Retries on network failures only
timeoutSecs120Download timeout
maxFileSizeMb100Larger files are refused with an explanation
headers{}Extra HTTP headers for private endpoints
proxyConfigurationoffFor hosts that block datacenter addresses

Good uses

Invoice and receipt automation · feeding contracts and reports into a RAG index · pulling financial tables out of annual reports and statements · reading filled-in tax and application forms · turning research papers into clean Markdown · migrating a document archive into a database.


Built by Power On Labs. Something extracted wrong? Open an issue on the Actor with the PDF or a link to it and it gets fixed — the extraction rules are ours, not a third-party library's default settings.