Registry
IRS Form W-4 — programmatic fill compatibility
Employee's Withholding Certificate · Internal Revenue Service (IRS) · 2026 edition. Tested 2026-08-06 on the unmodified upstream blank — every number below comes from a recorded run, not from documentation.
Verdict at a glance
| Tool | Version | Result | Detail |
|---|---|---|---|
| pdfops API | hosted, tested 2026-08-06 | verified + deterministic flatten | 48/48 supported fields landed; flatten=true byte-deterministic (sha256 e7201382e5b8…) |
| pdf-lib | 1.17.1 | verified (48/48) | 48 fields filled in-process; XFA layer dropped on save |
| @cantoo/pdf-lib (fork) | 2.6.5 | verified (48/48) | 48 fields filled in-process; XFA layer dropped on save |
| pypdf | 6.15.0 | text fields verified (43/43) | 43 text fields written and re-read (text fields only) |
| pdftk-java | 3.3.3 | verified (48/48) | 48 fields filled via FDF and re-dumped |
| qpdf --check | 11.9.0 | structurally clean | errors that qpdf cannot detect |
| poppler render check | 24.02.0 | values render (43/43) | 43/43 filled text values present in pdftotext output of the pdf-lib-filled file; page-1 raster sha256 b1199beefa49… |
File facts
- Source: https://www.irs.gov/pub/irs-pdf/fw4.pdf (issuer page: here)
- Blank tested: irs-w-4.pdf · 204 KB · sha256
92444d8856ce55d9e25dca8b6d1420634fc68b11e1ab1f760916ea29ddd312b2 - License: US federal government work — public domain (17 U.S.C. § 105). The hash-verified blank is redistributed here unmodified.
- Structure: 5 pages · AcroForm + XFA layer (hybrid)
- Fields: 48 (43 text, 5 checkbox)
Quirks & limitations
- Re-issued every tax year at the same URL — the upstream file at irs.gov/pub/irs-pdf/fw4.pdf changes content annually without the URL changing. Pin the revision you tested against by hash, not by URL.
- Hybrid AcroForm/XFA, same behavior as the W-9: libraries fill the AcroForm layer and drop the XFA layer on save.
- Only pages 1-4 of the 5-page PDF are the certificate + worksheets; field names are spread across all pages with the IRS's positional naming scheme (topmostSubform[0].Page1[0].f1_01[0] …) — map them via inspection, never by guessing.
Tested request
This exact shape ran against the live API during the recorded test (values here shortened to two fields; the test filled all 48 supported fields). With flatten=true, two identical runs produced byte-identical output — sha256 e7201382e5b88980fc6db7edc690357b72ac2350775684e5fe68c69cc70985ab.
curl -X POST https://pdfops.dev/api/fill-form \
-F "pdf=@irs-w-4.pdf" \
-F 'fields={"topmostSubform[0].Page1[0].Step1a[0].f1_01[0]":"Jane Doe","topmostSubform[0].Page1[0].Step1a[0].f1_02[0]":"Jane Doe"}' \
-F "flatten=true" \
-o filled.pdf
Enumerate all 48 field names first with the inspector or POST /api/inspect.
Field schema
All 48 fields (name, type, maxLength, options)
| Field name | Type | maxLength | Options |
|---|---|---|---|
topmostSubform[0].Page1[0].Step1a[0].f1_01[0] | TextField | ||
topmostSubform[0].Page1[0].Step1a[0].f1_02[0] | TextField | ||
topmostSubform[0].Page1[0].Step1a[0].f1_03[0] | TextField | ||
topmostSubform[0].Page1[0].Step1a[0].f1_04[0] | TextField | ||
topmostSubform[0].Page1[0].f1_05[0] | TextField | 11 | |
topmostSubform[0].Page1[0].c1_1[0] | CheckBox | ||
topmostSubform[0].Page1[0].c1_1[1] | CheckBox | ||
topmostSubform[0].Page1[0].c1_1[2] | CheckBox | ||
topmostSubform[0].Page1[0].c1_2[0] | CheckBox | ||
topmostSubform[0].Page1[0].Step3_ReadOrder[0].f1_06[0] | TextField | ||
topmostSubform[0].Page1[0].Step3_ReadOrder[0].f1_07[0] | TextField | ||
topmostSubform[0].Page1[0].f1_08[0] | TextField | ||
topmostSubform[0].Page1[0].f1_09[0] | TextField | ||
topmostSubform[0].Page1[0].f1_10[0] | TextField | ||
topmostSubform[0].Page1[0].f1_11[0] | TextField | ||
topmostSubform[0].Page1[0].c1_3[0] | CheckBox | ||
topmostSubform[0].Page1[0].f1_12[0] | TextField | ||
topmostSubform[0].Page1[0].f1_13[0] | TextField | ||
topmostSubform[0].Page1[0].f1_14[0] | TextField | 10 | |
topmostSubform[0].Page3[0].f3_01[0] | TextField | ||
topmostSubform[0].Page3[0].f3_02[0] | TextField | ||
topmostSubform[0].Page3[0].f3_03[0] | TextField | ||
topmostSubform[0].Page3[0].f3_04[0] | TextField | ||
topmostSubform[0].Page3[0].f3_05[0] | TextField | ||
topmostSubform[0].Page3[0].f3_06[0] | TextField | ||
topmostSubform[0].Page4[0].f4_01[0] | TextField | ||
topmostSubform[0].Page4[0].f4_02[0] | TextField | ||
topmostSubform[0].Page4[0].f4_03[0] | TextField | ||
topmostSubform[0].Page4[0].f4_04[0] | TextField | ||
topmostSubform[0].Page4[0].f4_05[0] | TextField | ||
topmostSubform[0].Page4[0].f4_06[0] | TextField | ||
topmostSubform[0].Page4[0].f4_07[0] | TextField | ||
topmostSubform[0].Page4[0].f4_08[0] | TextField | ||
topmostSubform[0].Page4[0].f4_09[0] | TextField | ||
topmostSubform[0].Page4[0].f4_10[0] | TextField | ||
topmostSubform[0].Page4[0].f4_11[0] | TextField | ||
topmostSubform[0].Page4[0].f4_12[0] | TextField | ||
topmostSubform[0].Page4[0].f4_13[0] | TextField | ||
topmostSubform[0].Page4[0].f4_14[0] | TextField | ||
topmostSubform[0].Page4[0].f4_15[0] | TextField | ||
topmostSubform[0].Page4[0].f4_16[0] | TextField | ||
topmostSubform[0].Page4[0].f4_17[0] | TextField | ||
topmostSubform[0].Page4[0].f4_18[0] | TextField | ||
topmostSubform[0].Page4[0].f4_19[0] | TextField | ||
topmostSubform[0].Page4[0].f4_20[0] | TextField | ||
topmostSubform[0].Page4[0].f4_21[0] | TextField | ||
topmostSubform[0].Page4[0].f4_22[0] | TextField | ||
topmostSubform[0].Page4[0].f4_23[0] | TextField |
Frequently asked
Can I fill the IRS Form W-4 programmatically?
Yes — the blank 2026 edition IRS Form W-4 is a fillable AcroForm with 48 fields. In our tested run, all 48 supported fields were filled through the pdfops API and verified by re-inspecting the output; upstream pdf-lib also round-trips it, and pypdf handled its text fields. Field names are positional (e.g. topmostSubform[0].Page1[0].f1_01[0]) — enumerate them via inspection rather than guessing.
Is the IRS Form W-4 an XFA form?
It is a hybrid: the PDF carries both a standard AcroForm field tree and an XFA layer. Programmatic fillers in the pdf-lib family fill the AcroForm layer and drop the XFA layer on save, so the output is a plain AcroForm PDF — which every mainstream viewer renders. Only XFA-first viewers (older Adobe products) behave differently.
← All forms in the registry · Inspect your own PDF · Make a flat PDF fillable