oneapi
Sign in
Financiar v1 / Fiscal

Salary gross to net

The net pay left from a gross, with the breakdown by component: every contribution separately, with its own computation base, the rate applied and the legislation behind that rate. An accountant cannot use a number they cannot justify.

⚠️ POST, not GET, and with zero retention. The input is somebody's salary: in a GET it would end up in access logs, in the browser history and in the Referer header. Nothing you send is written anywhere — not to the database, not to a cache, not to a log.

⚠️ Order matters and so does rounding. Pension and health contributions are computed on the whole gross, each rounded separately to the leu; only then is the personal allowance deducted and the income tax applied. Rounded only at the end, the figure comes out a leu away from the payslip for a share of salaries.

⚠️ The personal allowance is covered across the whole scale in art. 77(4) — all 41 bands, across five columns of dependants, transcribed from the legislative portal. The band is chosen by how far the gross exceeds the minimum wage, and the percentage is applied to the minimum wage, not to the gross. Above the minimum wage plus 2,000 lei the allowance is zero by law.

⚠️ If a band were nevertheless missing — today none is — the response does not assume zero but returns an interval: net comes back null and beside it net_interval carries the bounds, which remain exact. The mechanism is kept as a safety net, not because it is needed.

⚠️ If any rate used is flagged de_verificat, the response says so at response level, not buried inside the breakdown.

POST
https://oneapi.ro/api/financiar/v1/calcul/salariu/brut-la-net

Request body

Sent as JSON in the request body (Content-Type: application/json) — not in the query string.

NameTypeDescriptionAccepted values
brutrequired integer | null Required, in whole lei. 1 – 100000000
la_dataoptional string | null Optional, YYYY-MM-DD; defaults to today. Determines every rate used.
normaoptional string | null Optional: intreaga (default) or partiala. intreagapartiala
facilitateoptional string | null Optional: it, constructii, agroalimentar. Must be active at the requested date.
persoane_intretinereoptional integer | null Optional, 0-20; above 4 the level for 4 applies, as in the law. 0 – 20
deducere_personalaoptional integer | null Optional, in lei. The way around the untranscribed band: if you give it, it is used as is. 0 – 100000
5,000 lei gross Request body
{
    "brut": 5000
}
With two dependants Request body
{
    "brut": 4000,
    "persoane_intretinere": 2
}
The minimum wage Request body
{
    "brut": 4050
}
With the IT exemption, in 2023 Request body
{
    "brut": 15000,
    "facilitate": "it",
    "la_data": "2023-06-15"
}
Part-time Request body
{
    "brut": 2500,
    "norma": "partiala"
}

Query parameters

This route accepts no query parameters.

How it behaves

  • The net remaining from a gross, with a component breakdown: each contribution separately, with its calculation base, the rate applied and the legal act behind that rate. An accountant cannot use a number they cannot justify, so the breakdown is not optional.
  • ⚠️ POST, not GET, and with zero retention. The input is someone salary: in a GET it would land in the server access logs, in the browser history and in the Referer header of the next page. Nothing you send is written anywhere — not to the database, not to the cache, not to a log.
  • ⚠️ Order matters, and so does rounding. Pension and health contributions are computed on the full gross, each rounded to the leu separately; only then is the personal deduction subtracted and the income tax applied to what remains. Rounded only at the end, the figure comes out one leu off the payslip for some salaries.
  • ⚠️ The personal deduction is covered across the whole grid. The table in art. 77 para. (4) — one row for income up to the minimum wage plus 41 brackets of 50 lei above it, across five columns for dependants — is transcribed from the official legislative portal. The bracket is chosen by how far the gross exceeds the minimum wage, and the percentage applies to the minimum wage, not to the gross. Above the minimum wage plus 2,000 lei the deduction is zero by law. A worked example you can check by hand: a gross of 4,500 lei on 1 March 2025, minimum wage 4,050, so 450 lei above it → bracket +401..+450 → 15.5% × 4,050 = 628 lei deduction, giving a net of 2,695 lei.
  • ⚠️ Should a bracket ever be missing — none is today — the response does not assume zero but returns an interval: net comes back null with net_interval beside it, the bounds staying exact and the contributions staying single figures. The mechanism is kept as a safety net.
  • ⚠️ If any value used is flagged de_verificat, the response says so at response level, in meta, not buried in the breakdown.
  • ⚠️ Relief with a ceiling (IT, construction and agri-food, from January 2023): the law says "the part of the monthly gross income above the ceiling does not benefit from tax reliefs" — plural. So the part above is taxed and carries the general pension and health rates, while the part below keeps the reduced rate or the exemption. The interpretation applied to the taxable base of the part above is flagged in the response, because it is an interpretation, not a quoted text.
  • ⚠️ The non-taxable part of the minimum wage (200 or 300 lei, depending on the period) is deducted from EVERY base — income tax, pension and health — not only from the taxable one. Without it, the net at the minimum wage came out about 80 lei too low. The conditions in the law are about the contract (base wage exactly at the guaranteed minimum, full time, main job), while the request carries only the monthly gross: the route applies the rule when the gross sits between the minimum wage and the period ceiling, and says in meta.avertismente what it assumed. Checkable: at 4,050 lei gross in March 2025 the net is 2,574 lei; at 4,325 lei in September 2026, 2,699 lei.

What it is for

  • The net of a salary at a past date, with every deduction justified.
  • Checking a payslip, component by component.
  • The effect of a sectoral relief on the net, at the same gross.

Response structure

FieldTypeDescription
data.brut integer The smallest whole gross that yields exactly the requested net.
data.net integer The computed net position: total assets minus total liabilities.
data.la_data string The date the returned values are valid on.
data.facilitate null
data.componente[].cod string Code from the source code list — the key you filter on.
data.componente[].denumire string The name, exactly as the source publishes it.
data.componente[].baza_de_calcul integer The amount the rate was applied to — for income tax it is the taxable base, not the gross.
data.componente[].cota string
data.componente[].valoare integer The value published by the source for the combination above.
data.componente[].act_normativ string The legal basis of the rate applied to that component.
data.componente[].de_verificat boolean
meta.sursa string The official source the data comes from, with its licence where one is required.
meta.retentie string Confirmation that the input was not stored anywhere.
meta.nota_acoperire string Why the total is a lower bound, in prose. meta.total_e_limita_de_jos is the word to switch on in code.
meta.de_verificat Which values used are not legally confirmed. Empty means all are.
data.net_interval Present only when the deduction falls in the untranscribed band: the bounds the net certainly lies between. When present, net is null.
data.componente[].valoare_interval The same per component — appears only on those that depend on the deduction.
data.componente[].semn scade_din_baza_impozabila for the personal deduction, scade_din_toate_bazele for the non-taxable part of the minimum wage.
meta.fara_date data is null and this says why. The rates do not cover the requested date; the response says what is missing. The sector has no active relief then.
meta.fara_date_cod The short, STABLE word for the reason, for clients that branch on it in code: fara_date is prose and may be rephrased, this is not.

Response examples

body: {"brut": 5000}

POSThttps://oneapi.ro/api/financiar/v1/calcul/salariu/brut-la-net
{
    "data": {
        "brut": 5000,
        "net": 2981,
        "la_data": "2026-10-07",
        "facilitate": null,
        "componente": [
            {
                "cod": "cas",
                "denumire": "Contribuţia de asigurări sociale",
                "baza_de_calcul": 5000,
                "cota": "25%",
                "valoare": 1250,
                "act_normativ": "Codul Fiscal art.138",
                "de_verificat": false
            }
        ]
    },
    "meta": {
        "sursa": "Cotele vin din registrul comun `business_indicatori_fiscali`; facilităţile şi grila deducerii, din `Modules/Financiar/resources/fiscal`, cu act normativ pe fiecare valoare.",
        "retentie": "Zero. Intrarea nu se scrie nicăieri — nici în bază, nici în cache, nici în log.",
        "nota_acoperire": "Ce e acoperit şi ce nu: /financiar/v1/fiscal/acoperire."
    }
}

Error codes

CodeSlugWhen it occurs
401neautentificatMissing or invalid token.
403feature_indisponibilfinanciar.fiscal_calcul is not in the current plan.
422deducere_neacoperitaOnly for a la_data before 2023, where the deduction regime is not transcribed. From 2023 onwards the grid is complete. brut_lipsa. Plus values outside the expected shape.
429cota_depasitaThe plan's monthly quota has been exceeded (see Retry-After).

All errors share the same shape. eroare.cod is stable and is the value you should branch on in code — not the text in mesaj, which may change.

{
    "eroare": {
        "cod": "deducere_neacoperita",
        "mesaj": "Only for a la_data before 2023, where the deduction regime is not transcribed. From 2023 onwards the grid is complete. brut_lipsa. Plus values outside the expected shape."
    }
}