Skip to content

Extracting Gross Pay, Deductions and Net Pay From a Payslip

9 min read · updated August 11, 2026

Unlike a W-2, a payslip has no authority defining its fields. What it has instead is arithmetic, and the arithmetic is a better anchor than any label on the page.

There is no payslip standard

A W-2 box number means one specific thing because the IRS defines it. A payslip is whatever the payroll provider decided to print. The same concept appears as “Gross Pay”, “Total Earnings”, “Gross Earnings” or “Total Gross”; net pay appears as “Net Pay”, “Take Home”, “Net Amount” or as an unlabelled figure in a box at the bottom right. Sections are ordered differently, deductions are sometimes one block and sometimes split into statutory and voluntary, and the employer’s own costs may be printed in a column, a block, or not at all.

Keying on labels therefore gets you a schema that works for one provider. Keying on position gets you one that works for one template from one provider. The thing that is true of every payslip in every jurisdiction is the arithmetic, so that is what the schema should be built to test.

Two identities that have to close

There are two, they overlap, and each catches errors the other misses.

# Identity 1 — the totals chain
sum(earnings_lines)    == gross_pay
sum(deduction_lines)   == total_deductions
gross_pay - total_deductions == net_pay

# Identity 2 — the taxable-pay chain
gross_pay - pre_tax_deductions        == taxable_gross
taxable_gross - taxes - post_tax_deductions == net_pay

The first proves you captured every line in each block. The second proves you classified each deduction on the right side of the tax calculation, which the first cannot see — swap a pre-tax deduction for a post-tax one and identity 1 still closes perfectly while the taxable figure, which is the number that eventually has to agree with a W-2, is wrong.

That second identity is also why the pre-tax and post-tax distinction belongs in the schema as a field on each deduction line rather than as two separate lists. Whether a given deduction is pre-tax depends on the plan and sometimes on the tax being calculated — a benefit can reduce income tax withholding but not social insurance — so it is a property of the line, and forcing it into the structure loses the ability to represent the cases that do not fit.

Employer contributions are not deductions

Here is the failure that accounts for most broken payslip extractions. Many payslips print the employer’s own costs on the same sheet: the employer’s share of social insurance, a pension or retirement match, employer-paid insurance premiums. They appear as currency amounts in what looks like the deductions region, often in a second column headed something like “Employer” or “Company Contribution”.

They are not deducted from the employee’s pay. They are the employer’s expenditure, shown for information. An extractor that sums everything that looks like a deduction includes them, and identity 1 then fails by exactly the total of the employer-side items — which is the diagnostic. If your residual equals a recognisable subset of the deduction lines rather than a single one, look for a column header you ignored.

A second and subtler case is imputed income: the taxable value of a non-cash benefit, such as group-term life cover above the exempt limit or personal use of a company vehicle. It increases gross and taxable pay without any cash being paid, and it is then usually removed again as a post-tax deduction so that net pay is right. If you capture the addition and miss the removal, net pay comes out too high by that amount — and the figure will look entirely reasonable.

Building it

  1. Extract the pay period first. Period start, period end, pay date and pay frequency. Everything else is meaningless without them, and the pay date rather than the period end is what determines the tax year a payment falls in.
  2. Extract lines, not totals. Every earnings row and every deduction row with its printed description, its rate and hours where present, its current amount and its year-to-date amount. Totals should be extracted too, but as things to check against, not as the data.
  3. Classify each line as earnings, pre-tax deduction, tax, post-tax deduction, employer contribution or informational. Six values, one enum, and “informational” is what stops the model forcing a memo line into a real bucket.
  4. Run both identities and reject on failure rather than rounding it away. A tolerance of a cent for rounding is reasonable; a tolerance of a dollar hides a real error.
  5. Cross-check the year-to-date column against the previous payslip, which is the only check that proves you assigned the columns correctly — see extracting year-to-date totals.

One trap in step 2 worth naming: the rate and hours columns are currency-like numbers that are not currency. A model asked for “the amounts” on an earnings row will sometimes return the hourly rate, and 42.50 hours at 24.00 an hour produces three plausible numbers of which only one is the earnings figure. Ask for rate, hours and amount as named fields, and check that rate times hours approximately equals the amount where all three are present.

What breaks the identity legitimately

  • Rounding across percentage deductions. Several deductions computed as percentages and each rounded to the cent will not always sum to a percentage of the total. A cent of tolerance is correct; more than that is a bug.
  • Retroactive pay and mid-period rate changes. Produce two earnings lines for the same job at different rates, or a correction line with a negative amount. Negative earnings are legal and a parser that assumes earnings are positive will drop the sign.
  • Negative deductions. A refund of an over-deduction appears as a negative in the deductions block and increases net pay. Same problem, opposite direction.
  • Garnishments and court orders. Post-tax, often with an administrative fee line beside them, and frequently printed in their own block that a section-aware extractor misses entirely.
  • Multiple payments on one slip. An off-cycle bonus run combined with a regular period, or two assignments for one employee, produces two of everything, and the identity must be run per payment rather than over the sheet.
  • Advances and repayments. A salary advance paid in a previous period is recovered here, so net pay is legitimately far below what the earnings suggest.

Every one of these is a reason to record the residual and its composition rather than to loosen the tolerance. A payslip that fails the identity by an amount exactly matching a line you classified as informational is a classification bug you can fix; a payslip that fails by an unexplained amount is a reading error that should not be posted.