PDF Table Extractor: Cross-Page Tables to CSV & JSON avatar

PDF Table Extractor: Cross-Page Tables to CSV & JSON

Pricing

Pay per usage

Go to Apify Store
PDF Table Extractor: Cross-Page Tables to CSV & JSON

PDF Table Extractor: Cross-Page Tables to CSV & JSON

Extract tables from PDFs and rejoin the ones a page break cut in half. Reads ruled and borderless tables, drops the header the file repeats on every page, names each column type, and returns one clean grid per table as JSON and CSV.

Pricing

Pay per usage

Rating

0.0

(0)

Developer

Ai-Q Labs

Ai-Q Labs

Maintained by Community

Actor stats

0

Bookmarked

3

Total users

2

Monthly active users

33 minutes ago

Last modified

Share

Extract tables from PDFs — and put back together the ones a page break cut in half.

Every other PDF table extractor hands you a table per page. A forty-page statistical release becomes forty fragments, each carrying its own copy of the header, and you finish the job by hand. This one recognises that a fragment continues overleaf, joins it to the one before, and deletes the header the file repeated.

Free to run. No key, no account beyond Apify, no per-page charge.


What it does

Give it links to PDF files. You get one row per table, with the grid as JSON and as CSV.

  • Rejoins tables split across page breaks and drops the repeated header rows
  • Reads ruled tables from the lines the file draws, including tables that rule only their columns and leave the rows to whitespace
  • Reads borderless tables from column gaps that stay empty on every row
  • Finds the header, including headers set two or three lines deep, and flattens "June" over "2025" into June 2025
  • Names each column's type — number, currency, percent, date or text — after parsing 1,234, (1,234) as negative, 1.234,56, 98.6%, $12.50 and footnote markers
  • Reports what it is unsure about: spanning cells, sparse grids, tables found from whitespace alone, and grids with no numeric column at all

Why this one

The PDF table Actors already on Apify were read before this was built. Between them they offer text, tables, metadata, accuracy scores and OCR. Not one mentions what happens when a table runs onto the next page — and every long table does.

OthersThis Actor
Table split across pagesTwo tables, header twiceOne table, header once
Which pages a table came frompages: [28, 29, 30, 31]
The document's captioncaption: "Table B-1. Employees on nonfarm payrolls…"
Column types["text","number","number",…]
PricePay per PDF or per pageFree

Measured, not asserted

Both of these documents mark their own continuation pages, which gives a ground truth the run can be scored against. The test suite checks the extractor's answer against that list rather than against a number nobody can verify.

US Bureau of Labor Statistics, The Employment Situation (39 pages)

  • pages the document prints "Continued" on: 13, 29, 30, 31
  • pages absorbed by rejoining: 13, 29, 30, 31 — every one, and no others
  • Table B-1 comes out as one table of 247 rows across four pages, with 12 repeated header rows removed
  • Tables A-1, A-2 and A-3 have identical ten-column layouts and byte-identical headers. They stay three tables, because the caption says so.

Federal Reserve H.4.1 (11 pages, mixed landscape and portrait)

  • three continuation pages hold a table; all three are absorbed
  • Table 6 is rejoined even though it sits 8.6 pt further right on its second page
  • Table 1A, which shares table 1's five columns in the same places, is not swallowed

Unit tests: 44 passing. Live tests against real PDFs: 36 passing.

Two details that decide whether this works at all

A vertical rule is often not a vertical line. In the BLS release every vertical rule in every table is drawn as a horizontal stroke 0.6 pt long with a line width of 491 pt. Stroking a segment paints a band of lineWidth around it, so a short fat stroke is a tall thin bar. Read the segment and ignore the width and you find zero vertical rules on a page full of them.

Rules say where the columns are; text says where the rows are. A typical BLS data page draws nine vertical rules and four horizontal ones — the verticals separate every column, the horizontals mark only the top, the header and the bottom. Forty data rows have no rule between them. Waiting for a full grid finds nothing; assuming rows are ruled finds three rows where there are forty. Which of the two applies is counted per table, not assumed.

Input

FieldDefaultWhat it does
pdfUrlsDirect links to PDF files, one per line
mergeAcrossPagestrueRejoin tables split by a page break
useWhitespaceTablestrueAlso find tables that draw no lines
onlyDataTablesfalseKeep only tables with a numeric, currency, percent or date column
includeCsvtrueAdd a ready-to-save CSV string per table
includeRecordsfalseAdd one object per row, keyed by the detected header
minRows / minColumns2 / 2Floor on what counts as a table
maxPages200Pages read per PDF
maxRowsPerTable5000Rows kept per table
maxFileMb50Download stops here rather than taking the run down
maxConcurrency5PDFs at a time

Output

One row per table:

Trimmed from a real run over the BLS release:

{
"filename": "empsit.pdf",
"caption": "Table B-1. Employees on nonfarm payrolls by industry sector and selected industry detail",
"tableId": "empsit.pdf#28.1",
"pages": [28, 29, 30, 31],
"splitAcrossPages": true,
"pageFragments": 4,
"repeatedHeaderRowsDropped": 12,
"detectedBy": "rules",
"rowCount": 247,
"columnCount": 10,
"header": ["Industry", "June", "Apr.", "Not seasonally adjusted May p", "June p",
"June", "Apr.", "Seasonally adjusted May p", "June p", "Change from:"],
"columnTypes": ["text", "number", "number", "number", "number",
"number", "number", "number", "number", "number"],
"rows": [["Total nonfarm . . .", "159,299", "158,713", "159,398", "159,830",
"158,478", "158,798", "158,927", "158,984", "57"]],
"csv": "Industry,June,Apr.,Not seasonally adjusted May p,…",
"confidence": 0.93,
"status": "warning",
"issues": ["spanning_cells"],
"notes": ["This table was split across pages 28, 29, 30, 31 and has been rejoined into one grid, dropping 12 repeated header row(s)."]
}

The key-value store gets a SUMMARY with the totals and a list of every table that was rejoined, with the pages it came from.

Issue codes

CodeMeaning
spanning_cellsA cell covers more than one column; its text is in the leftmost
no_header_detectedThe columns are unnamed
no_ruling_linesFound from whitespace alone — check the split before trusting it
no_numeric_columnsEvery column reads as text; may be a form rather than data
sparse_gridUnder half the cells hold text
rows_truncated / page_budget_reachedA limit was reached; raise it for the rest
some_pages_have_no_textThose pages are scans; any table on them is an image
rotated_pages_skippedRuling lines were not used on rotated pages

Limits, stated plainly

  • No OCR. A scanned page has no text to read. It is reported, not guessed at.
  • Rotated pages are read for text but not for rules, so a table there may be missed.
  • Row-spanning cells are detected as column spans only; a cell that covers two rows lands in the upper one.
  • Headers deeper than four lines keep their first four; the rest stay as body rows. The BLS table B-1 header is seven lines deep and shows this.
  • A tax form is a grid, and it will be found as one. onlyDataTables is there for that.
  • Fragments are only joined when their columns line up and the caption or the repeated header agrees. A document that does neither keeps its fragments apart.

Built by Ai-Q Labs. Other Actors: PDF Inspector, PDF to Text & Markdown, HTTP Status Checker, Domain Availability Checker, Tech Stack Detector, Broken Link Checker, Bulk Domain Checker, AI Crawler Checker.