What WLTP CO2 was this car type-approved at?

Type-approval WLTP CO2 and technical specs for any European car, from the EU 2019/631 monitoring dataset — every figure stating the precision it was matched at.

By Matthias Begot · · Updated

An agent that needs a European vehicle’s CO2 figure — to cost a registration tax, to compare two listings, to fill an import declaration — cannot get it from a language model’s memory. The number depends on the test procedure and on the exact version of the car, and a commercial name like “Clio 2021” covers hundreds of versions that do not emit the same thing. GET /vehicle/co2 answers the question from the official EU CO2 monitoring dataset, offline, and — the part that matters — it tells you how precisely the figure was matched. One x402 settlement per answer, no account.

The problem: a figure that is wrong in two different ways

Ask a model “what is the CO2 of a 2019 Golf” and you will get a number. It will be wrong twice over:

  • Wrong procedure. The same car carries two official figures. The old NEDC value and the WLTP value, measured under the procedure that replaced it, are not the same number and do not answer the same question. Only one of them feeds a modern tax scale.
  • Wrong granularity. A make/model/year is not a vehicle. Under one commercial name a manufacturer sells petrol, diesel, hybrid — and increasingly a battery electric variant, type-approved at 0 g/km. A single “the CO2 of a 208” is a fiction.

Both failures are silent. The agent gets a plausible integer, uses it to price something, and nothing in the pipeline flags that it was invented. What an agent actually needs is a figure with a stated provenance and a stated precision.

One dataset: every new registration in the EU

The source is the EU CO2 monitoring dataset published under Regulation (EU) 2019/631. Member states report, for each new passenger car registered on their territory, the manufacturer, the type-approval number with its type, variant and version, the make and commercial name, the specific CO2 emissions under both the NEDC and WLTP protocols, masses, wheelbase, engine capacity and power, fuel type and mode. The European Environment Agency runs quality checks and publishes a provisional then a final database, one release per reporting year.

That makes it an unusually good base for an agent: it is official, it is per-registration rather than per-brochure, and it is a flat file — which is why Invoket serves it from a local store with no network call and no secret at request time.

It also sets the hard boundary of the endpoint: the dataset describes registrations, not individual cars. It carries no VIN.

Two keys, and the precision each one buys

The endpoint takes two mutually exclusive key forms — the precise one and the approximate one — and every response opens with a match_level naming which one you got.

match_levelHow it was keyedWhat you get
type_approval_exactType-approval number narrowed by variant and versionUnique published values for one approved version
type_approval_partialType-approval number aloneVersions of that approval, registration-weighted median
model_yearmake + model + yearA whole model-year population — the only level with p10/p90
GET /vehicle/co2?type_approval=e2*2007/46*0624*10
GET /vehicle/co2?make=Peugeot&model=208&year=2022

The three parts of the approximate key travel together: two of the three is a 400 naming the one that is missing, and a year outside the ingested window is a 404 that names the years actually served rather than returning a silent miss.

{ "code": "INVALID_INPUT", "error": "the approximate key needs all of 'make', 'model' and 'year' (missing: year)" }
{ "code": "YEAR_OUT_OF_WINDOW", "error": "year 2030 is outside the ingested window (covered years: 2018-2022)" }

Why the dispersion is the answer

Here is a real model_year answer for a Peugeot 208 registered in 2022:

{
  "data": {
    "match_level": "model_year",
    "vehicle": { "make": "PEUGEOT", "commercial_name": "208", "year": 2022 },
    "typical_specs": { "engine_capacity_cm3": 1199, "engine_power_kw": 74, "mass_kg": 1165 },
    "co2": {
      "wltp": { "p50": 115, "p10": 0, "p90": 122 },
      "unit": "g/km",
      "sample_size": 184519,
      "low_sample": false
    },
    "coverage": {
      "years_covered": [2018, 2019, 2020, 2021, 2022],
      "versions_aggregated": 225,
      "min_sample": 5
    }
  },
  "provenance": {
    "source": "eea-co2-monitoring",
    "freshness": { "kind": "snapshot", "as_of": "2026-07-28T00:00:00Z" }
  }
}

Read p10: 0. Two hundred and twenty-five versions sit behind that median, and the bottom decile of them is the battery electric e-208, type-approved at zero, sold under the same commercial name. The median of 115 g/km is a true statement about the population and a false statement about any car you are actually looking at.

This is the whole design argument for match_level. An endpoint that returned 115 and stopped would look more confident and be less useful. An agent that reads p10 and p90 can branch: when the spread is wide, the model-year answer is not good enough for a tax computation, and the agent must obtain the type-approval number instead of rounding a median.

typical_specs carries the same warning in its own limit: each figure there is an independent weighted median over the aggregated versions, so together they describe no single existing car. They are a portrait of a population, not a datasheet.

NEDC and WLTP are two different questions

The same endpoint on a pre-switchover year shows why the procedure has to be explicit:

"co2": { "wltp": { "p50": 138, "p10": 128, "p90": 169 }, "nedc": 113, "unit": "g/km", "sample_size": 535576 }

That is a 2019 Volkswagen Golf, over half a million registrations. The gap between the correlated NEDC value and the WLTP median is 25 g/km — enough to move a vehicle several brackets down a CO2-indexed tax scale. nedc is served only where the source publishes it; on later registrations the field is simply absent, and absent is not zero.

The same rule runs through the whole response. A field the source does not carry is omitted, never served null — a battery electric has no engine_capacity_cm3, and that absence is itself the fact. Conversely 0 is a real value: an EV type-approved at 0 g/km returns zeros, and an agent must never read a missing block as a zero.

What the endpoint refuses to do

Every answer carries its reading limits inline, because they are instructions rather than a disclaimer:

  • Type-approval values, not real-world consumption. These are laboratory WLTP figures. The European Commission’s own on-board monitoring (OBFCM) data, published in July 2024, found real-world emissions of petrol and diesel cars run around 20 % above their WLTP type-approval values — and plug-in hybrids more than three times above. Type-approval values are the right input for a regulatory or fiscal computation and the wrong input for a running-cost estimate.
  • A statistical aggregate of registrations, not a manufacturer datasheet.
  • No VIN → variant/version link. A European VIN does not carry the type-approval variant outside manufacturer databases. That is why a VIN-keyed answer is always model_year, and the limit is stated rather than worked around.
  • No regulated pollutants — NOx and particulates are out of scope.

It is also not a valuation, a price quote or a purchase recommendation.

Where it sits in the x402 loop

No signup precedes the call:

  1. Discover the endpoint in the live catalog, call it, receive the 402 challenge.
  2. Pay — sign the chosen rail and replay the request.
  3. Read match_level first, then the figures.
  4. Branch — feed a tax computation, flag a listing, or go back for a type-approval number when the dispersion is too wide to commit to.

Per the x402 golden rule, an agent pays for the answer to its question: a key the dataset matches is a billed 200, including a genuine 0 g/km for an electric vehicle. Requests the service cannot answer — no key, both key forms at once, an incomplete approximate key, a year outside the window, a vehicle absent from the dataset — leave the 200 range and are not billed. Quickstart walks the full discover → 402 → pay → replay cycle; price and accepted rails are served live by the catalog, never pinned in an article.

Chain it

CO2 and mass are exactly the two figures a French registration tax is priced on, so GET /vehicle/tax is the natural next call. For a used listing, GET /vehicle/vin/decode establishes the make, model and model year that key the CO2 lookup, while GET /vehicle/critair answers the low-emission-zone question from declared characteristics. For all of it against one VIN in a single settlement, use the composite GET /vehicle/report — where the CO2 block is always model_year, for the reason above.

For the full field reference, the coverage semantics and the error codes, see the GET /vehicle/co2 documentation; for how agents discover and call Invoket endpoints, see For agents.