Can this car enter a French ZFE? Its Crit'Air class, computed offline
Crit'Air class and in-force ZFE restrictions, computed offline from declared fuel plus Euro norm or first-registration date. No account, pay per call.
By Matthias Begot · · Updated
Whether a car may enter a French low-emission zone (ZFE) comes down to
its Crit’Air class — a deterministic function of vehicle category, fuel and
Euro emission standard, fixed by French regulation. That means an agent does
not need a registry lookup to answer it: one offline call,
GET /vehicle/critair, computes the class from
declared characteristics and returns the ZFE restrictions currently in
force for that class. Pure calculation, no store, no network, no account —
one x402 settlement per answer.
The problem: access rules an agent cannot guess
A purchase-assistant agent comparing used cars, a trip planner routing through Lyon, a fleet-compliance agent auditing vans: all of them hit the same question — is this vehicle allowed where it needs to go? The answer is regulatory, not observational. France classifies vehicles into Crit’Air classes under the Arrêté du 21 juin 2016 (art. R. 318-2 of the code de la route), and each ZFE restricts entry by class.
The rules are also moving. The 2025 simplification law voted to abolish the ZFE mandate; in May 2026 the Conseil constitutionnel struck that repeal down on procedural grounds, leaving the zones in force. An agent that hardcodes last year’s understanding of ZFE schedules gets this wrong; an endpoint that models only restrictions actually in force, from a dated snapshot, does not.
A calculation, not a lookup
GET /vehicle/critair takes declared inputs —
values the agent typically already holds from a registration document, a
listing, or a VIN decode:
fuel— always required (electric,hydrogen,petrol,diesel,petrol_hybrid,diesel_hybrid; a hybrid is classified by its combustion engine).- At least one of
euro_norm(pre_euro,euro2..euro6) orfirst_registration(YYYY-MM-DD, used to derive the norm). When both are given, the expliciteuro_normwins.
GET /vehicle/critair?fuel=diesel&first_registration=2009-05-01
The response gives the computed critair_class ("0", "1".."5" or
"non_classe"), the list of modelled zones that restrict that class —
each with a stable zone_id, its perimeter description and the date the
restriction took effect — and a derivation block reporting which input path
produced the effective Euro norm. The calculation is auditable: the agent
can always see whether the class came from the declared norm or was derived
from the registration date, and regulation.reference names the grid it was
computed against.
Because the core is a pure function over embedded reference tables, there is no store to miss and no staleness error to hit: the answer always computes, in milliseconds.
Reading the result honestly
The endpoint sells a regulatory verdict, and it is precise about what that verdict covers:
non_classeis a successful answer. A vehicle too old to qualify for any sticker is exactly the fact a purchase or compliance agent is paying to learn — a 200, billed, per the x402 golden rule.- An empty
zfe_restrictionslist is a successful answer too. A clean sticker that no modelled zone restricts is the verdict, not a miss. - Input is declared, not verified. The class is exact for the characteristics given; the service does not check that the vehicle really has them. Garbage in, wrong class out — which is why feeding it from a VIN decode or a registration document beats trusting a listing’s prose.
- Only permanent, in-force restrictions are modelled. Local, time-based
and sector exemptions (protected axes, low-mileage permits, day passes) are
explicitly out of scope, and the response says so in
notes. Tightening schedules revised by the 2025 law are only included once actually in force. - Scope is M1/N1 only (passenger cars and light vans) in v1, and there is
no lookup by licence plate. The ZFE perimeters come from a dated
snapshot;
provenance.freshness.as_ofcarries the oldest of the two embedded references, never a fresher-looking date.
Where it sits in the x402 loop
The call follows the same loop as every Invoket endpoint — no signup precedes it:
- Discover the endpoint via the live catalog and call it; receive the
402challenge. - Pay — sign the chosen rail and replay the request.
- Read —
critair_class, thederivationaudit trail, and the zones restricting that class. - Branch — clear the vehicle for its route, flag it in a purchase comparison, or mark the fleet unit for replacement.
The Quickstart walks the full discover → 402 → pay →
replay cycle with runnable snippets. Price and accepted rails are not pinned
here — they are served live by the
catalog; see the
endpoint reference for the current figure.
Chain it: decode, then classify
The Crit’Air call slots naturally after a VIN decode: model year from
GET /vehicle/vin/decode helps pin the
first-registration era, and the decoded facts keep the declared inputs honest.
For recall exposure on the same vehicle, add
GET /vehicle/recalls; for all three answers in
a single settlement, use the composite
GET /vehicle/report.
Used for what it is — an offline, auditable regulatory calculation with an
honestly-bounded ZFE model — /vehicle/critair lets an agent answer “can this
car go there?” before anyone buys, routes or dispatches it. For the full field
reference and error codes, see the
GET /vehicle/critair documentation; for how
agents discover and call Invoket endpoints, see
For agents.