Vehicles API Reference
Look up DfT vehicle licensing stock and registrations, search anonymised MOT results, and compare reliability.
Base URL: https://api.govdata.dev/v1
Licensed vehicle stock
GET /v1/vehicles/licensing returns quarterly licensed and SORN vehicle counts. Filters: make, model, quarter_from, quarter_to, body_type, licence_status, and geography (GB or UK).
/v1/vehicles/licensing
Waiting for request...
Parameters
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
make |
string | No | — | Case-insensitive make prefix. |
model |
string | No | — | Case-insensitive model prefix. |
quarter_from |
date | No | — | Inclusive YYYY-MM-DD lower bound. |
quarter_to |
date | No | — | Inclusive YYYY-MM-DD upper bound. |
body_type |
string | No | — | Exact DfT body type. |
licence_status |
string | No | — | Licensed or SORN. |
geography |
string | No | — | GB or UK. |
page |
integer | No | 1 | One-based page. |
per_page |
integer | No | 25 | Results per page, maximum 100. |
curl -H "Authorization: Bearer YOUR_API_KEY" "https://api.govdata.dev/v1/vehicles/licensing?make=FORD&geography=GB"
Net::HTTP.get_response(URI("https://api.govdata.dev/v1/vehicles/licensing?make=FORD&geography=GB"), {"Authorization" => "Bearer YOUR_API_KEY"})
requests.get("https://api.govdata.dev/v1/vehicles/licensing", headers={"Authorization": "Bearer YOUR_API_KEY"}, params={"make": "FORD", "geography": "GB"})
fetch("https://api.govdata.dev/v1/vehicles/licensing?make=FORD&geography=GB", { headers: { Authorization: "Bearer YOUR_API_KEY" } });
Response
{"data":[{"quarter":"2026-03-31","geography":"GB","licence_status":"Licensed","body_type":"Cars","make":"FORD","generic_model":"FOCUS","model":"FOCUS ZETEC","fuel":"Petrol","count":123}],"meta":{"source":"Department for Transport vehicle licensing statistics","api_version":"v1","licence":"Open Government Licence v3.0"},"pagination":{"total":1,"page":1,"per_page":25,"total_pages":1}}
Invalid dates return 400. No matches return an empty 200 response; 404 and request-body 422 are not applicable. GB coverage begins 1994 Q4 and UK coverage begins 2014 Q3. DfT revises this quarterly series; counts are individual vehicles, and zero-count combinations are omitted.
New registrations
GET /v1/vehicles/registrations returns quarterly counts of vehicles first registered in Great Britain. Filters: make, model, quarter_from, quarter_to, body_type, and geography (GB or UK).
/v1/vehicles/registrations
Waiting for request...
Parameters
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
make |
string | No | — | Case-insensitive make prefix. |
model |
string | No | — | Case-insensitive model prefix. |
quarter_from |
date | No | — | Inclusive lower bound. |
quarter_to |
date | No | — | Inclusive upper bound. |
body_type |
string | No | — | Exact DfT body type. |
page |
integer | No | 1 | One-based page. |
per_page |
integer | No | 25 | Results per page, maximum 100. |
curl -H "Authorization: Bearer YOUR_API_KEY" "https://api.govdata.dev/v1/vehicles/registrations?make=FORD"
Net::HTTP.get_response(URI("https://api.govdata.dev/v1/vehicles/registrations?make=FORD"), {"Authorization" => "Bearer YOUR_API_KEY"})
requests.get("https://api.govdata.dev/v1/vehicles/registrations", headers={"Authorization": "Bearer YOUR_API_KEY"}, params={"make": "FORD"})
fetch("https://api.govdata.dev/v1/vehicles/registrations?make=FORD", { headers: { Authorization: "Bearer YOUR_API_KEY" } });
Response
{"data":[{"quarter":"2026-03-31","geography":"GB","body_type":"Cars","make":"FORD","generic_model":"FOCUS","model":"FOCUS ZETEC","fuel":"Petrol","count":12}],"meta":{"source":"Department for Transport vehicle licensing statistics","api_version":"v1","licence":"Open Government Licence v3.0"},"pagination":{"total":1,"page":1,"per_page":25,"total_pages":1}}
Invalid dates return 400; 404 and 422 are not applicable. Coverage is Great Britain from 2001 Q1, refreshed quarterly. A first registration can be a new vehicle or a used import entering the GB register.
Search MOT tests
GET /v1/vehicles/mot-tests returns index-backed, paginated individual test results. Use it for statistical exploration, not vehicle-history checks.
/v1/vehicles/mot-tests
Waiting for request...
Parameters
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
make |
string | No | — | Case-insensitive make prefix. |
model |
string | No | — | Case-insensitive model prefix. |
test_year |
integer | No | — | Year of test; this slice contains 2025. |
fuel_type |
string | No | — | Enum label such as petrol, diesel, electric, or unknown. |
result |
string | No | — | Enum label such as passed, failed, or pass_with_rectification. |
postcode_area |
string | No | — | High-level station postcode area. |
first_use_year_from |
integer | No | — | Inclusive first-use year lower bound. |
first_use_year_to |
integer | No | — | Inclusive first-use year upper bound. |
page |
integer | No | 1 | One-based page. |
per_page |
integer | No | 25 | Results per page, maximum 100. |
curl -H "Authorization: Bearer YOUR_API_KEY" "https://api.govdata.dev/v1/vehicles/mot-tests?make=LEXMOTO&test_year=2025"
uri = URI("https://api.govdata.dev/v1/vehicles/mot-tests?make=LEXMOTO&test_year=2025")\nres = Net::HTTP.get_response(uri, {"Authorization" => "Bearer YOUR_API_KEY"})
response = requests.get("https://api.govdata.dev/v1/vehicles/mot-tests", headers={"Authorization": "Bearer YOUR_API_KEY"}, params={"make": "LEXMOTO", "test_year": 2025})
const response = await fetch("https://api.govdata.dev/v1/vehicles/mot-tests?make=LEXMOTO&test_year=2025", { headers: { Authorization: "Bearer YOUR_API_KEY" } });
Response
{"data":[{"test_id":1326094865,"test_date":"2025-01-08","test_year":2025,"test_class":"1","test_type":"NT","result":"pass_with_rectification","odometer":{"reading":6,"unit":"mi"},"postcode_area":"BH","make":"LEXMOTO","model":"ADRENALINE 125 EFi","colour":"BLACK","fuel_type":"petrol","cylinder_capacity_cc":125,"first_use_date":"2017-09-05"}],"meta":{"api_version":"v1","licence":"Open Government Licence v3.0","source":"DVSA anonymised MOT test results","source_url":"https://open.data.dvsa.gov.uk/mot-anonymised/index.html","test_year":2025,"coverage":"results_only"},"pagination":{"total":1,"page":1,"per_page":25,"total_pages":1}}
Errors
{"error":{"code":"invalid_parameter","message":"result is not recognised.","documentation_url":"https://docs.govdata.dev/errors/invalid_parameter"}}
Malformed year filters and unknown test-result values return 400. Valid filters with no matches return an empty 200 response; there is no 404 or request-body 422 branch.
Error response
{"error":{"code":"invalid_parameter","message":"fuel_type is not recognised.","documentation_url":"https://docs.govdata.dev/errors/invalid_parameter"}}
MOT pass rates
GET /v1/vehicles/mot-pass-rates reads the importer-built aggregate table, so requests never scan 42.7 million raw rows.
/v1/vehicles/mot-pass-rates
Waiting for request...
Parameters
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
make |
string | No | — | Case-insensitive make prefix. |
model |
string | No | — | Case-insensitive model prefix. |
fuel_type |
string | No | — | Normalised fuel label. |
vehicle_age_band |
string | No | — | 0-3, 4-6, 7-9, 10-14, 15-19, or 20+. |
test_year |
integer | No | 2025 | Test year. |
page |
integer | No | 1 | One-based page. |
per_page |
integer | No | 25 | Results per page, maximum 100. |
curl -H "Authorization: Bearer YOUR_API_KEY" "https://api.govdata.dev/v1/vehicles/mot-pass-rates?make=LEXMOTO&fuel_type=petrol"
uri = URI("https://api.govdata.dev/v1/vehicles/mot-pass-rates?make=LEXMOTO&fuel_type=petrol")\nres = Net::HTTP.get_response(uri, {"Authorization" => "Bearer YOUR_API_KEY"})
response = requests.get("https://api.govdata.dev/v1/vehicles/mot-pass-rates", headers={"Authorization": "Bearer YOUR_API_KEY"}, params={"make": "LEXMOTO", "fuel_type": "petrol"})
const response = await fetch("https://api.govdata.dev/v1/vehicles/mot-pass-rates?make=LEXMOTO&fuel_type=petrol", { headers: { Authorization: "Bearer YOUR_API_KEY" } });
Response
{"data":[{"make":"LEXMOTO","model":"ADRENALINE 125 EFi","first_use_year_band":"2015-2019","vehicle_age_band":"7-9","fuel_type":"petrol","test_year":2025,"statistics":{"tests":2,"passes":2,"pass_rate":1.0}}],"meta":{"api_version":"v1","licence":"Open Government Licence v3.0","source":"DVSA anonymised MOT test results","source_url":"https://open.data.dvsa.gov.uk/mot-anonymised/index.html","test_year":2025,"coverage":"results_only","aggregation":"normal_initial_tests"},"pagination":{"total":1,"page":1,"per_page":25,"total_pages":1}}
Errors
{"error":{"code":"invalid_parameter","message":"fuel_type is not recognised.","documentation_url":"https://docs.govdata.dev/errors/invalid_parameter"}}
A non-integer test year or unknown fuel type returns 400. Valid filters with no matching aggregate return an empty 200 response; there is no 404 or request-body 422 branch.
Common MOT failure reasons
GET /v1/vehicles/mot-failures returns the most common DVSA reason-for-rejection categories for a make and model. It is designed for reliability comparisons and used-vehicle research; requests read only the importer-built aggregate and never scan the 92.5 million detail rows.
/v1/vehicles/mot-failures
Waiting for request...
Parameters
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
make |
string | No | — | Case-insensitive make prefix. |
model |
string | No | — | Case-insensitive model prefix. |
rfr_type |
string | No | — | fail, prs, advisory, minor, or unknown. |
dangerous_only |
boolean | No | false | Only reasons in DVSA's Dangerous deficiency category. |
test_year |
integer | No | 2025 | Test year. |
page |
integer | No | 1 | One-based page. |
per_page |
integer | No | 25 | Results per page, maximum 100. |
curl -H "Authorization: Bearer YOUR_API_KEY" "https://api.govdata.dev/v1/vehicles/mot-failures?make=LEXMOTO&model=MICHIGAN&rfr_type=fail"
uri = URI("https://api.govdata.dev/v1/vehicles/mot-failures?make=LEXMOTO&model=MICHIGAN&rfr_type=fail")\nres = Net::HTTP.get_response(uri, {"Authorization" => "Bearer YOUR_API_KEY"})
response = requests.get("https://api.govdata.dev/v1/vehicles/mot-failures", headers={"Authorization": "Bearer YOUR_API_KEY"}, params={"make": "LEXMOTO", "model": "MICHIGAN", "rfr_type": "fail"})
const response = await fetch("https://api.govdata.dev/v1/vehicles/mot-failures?make=LEXMOTO&model=MICHIGAN&rfr_type=fail", { headers: { Authorization: "Bearer YOUR_API_KEY" } });
Response
{"data":[{"make":"LEXMOTO","model":"MICHIGAN 125","test_year":2025,"rfr_type":"fail","defect_category":"Major","defect_description":"pin or bush excessively worn","dangerous":false,"statistics":{"failures":1,"affected_tests":1}}],"meta":{"api_version":"v1","licence":"Open Government Licence v3.0","source":"DVSA anonymised MOT failure items","source_url":"https://open.data.dvsa.gov.uk/mot-anonymised/index.html","test_year":2025,"coverage":"failure_items","aggregation":"make_model_reason"},"pagination":{"total":1,"page":1,"per_page":25,"total_pages":1}}
failures counts item rows; affected_tests counts distinct anonymised tests. Repeated defects can therefore make the first value larger.
Errors
{"error":{"code":"invalid_parameter","message":"rfr_type is not recognised.","documentation_url":"https://docs.govdata.dev/errors/invalid_parameter"}}
Invalid filters return 400. This collection has no addressable resource, so 404 is not applicable; no request body is validated, so 422 is not applicable.
Traffic count points
GET /v1/vehicles/traffic/count-points returns paginated DfT road traffic statistics. Unit: Location and road-link metadata.
/v1/vehicles/traffic/count-points
Waiting for request...
Parameters
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
count_point |
integer | No | — | DfT count point ID. |
year_from |
integer | No | — | Inclusive first year. |
year_to |
integer | No | — | Inclusive final year. |
lat |
number | No | — | Latitude; requires lng. |
lng |
number | No | — | Longitude; requires lat. |
radius_km |
number | No | 5 | Bounding radius, maximum 100 km. |
page |
integer | No | 1 | One-based page. |
per_page |
integer | No | 25 | Results per page, maximum 100. |
curl -H "Authorization: Bearer YOUR_API_KEY" "https://api.govdata.dev/v1/vehicles/traffic/count-points?year_from=2025"
Net::HTTP.get_response(URI("https://api.govdata.dev/v1/vehicles/traffic/count-points?year_from=2025"))
requests.get("https://api.govdata.dev/v1/vehicles/traffic/count-points", params={"year_from": 2025})
await fetch("https://api.govdata.dev/v1/vehicles/traffic/count-points?year_from=2025")
Response
{"data":[{"count_point_id":51,"year":2025,"road_name":"A3111","road_type":"Major","latitude":49.91550316,"longitude":-6.317558051}],"meta":{"source":"DfT road traffic statistics","coverage":"1993–2025; count-point data 2000–2025"},"pagination":{"total":1,"page":1,"per_page":25,"total_pages":1}}
Errors
{"error":{"code":"invalid_parameter","message":"year_from and year_to must be integers.","documentation_url":"https://docs.govdata.dev/errors/invalid_parameter"}}
Malformed numeric filters return 400. Valid filters with no matches return an empty 200 collection; these endpoints accept no request body, so 404 and 422 do not apply.
Annual average daily flow
GET /v1/vehicles/traffic/aadf returns paginated DfT road traffic statistics. Unit: vehicles/day.
/v1/vehicles/traffic/aadf
Waiting for request...
Parameters
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
count_point |
integer | No | — | DfT count point ID. |
year_from |
integer | No | — | Inclusive first year. |
year_to |
integer | No | — | Inclusive final year. |
road_name |
string | No | — | Road-name substring. |
road_class |
string | No | — | Road category substring. |
region |
string | No | — | Region substring. |
local_authority |
string | No | — | Local-authority substring. |
page |
integer | No | 1 | One-based page. |
per_page |
integer | No | 25 | Results per page, maximum 100. |
curl -H "Authorization: Bearer YOUR_API_KEY" "https://api.govdata.dev/v1/vehicles/traffic/aadf?year_from=2025"
Net::HTTP.get_response(URI("https://api.govdata.dev/v1/vehicles/traffic/aadf?year_from=2025"))
requests.get("https://api.govdata.dev/v1/vehicles/traffic/aadf", params={"year_from": 2025})
await fetch("https://api.govdata.dev/v1/vehicles/traffic/aadf?year_from=2025")
Response
{"data":[{"count_point_id":51,"year":2025,"road_name":"A3111","estimation_method":"Estimated","daily_flows":{"cars_and_taxis":837,"all_motor_vehicles":1430}}],"meta":{"source":"DfT road traffic statistics","unit":"vehicles/day"},"pagination":{"total":1,"page":1,"per_page":25,"total_pages":1}}
Errors
{"error":{"code":"invalid_parameter","message":"year_from and year_to must be integers.","documentation_url":"https://docs.govdata.dev/errors/invalid_parameter"}}
Malformed numeric filters return 400. Valid filters with no matches return an empty 200 collection; these endpoints accept no request body, so 404 and 422 do not apply.
Regional traffic
GET /v1/vehicles/traffic/regional returns paginated DfT road traffic statistics. Unit: vehicle miles; present rounded to nearest million.
/v1/vehicles/traffic/regional
Waiting for request...
Parameters
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
year_from |
integer | No | — | Inclusive first year. |
year_to |
integer | No | — | Inclusive final year. |
region |
string | No | — | Region substring. |
breakdown |
string | No | — | vehicle_type or road_type. |
page |
integer | No | 1 | One-based page. |
per_page |
integer | No | 25 | Results per page, maximum 100. |
curl -H "Authorization: Bearer YOUR_API_KEY" "https://api.govdata.dev/v1/vehicles/traffic/regional?year_from=2025"
Net::HTTP.get_response(URI("https://api.govdata.dev/v1/vehicles/traffic/regional?year_from=2025"))
requests.get("https://api.govdata.dev/v1/vehicles/traffic/regional", params={"year_from": 2025})
await fetch("https://api.govdata.dev/v1/vehicles/traffic/regional?year_from=2025")
Response
{"data":[{"year":2025,"region_name":"South West","breakdown":"vehicle_type","vehicle_miles":{"all_motor_vehicles":23856600000}}],"meta":{"unit":"vehicle miles","presentation":"Round to the nearest million vehicle miles when presenting regional or local-authority figures."},"pagination":{"total":1,"page":1,"per_page":25,"total_pages":1}}
Errors
{"error":{"code":"invalid_parameter","message":"year_from and year_to must be integers.","documentation_url":"https://docs.govdata.dev/errors/invalid_parameter"}}
Malformed numeric filters return 400. Valid filters with no matches return an empty 200 collection; these endpoints accept no request body, so 404 and 422 do not apply.
Local-authority traffic
GET /v1/vehicles/traffic/local-authority returns paginated DfT road traffic statistics. Unit: vehicle miles; present rounded to nearest million.
/v1/vehicles/traffic/local-authority
Waiting for request...
Parameters
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
year_from |
integer | No | — | Inclusive first year. |
year_to |
integer | No | — | Inclusive final year. |
local_authority |
string | No | — | Local-authority substring. |
page |
integer | No | 1 | One-based page. |
per_page |
integer | No | 25 | Results per page, maximum 100. |
curl -H "Authorization: Bearer YOUR_API_KEY" "https://api.govdata.dev/v1/vehicles/traffic/local-authority?year_from=2025"
Net::HTTP.get_response(URI("https://api.govdata.dev/v1/vehicles/traffic/local-authority?year_from=2025"))
requests.get("https://api.govdata.dev/v1/vehicles/traffic/local-authority", params={"year_from": 2025})
await fetch("https://api.govdata.dev/v1/vehicles/traffic/local-authority?year_from=2025")
Response
{"data":[{"year":2025,"local_authority_name":"Isles of Scilly","vehicle_miles":{"all_motor_vehicles":1400000}}],"meta":{"unit":"vehicle miles","presentation":"Round to the nearest million vehicle miles when presenting regional or local-authority figures."},"pagination":{"total":1,"page":1,"per_page":25,"total_pages":1}}
Errors
{"error":{"code":"invalid_parameter","message":"year_from and year_to must be integers.","documentation_url":"https://docs.govdata.dev/errors/invalid_parameter"}}
Malformed numeric filters return 400. Valid filters with no matches return an empty 200 collection; these endpoints accept no request body, so 404 and 422 do not apply.
Data notes
Coverage: result and failure/RfR coverage for the 2025 test year. Failure aggregates become complete after the initial production failure-item import finishes.
Method: pass rates use normal initial tests (NT) with pass, fail, or pass-with-rectification outcomes. A PRS outcome counts as a final pass.
Source and cadence: DVSA anonymised MOT test results, refreshed annually after publication, reused under the Open Government Licence v3.0 with Crown copyright attribution.
Road traffic units and presentation: AADF is measured in vehicles/day. Regional and local-authority annual totals are supplied as unrounded vehicle miles; DfT requires presented figures to be rounded to the nearest million vehicle miles.
Mandatory AADF limitation: link-level AADF figures are modelled estimates, not continuous measurements. Most count points are counted manually once and factored. Consult estimation_method; link and small-area estimates are less robust than regional totals, and newer local-highway-authority counts may be more appropriate.
Failure detail: coded reasons, deficiency categories, and locations are decoded with DVSA's published lookup tables. The dangerous flag is derived from the post-May 2018 Dangerous deficiency category; the legacy D-mark is not used. Unknown codes remain explicitly unknown. Raw per-test failure detail is not exposed: anonymised test IDs are not useful vehicle identifiers, while the consumer use case is served more safely and efficiently by the indexed aggregate.
This is an anonymised statistical dataset. It contains no registration or VIN and is not an official DVSA vehicle-history service. Make/model, fuel, mileage, and first-use values can be incomplete or entered incorrectly; small groups are not predictions of an individual vehicle's reliability.