State income tax, with the rules attached
One HTTP call returns a year of state income tax for any of the 50 states or the District of Columbia — and, alongside the number, the rules that produced it. A state tax figure with no account of which exclusion, which Social Security formula and which bracket schedule applied is not checkable, and a figure you cannot check is not one to build on.
Request
POST https://quantcalc.app/api/state-tax
Content-Type: application/json
X-License-Key: YOUR_KEY
{
"state": "CA",
"filingStatus": "single",
"age": 67,
"ordinaryIncome": 90000,
"socialSecurity": 20000,
"capitalGains": 10000
}
| Field | Required | Meaning |
|---|---|---|
state | yes | Two-letter code, or DC. |
filingStatus | no | single, mfj, mfs or hoh. Defaults to single. |
age | no | Filer's age. Several states condition retirement exclusions and Social Security treatment on it, so omitting it changes the answer in those states. |
ordinaryIncome | no | Ordinary income excluding Social Security. |
socialSecurity | no | The federally-taxable portion of benefits, not gross benefits. This trips people up; see below. |
capitalGains | no | Net long-term capital gains. |
Response
{
"state": "CA",
"stateName": "California",
"tax": 4275.42,
"effectiveRate": 0.0356,
"input": { "filingStatus": "single", "age": 67, "ordinaryIncome": 90000,
"socialSecurity": 20000, "capitalGains": 10000 },
"rules": {
"taxesSocialSecurity": false,
"socialSecurityRule": "exempt",
"bracketCount": 9,
"standardDeduction": 5540,
"personalExemption": 0,
"seniorSubtraction": 0,
"seniorSubtractionMinAge": 0,
"capitalGainsTaxedAsOrdinary": true,
"capitalGainsInclusionRatio": 1,
"retirementIncomeExclusion": {
"cap": 0, "minAge": 0, "partialCap": 0, "partialMinAge": 0,
"incomeLimit": 0, "reducedBySocialSecurity": false
}
},
"taxYear": "2026",
"methodology": "https://quantcalc.app/docs/state-tax-api/"
}
The rules block is the point. It reports what the engine applied for that
state, filing status and age — so a result can be reconciled against the statute
rather than taken on trust.
Social Security is not one rule
Nine distinct statutory treatments are modelled, because that is how many there are.
socialSecurityRule names the one that applied:
| Value | What it means |
|---|---|
exempt | Benefits never enter the state base. |
full_above_threshold | Exempt up to an income threshold, fully taxable above it — a cliff, not a ramp. |
federal_conformity | The federally-taxable amount is taxed as-is. |
capped_25_percent | Above the threshold, a fixed fraction of taxable benefits is included. |
stepped_subtraction | The subtraction reduces in steps as income rises. |
ratable_phaseout | The exemption phases out proportionally across a band. |
credit_phaseout | Relief arrives as a credit that tapers with income. |
age_and_agi_test | Exempt only if both an age and an income test are met. |
age_banded_subtraction | Treatment differs by age band. |
The input that is most often wrong
socialSecurity must be the federally-taxable portion of
benefits — the amount that lands in federal AGI — not the gross benefit. States
that conform to the federal treatment start from that number, and passing gross benefits
instead will overstate the base in every state that taxes any of it. If you are computing
the federal side yourself, that is the figure from the Social Security benefits worksheet;
the rules behind it are set out in the
tax methodology.
What this does not include
- Federal income tax, FICA, and local or municipal levies.
- Credits that depend on circumstances outside the request — dependents, property tax paid, and similar.
- Part-year and non-resident apportionment. The calculation is a full-year resident.
Errors
Every refusal carries a reason. A missing or unrecognised state, a negative
income component, an impossible age, or a malformed body each return 400 with
a message naming the problem, rather than a confident zero.
Access
This endpoint is licensed. Send your key as X-License-Key; without one it
returns 403. The browser app reaches state tax inside a full plan via
/api/simulate, so every caller here is direct API use — which is the
licensed surface rather than the free one.
curl -s https://quantcalc.app/api/state-tax \
-H 'content-type: application/json' \
-H 'X-License-Key: YOUR_KEY' \
-d '{"state":"CT","filingStatus":"single","age":70,
"ordinaryIncome":60000,"socialSecurity":25000}'
Evaluating it, or working out which shape fits — a rules-and-tables drop you compile in, a metered endpoint, or a build of the module — starts with an email: [email protected]. Evaluation keys are not a problem.
The summary dataset behind the comparison tables — top rates, bracket counts, standard deductions, exclusion caps and their age tests — is published separately under CC-BY-4.0 at /data/state-retirement-tax/2026/ and is free to use with attribution. It is not enough to compute a liability; this endpoint is.
Tax year 2026. State rules change annually, and a meaningful share of the table turns over between October and December. Figures are checked against their sources and cross-checked between the engine and every page that restates them. QuantCalc is calculation software, not tax advice.