SF-133 as a table: the MAX portal XLSX and its Raw Data sheet

Checked 2026-09-08 · Federal budget and spending

SF-133 is published as machine-readable data, not only as PDFs: OMB's public MAX portal serves one .xlsx per agency per fiscal year, no login, and its Raw Data sheet is a flat TAFS x line-number table. The two things that cost time are the per-year attachment folder id, which is not derivable, and the twelve amount columns, which are not in calendar order.

What we saw

The FY2026 year page is live, anonymous, and spells the form 'SF 133 Reports' in the plural; the singular URL 404s. Source (page fetch, no cookies or credentials, 2026-09-08).

http=200 size=94030 ct=text/html; charset=UTF-8   (singular 'Report' form: http=404 size=4417)

The FY2026 page links 29 monthly per-agency workbooks, all under one attachment folder id 2692285459. Source (href attributes on the year page, 2026-09-08).

201 attachments/2692285459/
29 distinct FY2026_SF133_MONTHLY_<Agency>.xlsx filenames
last updated on 2026-08-20

The Raw Data sheet has 44 columns and one row per TAFS x SF-133 line, and the layout is the same across agencies. Source (sheet 'Raw Data', header row, 2026-09-08).

nsf.xlsx cols: 44 rows: 1886
edu.xlsx cols: 44 rows: 8102

Periods not yet reported are stored as 0, not as empty cells. Source (sheet 'Raw Data', columns AMT_JUL / AMT_AUG / AMT4, 2026-09-08).

nsf.xlsx AMT_AUG nonzero rows: 0  AMT4 nonzero: 0  AMT_JUL nonzero: 1795

The command

curl -sS -o y2026.html -w "http=%{http_code} size=%{size_download} ct=%{content_type}\n" "https://portal.max.gov/portal/document/SF133/Budget/FY%202026%20-%20SF%20133%20Reports%20on%20Budget%20Execution%20and%20Budgetary%20Resources.html"
http=200 size=94030 ct=text/html; charset=UTF-8

Checked 2026-09-08.

grep -oE 'attachments/[0-9]+/' y2026.html | sort | uniq -c
grep -oE 'FY2026_SF133_MONTHLY_[A-Za-z_&.-]+\.xlsx' y2026.html | sort -u | wc -l
    201 attachments/2692285459/
29

Checked 2026-09-08.

curl -sS -o nsf.xlsx -w "http=%{http_code} ct=%{content_type} size=%{size_download}\n" "https://portal.max.gov/portal/document/SF133/Budget/attachments/2692285459/FY2026_SF133_MONTHLY_National_Science_Foundation.xlsx"
http=200 ct=application/vnd.openxmlformats-officedocument.spreadsheetml.sheet size=3261053

Checked 2026-09-08.

python3 -c "import openpyxl,warnings;warnings.simplefilter('ignore')
wb=openpyxl.load_workbook('nsf.xlsx',read_only=True,data_only=True)
print(wb.sheetnames)
ws=wb['Raw Data'];it=ws.iter_rows(values_only=True);hdr=list(next(it));print(len(hdr));print(hdr)"
['Instructions', 'TAFS detail', 'Account Total', 'Bureau Total', 'Agency Total', 'Raw Data']
44
['RPT_YR', 'AGENCY', 'BUREAU', 'OMB_ACCT', 'TRAG', 'ALLOC', 'TRACCT', 'FY1', 'FY2', 'STAT', 'CRED_IND', 'COHORT', 'LINENO', 'LINE_DESC', 'CAT_B', 'F2_USER_ID', 'TAFS', 'AGENCY_TITLE', 'LAST_UPDATED', 'SECTION', 'SECTION_NO', 'LINE_TYPE', 'TAFS_ACCT', 'BUREAU_TITLE', 'OMB_ACCOUNT', 'FIN_ACCTS', 'F2_USER', 'AMT_NOV', 'AMT_JAN', 'AMT_FEB', 'AMT_APR', 'AMT_MAY', 'AMT_JUL', 'AMT_AUG', 'AGEUP', 'AMT_OCT', 'AMT1', 'AMT2', 'AMT3', 'AMT4', 'LINE_DESC_SHORT', 'PGM_CAT', 'PGM_CAT_STUB', 'CAT_B_STUB']

Checked 2026-09-08.

# totals for the lines people actually want, NSF and Education, July column
python3 -c "...sum r['AMT_JUL'] grouped by str(r['LINENO']).strip()..."
  nsf.xlsx line 1910 AMT_JUL = 10201180822.220001
  nsf.xlsx line 2190 AMT_JUL = 4672418781.5
  nsf.xlsx line 2490 AMT_JUL = 5528762040.72
  nsf.xlsx line 2500 AMT_JUL = 10201180822.220001
  nsf.xlsx line 3020 AMT_JUL = -7122751225.12
  edu.xlsx line 1910 AMT_JUL = 451051139539.42004
  edu.xlsx line 2190 AMT_JUL = 277570454435.62
  edu.xlsx line 2490 AMT_JUL = 173480685103.8
  edu.xlsx line 2500 AMT_JUL = 451051139539.42004
  edu.xlsx line 3020 AMT_JUL = -288764257344.0

Checked 2026-09-08.

Amount columnReporting period it holds
AMT_OCTOctober
AMT_NOVNovember
AMT1December (Q1)
AMT_JANJanuary
AMT_FEBFebruary
AMT2March (Q2)
AMT_APRApril
AMT_MAYMay
AMT3June (Q3)
AMT_JULJuly
AMT_AUGAugust
AMT4September (Q4)

Limits

Open question

The attachment folder id changes every year and I found no way to derive it. Scraping the year page is the only method checked.

Plain text