*** September 2026 Major Release ***Read and watch
Document type

Extracting data from pay stubs

A pay stub accompanies every paycheck, and state law fixes what must appear on it without saying how. California names gross wages, hours, deductions and net pay; the layout is the payroll system's own. Mortgage underwriters and benefits agencies read them in the thousands, as portal PDFs and photographs of paper.

An underwriter needs gross pay, the tax and deduction lines, net pay, and the pay period dates. A loan origination system annualizes gross pay into qualifying income, and a benefits system tests net pay against an eligibility threshold.

Page 1 of a sample Pay Stub, with extracted values outlined
2,397Markdown characters
114parse blocks
33fields extracted
The problem

What makes pay stubs hard

Two columns of every number

Each line carries the current period beside the year to date, so every amount appears twice with a different figure.

Codes without a key

Taxes and deductions are abbreviated to Med, SS, Federal and 401K, and each payroll system abbreviates them its own way.

Half a dozen small tables

Earnings, taxes, deductions, direct deposit and totals each get their own block and header row on a single page.

How it works

Parse, extract, verify

1

Parse

Parse turns the document into reading-order Markdown and a hierarchy of blocks (text, tables, figures), and grounds each block to its exact location on the page.

2

Extract

You define the fields in a JSON schema, and Extract returns their values in a consistent, structured format, each linked back to where it appears in the Markdown.

3

Verify

Each value links back to where it appears, down to the character range in the Markdown, and the page and bounding box in the source document, so every result can be verified.

Worked example

One pay stub, end to end

About this document: A Lenovo direct deposit earnings statement for a single pay period. One portrait page, set largely in Courier, with six ruled tables and a footnote about business expenses. This is a real employee's statement. The name, address, employee ID and last four of the social security number were replaced with obviously fake equivalents before publication. The pay amounts are printed as they were.

Processing objective: The objective is to pull one paycheck's arithmetic off the page so it can be checked. Gross pay of $5,361.60, less $1,387.30 in taxes and a $268.08 deduction, leaves the $3,706.22 that was deposited. The Medicare line is included because 1.45% of gross pay is exactly the amount shown.

Demonstration fields: Gross pay, Medicare tax, Total taxes, 401(k) deduction, Net pay

The document

1 page
  • Page 1 of the sample Pay StubPage 1shown below

Where each value came from

Page 1 of the sample Pay Stub, with every featured field outlined
Gross paytotals.gross_pay
5,361.6
The region of the sample Pay Stub containing Gross pay, with the extracted value outlined
extraction_metadata["totals.gross_pay"]
{
  "value": 5361.6,
  "page": 1,
  "range": {
    "end": 1162,
    "start": 1153
  },
  "box": {
    "xmin": 0.4054,
    "ymin": 0.46594,
    "xmax": 0.52627,
    "ymax": 0.48244
  }
}
Medicare taxtaxes[0].amount
77.74
The region of the sample Pay Stub containing Medicare tax, with the extracted value outlined
extraction_metadata["taxes[0].amount"]
{
  "value": 77.74,
  "page": 1,
  "range": {
    "end": 730,
    "start": 724
  },
  "box": {
    "xmin": 0.62941,
    "ymin": 0.29966,
    "xmax": 0.76654,
    "ymax": 0.31484
  }
}
Total taxestotals.total_taxes
1,387.3
The region of the sample Pay Stub containing Total taxes, with the extracted value outlined
extraction_metadata["totals.total_taxes"]
{
  "value": 1387.3,
  "page": 1,
  "range": {
    "end": 1194,
    "start": 1185
  },
  "box": {
    "xmin": 0.62932,
    "ymin": 0.46597,
    "xmax": 0.76655,
    "ymax": 0.48245
  }
}
401(k) deductiondeductions[0].amount
268.08
The region of the sample Pay Stub containing 401(k) deduction, with the extracted value outlined
extraction_metadata["deductions[0].amount"]
{
  "value": 268.08,
  "page": 1,
  "range": {
    "end": 1450,
    "start": 1443
  },
  "box": {
    "xmin": 0.25784,
    "ymin": 0.52008,
    "xmax": 0.38058,
    "ymax": 0.70302
  }
}
Net paytotals.net_pay
3,706.22
The region of the sample Pay Stub containing Net pay, with the extracted value outlined
extraction_metadata["totals.net_pay"]
{
  "value": 3706.22,
  "page": 1,
  "range": {
    "end": 1898,
    "start": 1889
  },
  "box": {
    "xmin": 0.44755,
    "ymin": 0.8395,
    "xmax": 0.64034,
    "ymax": 0.8604
  }
}

How to try it yourself

Everything on this page is reproducible. The sample, its schema, and the Parse and Extract API outputs are published. You can repeat the same two calls from whichever surface you already build on.

  • Claude Skilldescribe the job; the agent writes it
  • CLIno code at all
  • MCPexpose ADE docs as an agent tool
  • REST APIany language
  • Pythonlandingai-ade
  • TypeScriptlandingai-ade
Why LandingAI?

Documents into data

Accurate

The best accuracy on the difficult documents other tools get wrong, such as messy scans, complicated tables, checkboxes and handwriting.

Auditable

Every answer cites its sources. Values come back with an audit trail (page, bounding box, character range) so a human can review them.

Agentic

Applies multi-step reasoning, and plans before it acts. Handles documents it has never seen before and adapts to variations.

API-first

Composable APIs that drop into any tech stack and can be called from anywhere. Easy to build on and embed in your pipelines and products.

Affordable

Cost-competitive rates with complete price transparency. Spend controls before you run, and every job reports its exact cost.

Enterprise-grade

Built for production and scale from day one. Fully observable and monitored, running securely in our cloud or yours.

Built for high-volume, high-throughput production

  • Zero data retention
  • Priority rate limits
  • Enhanced support
  • VPC and on-prem deployment

Available on Team and Enterprise plans.

Compare plans
Capabilities

Pay Stub processing highlights

Gross, net and totals
Current period and YTD columns
Earnings lines with rates
Tax codes and amounts
Payroll deduction lines
Employer name and EIN
Pay period dates
Hours worked and PTO
Direct deposit details
Any payroll system layout
Photographed and scanned stubs
Bounding box for every value
FAQs

Questions, answered

Name them as separate fields in your schema. Both columns come back attributed to the line they belong to, so a gross pay figure for the period never arrives carrying the year-to-date total.

Both. Define the tax and deduction lines as arrays. Every row comes back as a structured entry with its code and amount, alongside the totals printed at the foot of each block.

Every value comes back with the page, a bounding box and the character range in the parsed Markdown. On a ruled stub the box covers the cell the amount sits in, so a reviewer can see which row and column it was read from.

Yes. A schema names the fields; each payroll system's own arrangement of blocks and codes is read at parse time. A stub from a system the schema has never seen returns the same field names.

A pay stub carries a home address, a bank account number and the last four of a social security number, none of which an income check needs. A schema can ask for the pay figures alone. Accounts are available with zero data retention, alongside SOC 2 Type II, GDPR and HIPAA compliance.

Start extracting pay stub data

Reliable, structured outputs with full traceability in minutes