Every January, W-2s arrive by the thousands — as clean payroll-provider PDFs, as phone photos of paper copies, as scans of scans. If your product or practice needs the numbers inside them (tax prep intake, loan underwriting, income verification, benefits enrollment), someone is either re-keying boxes by hand or you have an extraction pipeline.
This page covers what a W-2 actually contains, which fields a parser should extract, why W-2s are easier than most documents to parse reliably (and where they still bite), and how to wire W-2 extraction into your own system with a schema-first API.
What a W-2 contains
Form W-2 (Wage and Tax Statement) is the IRS form employers file for each employee, reporting the year’s compensation and everything withheld from it. The layout is standardized by the IRS, which is good news for parsing: the same information appears in numbered boxes on every copy.
The identity block carries the employer’s name, address, and EIN (a 9-digit number formatted XX-XXXXXXX), plus the employee’s name, address, and SSN. The numeric grid is where the value is:
- ▪Box 1 — wages, tips, other compensation (federal taxable wages)
- ▪Box 2 — federal income tax withheld
- ▪Boxes 3-6 — Social Security wages/tax and Medicare wages/tax (these often differ from Box 1 because of pre-tax deductions)
- ▪Box 12 (a-d) — coded amounts: D for 401(k) deferrals, W for HSA contributions, DD for employer health coverage cost, and two dozen other codes
- ▪Box 13 — checkboxes for statutory employee, retirement plan, third-party sick pay
- ▪Boxes 15-20 — the state and local block: state, employer’s state ID, state wages and withholding, local wages, local tax, locality name
The extraction schema
A practical W-2 schema captures the boxes your downstream process consumes. This is the shape Dokyumi’s tax-prep customers typically start from — trim or extend it to match what your system needs:
| Field | Type | Required | Notes |
|---|---|---|---|
| employer_name | string | Yes | Legal name from the employer block |
| employer_ein | string | Yes | XX-XXXXXXX format — a strong validation anchor |
| employee_name | string | Yes | |
| wages_box1 | currency | Yes | Federal taxable wages |
| federal_tax_withheld_box2 | currency | No | |
| ss_wages_box3 | currency | No | Capped at the annual SS wage base |
| medicare_wages_box5 | currency | No | Usually ≥ Box 1 |
| box12_codes | array | No | Array of { code, amount } pairs |
| state | string | No | Box 15 state abbreviation |
| state_wages_box16 | currency | No | |
| state_tax_box17 | currency | No |
Fixed layout — mostly. Where W-2 parsing actually goes wrong
The IRS standardizes W-2 box numbering, so unlike invoices, you never have to guess what a number means — Box 1 is Box 1 everywhere. But the IRS permits "substitute" W-2s, and payroll providers use that freedom heavily. ADP, Gusto, Paychex, and Workday each arrange the boxes differently, and many print employee copies in a 4-up layout: four near-identical copies (Copy B, C, 2, 2) of the same W-2 on one page.
That 4-up layout is the classic W-2 parsing trap: a naive text extractor sees every value four times and a template-based parser built on one provider’s layout breaks on the next provider’s. A schema-first extractor sidesteps both problems — it maps whatever layout it sees onto your named fields once, rather than reading coordinates.
The other reality of W-2 season is capture quality. Employees photograph paper copies at an angle, in bad light, with the perforated edge torn through Box 12. Inspect the model-reported confidence map and validation details on these files; a review response can contain low-confidence fields, type-validation errors, or both.
API integration
Define a schema once (describe the fields in plain English, or upload a couple of sample W-2s and let schema inference build it), give it the slug w2-parser, and every extraction is one HTTP call:
Request — POST /api/v1/extract
curl -X POST https://dokyumi.com/api/v1/extract \
-H "Authorization: Bearer dk_live_your_api_key" \
-F "file=@employee-w2.pdf" \
-F "schema=w2-parser"Response
{
"id": "e5d48289-0a2e-46f8-9337-0b2e6ca9d714",
"status": "completed",
"request_id": "63b173a7-1720-46ec-a946-4df77f570df7",
"schema": "w2-parser",
"data": {
"employer_name": "Brightline Manufacturing Inc.",
"employer_ein": "84-1234567",
"employee_name": "R. Chen",
"wages_box1": 68450.00,
"federal_tax_withheld_box2": 8760.00,
"ss_wages_box3": 68450.00,
"medicare_wages_box5": 68450.00,
"box12_codes": [{ "code": "D", "amount": 4100.00 }],
"state": "CA",
"state_wages_box16": 68450.00,
"state_tax_box17": 2890.00
},
"confidence": {
"wages_box1": 0.99,
"employer_ein": 0.97,
"federal_tax_withheld_box2": 0.98,
"box12_codes": 0.93
},
"validation": {
"valid": true,
"errors": [],
"low_confidence_fields": []
},
"meta": {
"processing_time_ms": 1050,
"page_count": 1,
"credits_used": 1,
"ocr_cached": false,
"model": "anthropic/claude-sonnet-4"
}
}Branch on a successful response status: "completed" means there are no validation errors or known below-threshold model scores; "review" means you must inspect both validation.errors and validation.low_confidence_fields. The confidence map may omit fields. Failed requests return a non-2xx error envelope instead of an extraction result. Accepted inputs are PDF, PNG, JPG, TIFF, and WEBP up to 20MB.
For intake season at volume, configure a webhook on a Dokyumi upload site and process site submissions asynchronously instead of polling — each completed site extraction POSTs to your endpoint.
Accuracy and the review workflow
No extractor is perfect on every scan, and any vendor quoting a single universal accuracy number is averaging away the documents that matter. Dokyumi returns a model-reported confidence map plus validation details. When status is review, inspect both validation.errors and validation.low_confidence_fields; a field may be missing from the confidence map, so the caller must not treat missing confidence as approval. The default schema threshold is 0.8.
W-2s also validate unusually well because the form is self-consistent: the EIN has a fixed format, Box 3 cannot exceed the Social Security wage base, and Boxes 1, 3, and 5 relate to each other in predictable ways. Cheap downstream checks catch the rare confident-but-wrong extraction before it reaches a tax return.
What it costs
Dokyumi uses flat monthly credit tiers. One credit covers up to 5 pages, and longer documents use another credit for each additional 5-page block:
A W-2 up to 5 pages uses one credit, regardless of layout or duplicate copies printed on the same page. A tax practice handling a few hundred short W-2 files per season fits in Starter; the free tier is enough to test against your own documents before entering a card.
- ▪Free — $0/month: 25 extraction credits, 2 schemas. Enough to validate extraction quality on your real documents before paying anything.
- ▪Starter — $99/month: 500 extraction credits, 10 schemas, REST API and webhook delivery.
- ▪Growth — $499/month: 3,000 extraction credits, 50 schemas, 25 white-label upload portals.
- ▪Enterprise — quoted: custom volume, documents beyond the 50-page self-serve limit, unlimited schemas and portals, schemas built for you.
- ▪One credit covers a document of up to 5 pages. A 6–10 page document uses 2 credits, 11–15 uses 3, and so on up to the 50-page self-serve ceiling — no per-page metering and no overage billing.
Full details on the pricing page.