How the Germany net salary calculator is verified
Inputs
| Input | Field | Type |
|---|---|---|
| Gross annual salary (EUR) | grossAnnual | number |
| Children (0 = childless surcharge on care insurance) | children | number |
| Church tax rate (0, 0.08 or 0.09) | churchRate | number |
The formula
Each derived value below is computed by the Valem engine from the expression shown, in this order. Nothing else runs — there is no hidden code path.
| Derived value | Field | Formula (JSONata) |
|---|---|---|
| Pension + unemployment | socialPensionUnemp | $round(($const.pensionRate + $const.unempRate) * (grossAnnual < $const.ceilingPensionUnemp ? grossAnnual : $const.ceilingPensionUnemp), 2) |
| Care Rate | careRate | children > 0 ? $const.careBase : $const.careBase + $const.childlessSurcharge |
| Health + long-term care | socialHealthCare | $round(($const.healthRate + $const.addOnHalf + careRate) * (grossAnnual < $const.ceilingHealthCare ? grossAnnual : $const.ceilingHealthCare), 2) |
| Social Total | socialTotal | $round(socialPensionUnemp + socialHealthCare, 2) |
| Taxable income (zvE) | zvE | ($b := grossAnnual - socialTotal - $const.allowanceEmployee - $const.specialLump; $round($b > 0 ? $b : 0, 2)) |
| Income tax (§32a) | incomeTax | ($z := zvE; $g := $const.grundfreibetrag; $y := ($z - $g) / 10000; $w := ($z - 17443) / 10000; $t := $z <= $g ? 0 : $z <= 17443 ? (932.30 * $y + 1400) * $y : $z <= 68480 ? (176.64 * $w + 2397) * $w + 1015.13 : $z <= 277825 ? 0.42 * $z - 10911.92 : 0.45 * $z - 19246.67; $round($t, 2)) |
| Solidarity surcharge | solidaritySurcharge | $round(incomeTax > $const.soliThreshold ? incomeTax * $const.soliRate : 0, 2) |
| Church tax | churchTax | $round(incomeTax * churchRate, 2) |
| Total Tax | totalTax | $round(incomeTax + solidaritySurcharge + churchTax, 2) |
| Net / year | netAnnual | $round(grossAnnual - socialTotal - totalTax, 2) |
| Net / month | netMonthly | $round(netAnnual / 12, 2) |
Rates and thresholds
The expressions above read these as $const. They are the figures the annual refresh replaces, so they live as data rather than being written into the formulas — which is what makes a year-on-year change a one-line diff instead of an edit to arithmetic.
| Constant | Value |
|---|---|
pensionRate | 0.093 |
unempRate | 0.013 |
ceilingPensionUnemp | 96600 |
healthRate | 0.073 |
addOnHalf | 0.0125 |
ceilingHealthCare | 66150 |
careBase | 0.018 |
childlessSurcharge | 0.006 |
allowanceEmployee | 1230 |
specialLump | 36 |
grundfreibetrag | 12096 |
soliThreshold | 19950 |
soliRate | 0.055 |
Test vectors (2)
Every case is executed against this model on each build, by the Java engine and independently by a JavaScript one. A mismatch of a single cent fails the build, so the page cannot ship advertising a figure the model no longer produces.
Case 1 — S1 48,000 EUR single childless — default case
given grossAnnual | 48000 |
|---|---|
given children | 0 |
given churchRate | 0 |
expect solidaritySurcharge | 0 |
expect churchTax | 0 |
Case 2 — S3 150,000 EUR single — ceilings and soli relief floor
given grossAnnual | 150000 |
|---|---|
given children | 0 |
given churchRate | 0 |
expect socialPensionUnemp | 10239.6 |
Sources
- BMF Brutto-Netto-Rechner (official calculator) · checked 2026-08-02
- §32a EStG — income tax tariff · checked 2026-08-02
Check it yourself — and reuse it
The whole model as JSON — inputs, outputs, formulas, vectors and sources in one re-runnable document.
This model is published under CC BY 4.0: use it, adapt it, build on it, with attribution. The licence covers the model — the expression of each rule and the arrangement of the catalog. The rates and thresholds are law, and nobody licenses those; the sources above are where they come from.
← Back to the Germany net salary calculator
Built with Valem — a live, verifiable calculator you can fork and embed.