GET /vehicle/lookup
Turns the designation an agent holds - “Renault Clio V”, “VW Golf 2020”,
a make and a commercial name copied from a classified ad - into the
make / model / year key that
/vehicle/recalls and
/vehicle/co2 require, spelled the way the
referential spells it. Those two routes do not need a VIN, but they do need
the referential’s own spelling: without it, “Renault Clio V” returns an empty
recall list that looks exactly like “no recall” - the most expensive silent
failure of the vehicle family. Answers come from local stores already
ingested, with no network call and no secret at request time.
Start here when the agent holds a name. If it holds a VIN, skip this route:
/vehicle/vin/decode,
/vehicle/report and
/vehicle/prepurchase take the VIN directly. See the
live /catalog for the authoritative listing and price.
ambiguous is the normal answer here
Read this before the examples: on this route, ambiguous is the nominal
case, not a degraded one. A model spans several model years, and the
European aggregates index each year as its own key - so “Skoda Octavia” is six
candidates, “Alpine A110” five, and an exact usually needs a year in the
text. That is the opposite of /company/lookup,
where most names resolve to a single company; each page tells the truth of
its own domain.
verdict | Meaning | candidates |
|---|---|---|
exact | One candidate answers | exactly one element |
ambiguous | Several do - all served, up to 25, with total exact. The usual case | two or more elements |
none | The referentials publish nothing for this text, at the snapshot date | [] |
candidates is always present - empty on none, a one-element list on
exact. The service never picks a candidate for you: the list is the
answer, and each candidate carries its model years and the endpoints that
accept it, which is what lets you choose.
none is a paid answer
A none is served as a 200 and billed - never a 404. “No referential
publishes this designation” is exactly what stops an agent from inventing a
make and a model and paying downstream calls that cannot succeed. Only a
malformed request - q missing, empty or longer than 200 characters - leaves
the 200 range, and it is not billed.
What a resolution does not attest
Every answer carries the same fixed sentence in data.limits, served
verbatim:
A resolution names what the referential publishes for the text you sent, as of the date carried by the provenance; it does not certify that the entity resolved is the one you had in mind. Candidates come from the recall campaigns and the European registration aggregates already ingested: a make/model absent from both is not a statement about the vehicle, only about these referentials. Generation designations (‘V’, ‘5’, ‘Mk5’) are not interpreted, as no referential publishes them; a four-digit number in the text is read as a model year. A candidate without a year is accepted by /vehicle/recalls, where the year is optional, but not by /vehicle/co2, which requires one.
Three consequences, each worth a line.
Generations are not interpreted. “Renault Clio V” is exactly what an
agent will write, and no referential publishes generations - only model
years. So “Clio V”, “Clio 5” and “Clio” resolve to the same Clio keys, and
the RENAULT CLIO 2018 candidate an agent gets for “Clio V” is a
fourth-generation car. If the generation matters, the year is the only
lever the referentials offer.
A short model name that appears in your text is a candidate. A model is
retained when its published label appears, as a whole, in the text -
extra words (trim, engine, generation) do not prevent the match. Without a
make, that cuts both ways: Clio V alone returns 12 candidates, including the
Mercedes-Benz V (the V-Class) and the Orion V bus, because V is a
published model name. Name the make in the text or pass the make
filter: “Renault Clio V” returns Renault keys only.
A none is a statement about these referentials, not about the vehicle.
The Microlino is a real vehicle; it is a quadricycle, outside the
passenger-car registration aggregates and absent from the recall campaigns -
so it comes back none. That is the answer, not a verdict on the car.
The key: three natures, one of them partial
Each candidate’s id is the key downstream accepts as is - copy it into
the next call without reworking it. It is composite here, because that is
what the vehicle referentials index, and id_kind names how much of it is
there:
id_kind | id | Accepted by |
|---|---|---|
make_model_year | { make, model, year } | /vehicle/co2 and /vehicle/recalls |
make_model | { make, model } | /vehicle/recalls only - no year |
make | { make } | /vehicle/recalls only - the make-only fallback |
A candidate without a year is accepted by
/vehicle/recalls but not by
/vehicle/co2, which needs a model year to find a
registration aggregate. Each candidate says so itself in accepted_by, which
is computed against the store, never deduced from a convention. Read it
before paying for a co2 call that cannot succeed.
The make-only fallback is a partial match
When the text names a known make but no model any referential publishes,
the answer is a single id_kind: "make" candidate - “Koenigsegg Jesko”
returns { "make": "KOENIGSEGG" }, because no ingested referential publishes
a Jesko. Because it is the only candidate, the verdict is exact: the
verdict counts candidates, it does not grade them. What tells you the match
is partial is the candidate itself - id_kind: "make" and accepted_by
without /vehicle/co2. Read id_kind before the verdict. A make-wide
recall search is still a useful next call; a CO2 lookup is not.
A make that no referential knows does not fall back to anything: it is a
none.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
q | string | yes | The designation as you hold it - make, model, commercial name, with or without a model year. Up to 200 characters |
make | string | no | Narrows to one make, in any spelling the curated aliases know (vw, volkswagen, mercedes-benz) |
GET /vehicle/lookup?q=golf&make=vw
- The
makefilter is firm. It only reduces the candidate set, never changes the shape of the answer - and it narrows even to nothing:q=golf&make=zorglubis anone, not a wider list. - A year in the text is an indication, not a filter. A four-digit number
in
qis read as a model year: it narrows when it matches (Renault Clio 2021isexact), and it steps aside when it would leave nothing -Clio 2015does not come back empty on a referential that publishes the Clio, it returns the year-lessRENAULT CLIOkey. - No pagination. The list is capped at 25;
totalcounts every candidate before the cap andtruncatedsays whether the list was cut. A second page would be a second paid call for the same question - narrow withmakeor a year instead. A truncated list is neverexact.
200 response - UnifiedResponse
{
"data": { ... },
"provenance": {
"source": "nhtsa+rappelconso+eea-co2-monitoring",
"fetched_at": "2026-10-15T15:32:03.517946196Z",
"freshness": { "kind": "snapshot", "as_of": "2026-07-28T00:00:00Z" }
}
}
provenance.source: the referentials queried - NHTSA recall campaigns, RappelConso, and the EEA CO2 monitoring aggregates.freshness.as_of: the oldest snapshot among them. Anoneis only worth something if it is dated.
Fields of data
| Field | Type | Description |
|---|---|---|
query | string | The text you sent, echoed verbatim |
verdict | string | exact, ambiguous or none |
candidates | object[] | Always served. Ordered by make, then model, then model year descending - a stable reading order, never a relevance ranking |
total | number | Candidates found before the 25-item cap; the verdict is derived from it |
truncated | bool | Whether the served list is cut at the cap |
limits | string | The fixed sentence quoted above |
Each candidate:
| Field | Type | Description |
|---|---|---|
id | object | { make, model?, year? } - the indexed values, to pass downstream as is |
id_kind | string | make_model_year, make_model or make |
label | string | The designation as the source publishes it - never recased, never re-accented, never completed |
years | object | { from, to } - the model years attached to this make/model in the referentials. Omitted when none is published |
sources | string[] | Which referentials publish this candidate: nhtsa, rappelconso, eea-co2-monitoring |
accepted_by | string[] | Which endpoints accept this key, computed against the store |
There is no similarity score anywhere in the response, and the order is mechanical: a rank would read as a confidence the service does not have.
Labels are served as published, quirks included. The European aggregates
are reported by member states, and some rows carry the make again in the
commercial name (RENAULT CLIO as a model, next to CLIO) or a misspelled
make (VOLKSAWGEN, VW). They are separate keys in the referential, so they
are separate candidates here - never cleaned up, because a cleaned key would
no longer be accepted downstream.
Example - exact
GET /vehicle/lookup?q=Renault%20Clio%202021:
{
"data": {
"query": "Renault Clio 2021",
"verdict": "exact",
"candidates": [
{
"id": { "make": "RENAULT", "model": "CLIO", "year": 2021 },
"id_kind": "make_model_year",
"label": "RENAULT CLIO 2021",
"years": { "from": 2021, "to": 2021 },
"sources": ["eea-co2-monitoring", "rappelconso"],
"accepted_by": ["/vehicle/co2", "/vehicle/recalls"]
}
],
"total": 1,
"truncated": false,
"limits": "A resolution names what the referential publishes for the text you sent, as of the date carried by the provenance; it does not certify that the entity resolved is the one you had in mind. Candidates come from the recall campaigns and the European registration aggregates already ingested: a make/model absent from both is not a statement about the vehicle, only about these referentials. Generation designations ('V', '5', 'Mk5') are not interpreted, as no referential publishes them; a four-digit number in the text is read as a model year. A candidate without a year is accepted by /vehicle/recalls, where the year is optional, but not by /vehicle/co2, which requires one."
},
"provenance": {
"source": "nhtsa+rappelconso+eea-co2-monitoring",
"fetched_at": "2026-10-15T15:32:03.517946196Z",
"freshness": { "kind": "snapshot", "as_of": "2026-07-28T00:00:00Z" }
}
}
The id goes straight into /vehicle/co2?make=RENAULT&model=CLIO&year=2021
or /vehicle/recalls?make=RENAULT&model=CLIO&year=2021.
Example - ambiguous, the usual case
GET /vehicle/lookup?q=Renault%20Clio%20V - the form an agent actually
writes (limits and provenance as above):
{
"data": {
"query": "Renault Clio V",
"verdict": "ambiguous",
"candidates": [
{ "id": { "make": "RENAULT", "model": "CLIO", "year": 2022 }, "id_kind": "make_model_year", "label": "RENAULT CLIO 2022", "years": { "from": 2018, "to": 2022 }, "sources": ["eea-co2-monitoring", "rappelconso"], "accepted_by": ["/vehicle/co2", "/vehicle/recalls"] },
{ "id": { "make": "RENAULT", "model": "CLIO", "year": 2021 }, "id_kind": "make_model_year", "label": "RENAULT CLIO 2021", "years": { "from": 2018, "to": 2022 }, "sources": ["eea-co2-monitoring", "rappelconso"], "accepted_by": ["/vehicle/co2", "/vehicle/recalls"] },
{ "id": { "make": "RENAULT", "model": "CLIO", "year": 2020 }, "id_kind": "make_model_year", "label": "RENAULT CLIO 2020", "years": { "from": 2018, "to": 2022 }, "sources": ["eea-co2-monitoring", "rappelconso"], "accepted_by": ["/vehicle/co2", "/vehicle/recalls"] },
{ "id": { "make": "RENAULT", "model": "CLIO", "year": 2019 }, "id_kind": "make_model_year", "label": "RENAULT CLIO 2019", "years": { "from": 2018, "to": 2022 }, "sources": ["eea-co2-monitoring", "rappelconso"], "accepted_by": ["/vehicle/co2", "/vehicle/recalls"] },
{ "id": { "make": "RENAULT", "model": "CLIO", "year": 2018 }, "id_kind": "make_model_year", "label": "RENAULT CLIO 2018", "years": { "from": 2018, "to": 2022 }, "sources": ["eea-co2-monitoring", "rappelconso"], "accepted_by": ["/vehicle/co2", "/vehicle/recalls"] },
{ "id": { "make": "RENAULT", "model": "CLIO V" }, "id_kind": "make_model", "label": "RENAULT CLIO V", "sources": ["rappelconso"], "accepted_by": ["/vehicle/recalls"] },
{ "id": { "make": "RENAULT", "model": "RENAULT", "year": 2019 }, "id_kind": "make_model_year", "label": "RENAULT RENAULT 2019", "years": { "from": 2019, "to": 2019 }, "sources": ["eea-co2-monitoring"], "accepted_by": ["/vehicle/co2", "/vehicle/recalls"] },
{ "id": { "make": "RENAULT", "model": "RENAULT CLIO", "year": 2019 }, "id_kind": "make_model_year", "label": "RENAULT RENAULT CLIO 2019", "years": { "from": 2019, "to": 2019 }, "sources": ["eea-co2-monitoring"], "accepted_by": ["/vehicle/co2", "/vehicle/recalls"] }
],
"total": 8,
"truncated": false,
"limits": "A resolution names what the referential publishes for the text you sent, …"
},
"provenance": { "…": "…" }
}
Read it line by line, because every line teaches something:
- the five
CLIOkeys are the nominal ambiguity - one per model year the aggregates publish. Adding the year to the text (Renault Clio 2021) makes itexact; - the
Vwas not read as a generation:RENAULT CLIO 2018is a fourth-generation Clio, and it is served becauseCLIOappears in the text; CLIO Vis a separate key because RappelConso publishes that literal label - it has no year, so it is accepted by/vehicle/recallsand not by/vehicle/co2, andyearsis omitted;- the last two are aggregate rows where the member state reported the make inside the commercial name. They are real keys of the registry, served as published.
Example - make-only fallback
GET /vehicle/lookup?q=Koenigsegg%20Jesko (limits and provenance as
above):
{
"data": {
"query": "Koenigsegg Jesko",
"verdict": "exact",
"candidates": [
{
"id": { "make": "KOENIGSEGG" },
"id_kind": "make",
"label": "KOENIGSEGG",
"sources": ["eea-co2-monitoring", "nhtsa"],
"accepted_by": ["/vehicle/recalls"]
}
],
"total": 1,
"truncated": false,
"limits": "A resolution names what the referential publishes for the text you sent, …"
},
"provenance": { "…": "…" }
}
exact - one candidate - but partial: the make is known, the Jesko is
not published, and the candidate says so twice (id_kind: "make", no
/vehicle/co2 in accepted_by).
Example - none
GET /vehicle/lookup?q=Microlino:
{
"data": {
"query": "Microlino",
"verdict": "none",
"candidates": [],
"total": 0,
"truncated": false,
"limits": "A resolution names what the referential publishes for the text you sent, as of the date carried by the provenance; it does not certify that the entity resolved is the one you had in mind. Candidates come from the recall campaigns and the European registration aggregates already ingested: a make/model absent from both is not a statement about the vehicle, only about these referentials. Generation designations ('V', '5', 'Mk5') are not interpreted, as no referential publishes them; a four-digit number in the text is read as a model year. A candidate without a year is accepted by /vehicle/recalls, where the year is optional, but not by /vehicle/co2, which requires one."
},
"provenance": {
"source": "nhtsa+rappelconso+eea-co2-monitoring",
"fetched_at": "2026-10-15T15:32:03.676846569Z",
"freshness": { "kind": "snapshot", "as_of": "2026-07-28T00:00:00Z" }
}
}
A 200, billed, dated: neither the recall campaigns nor the passenger-car
aggregates publish this designation as of 2026-07-28. The agent now knows not
to pay a recalls or co2 call on a key it would have had to invent.
Sources
Candidates come from two kinds of referential, both already ingested:
- recall campaigns - NHTSA (US) and RappelConso (France): make/model pairs, with the model-year ranges of their campaigns;
- EEA CO2 monitoring aggregates (Regulation (EU) 2019/631): make, commercial name and model year of the cars registered in the EU.
NHTSA’s vPIC is used as a model-to-make bridge only - it lets “Golf”
reach Volkswagen when the make is not in the text. It is not a source of
candidates: vPIC does not publish enumerable make/model pairs, and its labels
are not the keys the downstream routes accept. It therefore never appears in
sources or in the provenance.
Errors
Only requests the service cannot answer leave the 200 range - none of them
are billed. There is no 404 on this route: a designation no
referential publishes is a none.
| Status | code | Case |
|---|---|---|
| 400 | MISSING_PARAMETER | q absent, empty or blank |
| 400 | INVALID_INPUT | q longer than 200 characters |
| 500 | INTERNAL | Internal error (detail logged, not exposed) |
{ "code": "MISSING_PARAMETER", "error": "missing or empty required parameter: q" }
{ "code": "INVALID_INPUT", "error": "'q' must be at most 200 characters" }
Attribution
Candidates are derived from public sources, each under its own terms:
- NHTSA vehicle recalls and the vPIC decoder, published by the US National Highway Traffic Safety Administration in the public domain.
- RappelConso, the French government recall service, under the Licence Ouverte / Etalab.
- The EU CO2 monitoring dataset published under Regulation (EU) 2019/631 (European Environment Agency / European Commission), reused under its open licence.
See also
GET /vehicle/recalls- recall campaigns for a make/model(/year) key: accepts every candidate this route returns.GET /vehicle/co2- EU type-approval CO2 and characteristics: accepts onlymake_model_yearkeys.GET /vehicle/vin/decode- when the agent holds a VIN instead of a name.GET /company/lookup,GET /medication/lookupandGET /label/lookup- the samelookupcontract for French companies (a name in, a SIREN out), medications (a CIS, NDC or substance out) and EU labelling (a CAS, EC, INCI or E number out).- For agents - discovery surfaces, the live
/catalogand how settlement works.