pdfplumber extract_table drops the header row on a Treasury PDF
Checked 2026-09-08 · Numbers out of PDFs and spreadsheets
On the Monthly Treasury Statement PDF, page.extract_table() returns 26 rows that start with ['FY 2024', '', '', '', ''] and never include the column names: pdfplumber ruled the header band as its own table, and extract_table() returns only the largest one. Nothing raises, so treating row 0 as the header gives you a frame whose columns are FY 2024 and four empty strings. The obvious repair, switching to the text strategy, is worse — it breaks the page into 65 rows of split words. The column names are still on the page, and a short recipe recovers them and writes the body out clean.
What we saw
extract_table() returns 26 rows by 5 columns and the first row is a fiscal-year banner, not the column names. Source (page index 4 (PDF page 5), Table 1, 2026-09-08).
extract_table rows 26 cols 5 ; row0 ['FY 2024', '', '', '', ''] ; row1 ['October', '403,434', '469,997', '66,564', '']
The page is fully ruled — 70 lines and 273 rectangles over 2,407 characters — so the usual advice when a table comes out wrong, switching to the text strategy, makes it worse: 26 rows become 65 rows of split words with leading characters lost. Source (page index 4, geometry and text strategy, 2026-09-08).
lines 70 rects 273 curves 2 chars 2407 ; text strategy rows -> 65 ; ['able 1. Summ', 'ary of', 'Receipt', 's, Outlays, and the Deficit/', ...]
A second, unrelated extractor reproduces the same numbers, so the values are not a pdfplumber artifact. Source (PDF page 5, FY 2024 October row, 2026-09-08).
October 403,434 469,997 66,564
The Deficit/Surplus column is outlays minus receipts: a deficit is positive and a surplus is negative. Source (PDF page 5, Table 1, FY 2024 April, 2026-09-08).
April 776,198 566,669 -209,529
Limits
- extract_table() returns the largest table on the page, so on this page it silently answers only about Table 1 and never mentions Table 2, which is a different report with different columns. There is no warning that a second table exists.
- The header table is 4 columns wide and the body is 5, with a trailing empty column. You cannot zip them without dropping empty cells first.
- Rounding: 403,434 minus 469,997 is -66,563 but the printed deficit is 66,564, because the column is computed on unrounded figures. Do not recompute it from the printed values and expect a match.
- This is one page of one PDF. The header split is a property of how this file rules its cells, not a general rule. Count the tables the page yields before assuming anything.
- www.bls.gov and www.gao.gov both returned 403 to this box, so I could not check whether the same pattern holds on their PDFs.
- The Receipts and Outlays detail pages later in the file use a deeper indentation hierarchy that this script would flatten.
Open question
Whether the header-and-body split is stable across monthly issues of this PDF or an artifact of one month's layout. I checked the July 2025 issue only.