GET /vehicle/tax
Computes the French registration tax (carte grise) of a vehicle: the CO2 malus, the weight malus and the regional certificate tax, each priced against the schedule in force on the registration date you supply — not the one a model happens to remember. Every item shows its bracket, its threshold, the articles of the code des impositions sur les biens et services behind it and their LEGI identifiers, so the figure is auditable against the law rather than taken on trust.
The schedules are embedded locally: pure calculation at request time, no
network call and no secret. The technical figures can be declared, or resolved
from the EU CO2 dataset by passing a vehicle key — in
which case each figure says whether it was declared or resolved. See the live
/catalog for the authoritative endpoint listing and price.
Two properties carry the credibility of this endpoint. Coverage is France
only: any other country is a 400, because approximating a neighbouring
schedule would be worse than refusing. And the total disappears rather than
being partial: if a required figure was neither declared nor resolved, that
item is undetermined with a reason, the others stay computed, and total_eur
is absent — a partial sum presented as a total is a wrong figure.
x402 golden rule: the agent pays for the answer to its question. A
computable request is a billed 200, including one where an item is exempt
(0 EUR, with the exempting article named), not_applicable (no schedule existed
at that date) or undetermined (no amount, a reason instead). Only requests the
service cannot answer — a country other than France, a date before the first
published schedule, an unknown region, a missing fuel — leave the 200 range and
are not billed.
Parameters
Query-string parameters. country=FR is always required. The technical figures
(co2_wltp, weight_kg, fuel) are either declared or resolved from a
vehicle key; the administrative figures (fiscal_power, region) are French
records absent from the EU registry and are therefore always declarative.
| Parameter | Type | Required | Description |
|---|---|---|---|
country | string | yes | Jurisdiction. FR is the only covered value; anything else is a 400 COUNTRY_NOT_COVERED |
registration_date | string | no (default today, UTC) | YYYY-MM-DD — selects the schedule to the day (the CO2 schedule changed mid-year on 2025-03-01) |
co2_wltp | int | declared or resolved | WLTP CO2 in g/km (0–1000) — base of the CO2 malus |
weight_kg | int | declared or resolved | Mass in running order, kg (100–20000) — base of the weight malus |
fuel | string | declared or resolved | electric, hydrogen, gas (gpl/gnv), plug_in_hybrid, petrol, diesel, petrol_hybrid, diesel_hybrid — the vocabulary of /vehicle/critair, usual synonyms accepted |
fiscal_power | int | for the regional tax | Fiscal power in CV (1–999) — base of the regional tax; always declarative |
region | string | for the regional tax | French region by INSEE code (11) or exact name (case and accents folded); always declarative |
type_approval | string | resolution key | Resolves the technical figures from /vehicle/co2 |
make model year | — | resolution key | The approximate key of /vehicle/co2, all three together |
GET /vehicle/tax?country=FR&co2_wltp=150&weight_kg=1900&fuel=diesel&fiscal_power=7®ion=11®istration_date=2026-05-01
GET /vehicle/tax?country=FR&make=Renault&model=Clio&year=2021&fiscal_power=5®ion=52
A declared value always wins over a resolved one. A future
registration_date is legitimate as long as an open schedule covers it; a date
before the first published schedule is a 404 that names the bound (2020-01-01).
200 response — UnifiedResponse
{
"data": {
"country": "FR",
"registration_date": "2026-05-01",
"inputs_used": { "...": "..." },
"co2_malus": { "status": "due", "...": "..." },
"weight_malus": { "status": "due", "...": "..." },
"regional_tax": { "status": "due", "...": "..." },
"total_eur": 10272.65,
"schedule": { "...": "..." },
"limits": ["..."]
},
"provenance": {
"source": "fr-registration-tax",
"fetched_at": "2026-07-30T18:29:54Z",
"freshness": { "kind": "snapshot", "as_of": "2026-07-28T00:00:00Z" }
}
}
Fields of data
| Field | Type | Description |
|---|---|---|
inputs_used | object | Every figure used, with its source (declared or resolved), so the result is reconstructable; a resolution entry appears when a vehicle key served, carrying the match_level of /vehicle/co2 |
co2_malus | object | CO2 malus, priced gram by gram against the dated schedule |
weight_malus | object | Weight malus, at the marginal rate bracket by bracket on the mass after any allowance |
regional_tax | object | Fiscal power × the tariff voted by the region, plus the flat national fee |
total_eur | number | Sum of the three items — absent as soon as one is undetermined |
schedule | object | Which schedule was applied: validity window per item, plus legal_basis[] (code, article, legi_id, in_force_since, subject) |
limits | string[] | The six reading limits carried by every answer |
status — read it before the amount
The three items share one closed enumeration:
| Value | Meaning |
|---|---|
due | Computed (including 0 EUR below the threshold — a real zero) |
exempt | An automatic exemption applied, named by its article in exemption — never silent |
not_applicable | No schedule existed at that date (the weight malus before 2022): an absence of tax, not a data gap |
undetermined | A required figure was neither declared nor resolved: no amount, a reason instead — and total_eur disappears |
The regional tax deserves a note of its own: its flat national fee stays due
even when the region exempts the vehicle, so an exempt electric car still shows
status: "exempt" with a non-zero amount_eur. That is the law, not a rounding
artefact.
Example — a diesel registered in Île-de-France
GET /vehicle/tax?country=FR&co2_wltp=150&weight_kg=1900&fuel=diesel&fiscal_power=7®ion=11®istration_date=2026-05-01
{
"data": {
"country": "FR",
"registration_date": "2026-05-01",
"inputs_used": {
"co2_wltp_g_km": { "value": 150, "source": "declared" },
"weight_kg": { "value": 1900, "source": "declared" },
"fiscal_power_cv": { "value": 7, "source": "declared" },
"fuel": { "value": "diesel", "source": "declared" },
"region": {
"value": { "insee_code": "11", "name": "Île-de-France", "eur_per_fiscal_hp": 68.95 },
"source": "declared"
}
},
"co2_malus": {
"status": "due",
"amount_eur": 4279,
"co2_g_km": 150,
"threshold_g": 108,
"capped": false
},
"weight_malus": {
"status": "due",
"amount_eur": 5500,
"mass_kg": 1900,
"taxable_mass_kg": 1900,
"threshold_kg": 1500,
"brackets": [
{ "from_kg": 1500, "to_kg": 1699, "taxable_kg": 200, "eur_per_kg": 10, "amount_eur": 2000 },
{ "from_kg": 1700, "to_kg": 1799, "taxable_kg": 100, "eur_per_kg": 15, "amount_eur": 1500 },
{ "from_kg": 1800, "to_kg": 1899, "taxable_kg": 100, "eur_per_kg": 20, "amount_eur": 2000 }
]
},
"regional_tax": {
"status": "due",
"amount_eur": 493.65,
"region": { "insee_code": "11", "name": "Île-de-France", "eur_per_fiscal_hp": 68.95 },
"fiscal_power_cv": 7,
"proportional_eur": 482.65,
"fixed_fee_eur": 11
},
"total_eur": 10272.65,
"schedule": {
"as_of": "2026-07-28",
"co2_malus": { "valid_from": "2026-01-01", "valid_to": "2026-12-31" },
"weight_malus": { "valid_from": "2026-01-01", "valid_to": null },
"regional_tax_as_of": "2026-07-28",
"legal_basis": [
{
"code": "code des impositions sur les biens et services",
"article": "L. 421-62",
"legi_id": "LEGIARTI000051214838",
"in_force_since": "2025-03-01",
"subject": "barèmes CO2 WLTP"
}
]
}
},
"provenance": {
"source": "fr-registration-tax",
"fetched_at": "2026-07-30T18:29:54Z",
"freshness": { "kind": "snapshot", "as_of": "2026-07-28T00:00:00Z" }
}
}
Every bracket of the weight malus is shown with its rate and its taxable
kilograms: the 5 500 EUR is not asserted, it is derived in the open.
legal_basis is elided here to one entry — the real answer carries the ten
articles behind the three items, each with its LEGI identifier.
Example — exemptions are named, and the flat fee survives them
Same vehicle as an electric one
(fuel=electric&co2_wltp=0): all three items are exempt, each citing its
article — and the regional tax still bills the flat national fee.
{
"co2_malus": {
"status": "exempt",
"amount_eur": 0,
"co2_g_km": 0,
"threshold_g": 108,
"exemption": {
"id": "electric_or_hydrogen",
"article": "L. 421-67",
"label": "vehicle powered exclusively by electricity, hydrogen or both"
}
},
"weight_malus": {
"status": "exempt",
"amount_eur": 0,
"mass_kg": 1900,
"threshold_kg": 1500,
"exemption": {
"id": "low_carbon_footprint_electric_or_hydrogen",
"article": "L. 421-79",
"label": "low-carbon-footprint vehicle, and hydrogen or electric vehicle"
}
},
"regional_tax": {
"status": "exempt",
"amount_eur": 11.0,
"proportional_eur": 0.0,
"fixed_fee_eur": 11,
"exemption": {
"id": "regional_clean_vehicle_exemption",
"article": "L. 421-49",
"label": "region has voted the exemption of electric and hydrogen vehicles"
}
},
"total_eur": 11.0
}
A plug-in hybrid instead shows an allowance rather than an exemption — the mass deduction of article L. 421-79-1, with the kilograms it removed and the fact that it does not exempt:
"weight_malus": {
"status": "due",
"amount_eur": 5500,
"mass_kg": 2100,
"taxable_mass_kg": 1900,
"threshold_kg": 1500,
"allowance": {
"id": "hybrid",
"article": "L. 421-79-1",
"label": "hybrid vehicles, by first registration date and drivetrain",
"allowance_kg": 200,
"exempts": false
}
}
Example — an undetermined item removes the total
Same request without co2_wltp and without a resolution key. The CO2 malus
cannot be computed, so it carries a reason instead of an amount; the other two
items stay computed; and total_eur is absent from the JSON — a billed 200
that refuses to publish a wrong number:
{
"data": {
"country": "FR",
"registration_date": "2026-05-01",
"co2_malus": {
"status": "undetermined",
"threshold_g": 108,
"reason": "'co2_wltp' is neither declared nor resolved: the CO2 malus cannot be computed"
},
"weight_malus": { "status": "due", "amount_eur": 5500, "...": "..." },
"regional_tax": { "status": "due", "amount_eur": 493.65, "...": "..." }
}
}
The same happens the other way round when weight_kg is missing
("'weight_kg' (mass in running order) is neither declared nor resolved: the mass tax cannot be computed"). And a vehicle registered before 2022 shows the
weight malus as not_applicable — an absence of tax, dated and reasoned
("the mass tax applies to vehicles first registered from 2022 onwards"), with
total_eur still served because nothing is unknown.
Example — figures resolved from the vehicle instead of declared
GET /vehicle/tax?country=FR&make=Renault&model=Clio&year=2021&fiscal_power=5®ion=52
resolves CO2, mass and fuel from the EU dataset and says so, item by item:
"inputs_used": {
"co2_wltp_g_km": { "value": 118, "source": "resolved" },
"weight_kg": { "value": 1171, "source": "resolved" },
"fuel": { "value": "petrol", "source": "resolved" },
"fiscal_power_cv": { "value": 5, "source": "declared" },
"resolution": {
"match_level": "model_year",
"source": "eea-co2-monitoring",
"make": "RENAULT",
"commercial_name": "CLIO",
"year": 2021
}
}
provenance.source then becomes fr-registration-tax+eea-co2-monitoring, and
limits gains a line stating that the CO2 came from a model-year aggregate
spanning N versions, so the malus of one specific version may differ. The
precision limit of /vehicle/co2
is propagated, never hidden behind a clean-looking euro amount.
What this endpoint refuses to do
The six limits come back with every answer:
- new vehicles registered in France: the reduction coefficient for vehicles already registered abroad (imported used cars) is not modelled;
- WLTP schedules only: the NEDC and administrative-power schedules for vehicles outside WLTP are not modelled;
- exemptions and allowances that depend on the taxpayer’s situation (disability card, dependent children, legal person operating a vehicle with at least eight seats) are published in the schedules but never applied automatically;
- the superethanol E85 allowance (L. 421-68) is not applied: the fuel
vocabulary shared with
/vehicle/critairdoes not express it; - the regional tariff is voted yearly and applies at the date the certificate is issued, not at first registration — only current tariffs are published, so no history is embedded;
- out of scope: annual taxes on business use of vehicles, transport-vehicle taxes, purchase incentives (bonus) and the non-tax delivery fee.
This is a costing of the published schedules, not tax advice and not a quote from the administration.
Freshness
The schedules and regional tariffs are served from a local snapshot, dated by
freshness.as_of and by schedule.as_of / schedule.regional_tax_as_of. A
missed refresh keeps serving the last good snapshot, honestly dated, and never
becomes a 5xx.
Errors
Only requests the service cannot answer leave the 200 range — none of them are billed.
| Status | code | Case |
|---|---|---|
| 400 | COUNTRY_NOT_COVERED | country is not FR — a neighbouring schedule is never approximated |
| 400 | INVALID_INPUT | Missing fuel without a resolution key; unknown region; a figure outside its bounds |
| 404 | DATE_OUT_OF_RANGE | registration_date predates the first published schedule (the message names it) |
| 404 | NOT_FOUND | A resolution key that the EU CO2 dataset does not match; unknown route |
| 500 | INTERNAL | Internal error (detail logged, not exposed) |
{ "error": "country 'DE' is not covered; only France (country=FR) has an embedded registration tax schedule", "code": "COUNTRY_NOT_COVERED" }
{ "error": "no published registration tax schedule for '2019-01-01'; France is covered from 2020-01-01 onwards", "code": "DATE_OUT_OF_RANGE" }
{ "error": "unknown region 'Atlantide'; supply an INSEE region code (for instance '11' for Île-de-France) or the exact region name", "code": "INVALID_INPUT" }
Attribution
The schedules are those of the code des impositions sur les biens et services, published by Légifrance (DILA) under the Licence Ouverte / Etalab — each applied article is returned with its LEGI identifier. Regional tariffs are the ones voted by the regional councils. Technical figures resolved from a vehicle key come from the EU CO2 monitoring dataset (Regulation (EU) 2019/631).
See also
GET /vehicle/co2— the type-approval CO2 and mass this costing is priced on.GET /vehicle/report— one VIN, one settlement: decode, recalls, CO2, registration tax and Crit’Air.GET /vehicle/critair— the Crit’Air class, which shares this endpoint’s fuel vocabulary.- For agents — discovery surfaces, the live
/catalogand how settlement works.