"We Just Need to Handle All 50 States"

Every payroll engineering team starts the same way. Federal taxes? Got it — IRS Publication 15-T, seven brackets, standard deduction. State taxes? More work, but manageable — download each state's withholding guide, implement 43 different bracket systems (plus the 7 states with no income tax and the 2 that only tax dividends/interest).

Then someone asks: "What about local taxes?"

And that's when the project timeline doubles.

The Scope of the Problem

Here are the numbers that most product teams don't see coming:

For a payroll system that claims to handle "all US jurisdictions," the local layer is 98% of the jurisdictions and 80% of the implementation complexity.

Why Teams Underestimate Local Taxes

It's Not Just Different Rates

If local taxes were simply "look up a rate and apply it," they'd be straightforward. The real complexity comes from the rules:

Stacking: In Kentucky, an employee owes both city OLT and county OLT — they stack. In Ohio, city income taxes don't stack the same way. In Pennsylvania, the rules are different again.

Credits: Ohio municipalities give credits for taxes paid to the work city against the residence city's tax. The credit limit varies by city. You need to calculate: (1) tax owed to work city, (2) credit available in residence city, (3) net tax owed to residence city.

Resident vs. non-resident rates: Many jurisdictions charge different rates depending on whether the employee lives or works there. Louisville charges 2.20% to residents but 1.45% to non-residents.

Collection systems: Ohio has three separate systems (RITA, CCA, self-administered) with different registration, filing, and payment requirements. Your payroll system needs to know which system each municipality uses.

The Data Maintenance Problem

Federal tax brackets change once a year (January 1). State rates change once a year. But local rates can change:

Symmetry employs ~68 people and dedicates a meaningful portion to monitoring and maintaining rate data across 7,040+ jurisdictions. It's a full-time operation.

The Geocoding Problem

Here's the problem that kills most DIY approaches: a street address determines the tax jurisdiction, not a ZIP code.

ZIP codes were designed for mail delivery, not tax boundaries. A single ZIP code routinely spans multiple municipalities. If you use ZIP codes to determine local tax rates, you will calculate the wrong tax for some employees.

The correct approach requires either:

  1. Geocoding the address to coordinates, then checking which municipal boundary contains those coordinates
  2. Using Pennsylvania PSD codes (for PA), which map addresses to specific tax collection districts
  3. Querying a jurisdiction lookup API that handles the mapping for you

The Build vs. Buy Decision

What "Building It Yourself" Actually Looks Like

Teams that decide to build their own local tax system typically go through this progression:

Month 1: "We'll just add a table of city tax rates." Create a spreadsheet with the 50 biggest cities.

Month 3: "We need more cities." Discover Ohio has 528 municipalities. Start scraping RITA's website.

Month 6: "Pennsylvania has HOW many municipalities?" Discover 2,573 PA municipalities, each needing a PSD code. Try to find an authoritative data source.

Month 9: "Kentucky taxes stack?!" Discover that your data model assumed one local tax rate per location. Refactor the entire withholding calculation.

Month 12: "This is now a full-time job." Hire someone to monitor rate changes across 4,246 jurisdictions.

Symmetry's marketing claims that building in-house payroll compliance costs over $4 million. That's probably inflated, but the ongoing maintenance cost of $1-2M/year is realistic for a team monitoring and updating rates across all US jurisdictions.

What an API Looks Like

// One call. All jurisdictions. Current rates.
const response = await fetch(
  'https://payroll-tax-api-9f4b18020da9.herokuapp.com/v1/rates/lookup' +
  '?workState=OH&workCity=Columbus&payDate=2026-04-15' +
  '&filingStatus=single&grossWages=5000&payPeriod=biweekly',
  { headers: { 'Authorization': 'Bearer ptx_free_your_key' } }
);

const { taxes, meta } = await response.json();
// Returns: federal brackets, FICA, FUTA, OH state income,
// Columbus city income tax — all in ~12ms

No spreadsheets. No scraping. No geocoding. No monitoring rate changes.

The 2024-2026 Landscape

The multi-state payroll landscape continued to evolve:

Remote work increased local tax complexity. Post-pandemic remote work policies mean employees increasingly work from home in different jurisdictions than their office. Ohio's Cleveland CWT (Central Withholding Tax) controversy highlighted the tension — should the work city still collect income tax when the employee works from home in a different city?

More jurisdictions, not fewer. The trend is toward more local taxation, not less. As state and local governments face budget pressures, local income taxes are an attractive revenue source. New jurisdictions continue to be created.

Rate changes accelerated. Between 2024 and 2026:

What Good Multi-State Payroll Architecture Looks Like

If you're building (or rebuilding) a multi-state payroll system, here's the architecture that works:

1. Model Taxes as a Stack, Not a Single Rate

Every employee potentially owes multiple layers of tax. Your data model should be:

Employee Pay Period
  ├── Federal Income Tax (graduated)
  ├── Social Security (6.2%, capped)
  ├── Medicare (1.45%, + 0.9% surtax)
  ├── FUTA (employer, capped)
  ├── State Income Tax (graduated or flat)
  ├── State Unemployment (employer, experience-rated)
  ├── State Disability (if applicable)
  ├── City Income Tax (work city)
  ├── City Income Tax (residence city, net of credit)
  ├── County Tax (if applicable, e.g., KY OLT)
  └── School District Tax (if applicable, e.g., OH)

2. Always Query by Pay Date

Rates have effective dates. Never cache "the current rate" — always query based on the actual pay date. A paycheck covering December 28 - January 10 might span two different rate years.

3. Separate Rate Data from Calculation Logic

Get your rate data from an authoritative source (an API), then implement your own withholding calculations. This separation means:

4. Plan for the Long Tail

The top 50 cities cover maybe 60% of your employees. The next 500 cover another 30%. The last 2,800 cover the remaining 10%. But that 10% generates 90% of the support tickets, because it's the edge cases that break.

Start Simple, Scale Later

You don't need to cover every jurisdiction on day one. A practical rollout:

  1. Phase 1: Federal + all 50 states (covers ~85% of payroll needs)
  2. Phase 2: Major local jurisdictions — NYC, Philadelphia, Columbus, Cleveland (covers the biggest cities)
  3. Phase 3: Full local coverage — PA, OH, KY, IN, MI, MD, and all states with local taxes (covers 4,246 jurisdictions including 713 school districts)

Or skip the phased build and use an API that already covers 4,246 jurisdictions. Free tier gets you started in 30 seconds.