Hope It Passes

API v1.3

API documentation

The Hope It Passes API returns MOT pass likelihood, Car Care Scores and the components most likely to fail for any vehicle in the DVSA MOT dataset, which covers England, Scotland and Wales and also includes vehicles tested in Northern Ireland by the DVA. There is one endpoint, it takes a registration, and it returns JSON.

Base URL: https://trade.hopeitpasses.com

Quick start

1. Get a free sandbox key. It appears on screen straight away.
2. Call the endpoint with the key in the X-API-Key header:

curl https://trade.hopeitpasses.com/v1/vehicle/YA08FOT \
  -H "X-API-Key: hip_sand_your_key_here"

Python:

import requests

resp = requests.get(
    "https://trade.hopeitpasses.com/v1/vehicle/YA08FOT",
    headers={"X-API-Key": "hip_sand_your_key_here"},
    timeout=10,
)
resp.raise_for_status()
vehicle = resp.json()
print(vehicle["pass_probability"], vehicle["care_score"])

JavaScript (server side; don't put your key in browser code):

const res = await fetch("https://trade.hopeitpasses.com/v1/vehicle/YA08FOT", {
  headers: { "X-API-Key": process.env.HIP_API_KEY },
});
const vehicle = await res.json();

Want to see the output before signing up? The try-it box on the home page returns exactly the same response.

Authentication

Every request to /v1/ needs your key in the X-API-Key header. Sandbox keys start with hip_sand_ and production keys with hip_live_. A missing, unknown or suspended key returns 401. When you move to production, your sandbox key stops working, so switch to the production key shown in the portal.

Treat keys like passwords. Call the API from your server, not from a web page, where anyone could read the key. If a key leaks, rotate it in the portal under Settings: the old key stops working immediately.

GET /v1/vehicle/{registration}

Returns the scores for one vehicle. Spaces and punctuation in the registration are ignored and it isn't case-sensitive, so ya08 fot and YA08FOT are the same.

Example response

A real response, captured 2 October 2026. Values change as new MOT results arrive.

{
  "registration": "YA08FOT",
  "make": "Volkswagen",
  "model": "POLO",
  "year": 2008,
  "mot_expiry": "2027-03-27",
  "last_recorded_mileage": 58404,
  "pass_probability": 63,
  "care_score": 7.2,
  "owner_care_score": 5.6,
  "owner_score_status": "ok",
  "owner_data_confidence": "medium",
  "v5c_issued_date": "2024-10-10",
  "keeper_data_checked_at": "2026-06-30T04:16:33Z",
  "data_confidence": "medium",
  "at_risk": [
    {
      "component": "Tyre",
      "count": 40874,
      "title": "Tyre Wear & Damage",
      "body": "Your tyres are the only thing touching the road. This covers bald tread, cracks, or sidewall damage."
    },
    {
      "component": "Headlamp",
      "count": 25734,
      "title": "Lights & Signals",
      "body": "Usually just a simple blown bulb change, but can sometimes mean a cloudy lens needs polishing or aiming."
    },
    {
      "component": "Windscreen Washer",
      "count": 12590,
      "title": "Driver's View",
      "body": "Anything blocking your view, like a chip in the glass or smeary worn-out wipers."
    }
  ],
  "cohort": {
    "model": "VOLKSWAGEN POLO",
    "generation": "Mk4 (9N/9N3)",
    "mileage_band": "50k_60k"
  }
}

Response fields

FieldTypeDescription
registrationstringThe registration, normalised to upper case with no spaces.
makestringManufacturer, from the MOT record.
modelstringModel family without the make (for example POLO, not a trim level). Empty if we couldn't resolve one.
yearinteger | nullYear of first use or registration.
mot_expirydate | nullYYYY-MM-DD expiry of the most recent passed MOT.
last_recorded_mileageinteger | nullOdometer reading in miles at the most recent MOT with a readable odometer. Readings in km are converted.
pass_probabilityinteger | null0–100. The historical MOT pass rate for the vehicle's cohort (see cohort). null when there isn't enough data for the cohort.
care_scorenumber | null0–10, one decimal place. How well the vehicle has been maintained over its whole MOT history, worked out at the time of your request. null if the vehicle has no MOT history yet.
owner_care_scorenumber | null0–10. The same score using only the MOTs since the current V5C was issued (see v5c_issued_date), worked out at the time of your request. null unless owner_score_status is ok; in that case use care_score.
owner_score_statusstringWhy there is or isn't a keeper score:
ok: owner_care_score is filled in.
no_mot_since_v5c: the V5C was issued after the latest MOT, so there's no MOT for the current keeper yet.
keeper_data_unavailable: we don't hold a V5C date for this vehicle. Either we have no DVLA record for it, or the record we hold describes a different vehicle (for example, a private plate that has since moved to another car).
no_mot_history: the vehicle has no MOTs on record.
owner_data_confidencestring | nullhigh, medium or low: how much history supports the keeper score. high is three or more MOTs since the V5C (a fail and its retest within 30 days count as one), medium is two and low is one. A V5C issued in the last 12 months is always low. null when there's no keeper score.
v5c_issued_datedate | nullYYYY-MM-DD date the current V5C (logbook) was issued, from DVLA. The keeper score covers MOTs on or after this date. It's usually the date the current keeper took over, but DVLA also issues a new V5C after a change of name or address, or a plate transfer, so the window can start later than the keeper's actual purchase.
keeper_data_checked_atdatetime | nullWhen we last fetched the vehicle's DVLA record (UTC, ISO 8601, e.g. 2026-06-30T14:02:11Z). If the car has changed hands since then, the keeper score can still describe the previous keeper. null if we've never fetched it.
data_confidencestring | nullhigh, medium or low: how much history supports the keeper score (or the lifetime score, if there's no keeper score). A V5C issued in the last 12 months is always low. Kept for compatibility: use owner_data_confidence for the keeper score on its own.
at_riskarrayUp to five components, most frequent first, that fail most often for the vehicle's cohort. Empty when pass_probability is null.
at_risk[].componentstringComponent name from our taxonomy of 213 components.
at_risk[].countintegerHow many failures of this component were recorded in the cohort. Use it for ranking; it isn't a probability.
at_risk[].title, .bodystringA plain-English heading and explanation, suitable for showing to a car buyer or driver.
cohortobject | nullThe group of vehicles the prediction is based on: model, generation and mileage_band (10,000-mile bands up to 100k, then wider).

We may add new fields to the response. Build your integration to ignore fields it doesn't recognise. We won't remove or rename a field without a new version (see Changelog).

Errors

Errors return JSON with an error message, and usually a hint.

StatusMeaningWhat to do
400The registration was empty after removing spaces and punctuation.Check the input.
401API key missing, unknown or suspended.Check the X-API-Key header.
404No MOT record for this registration.Common for cars under three years old (four in Northern Ireland) and mistyped plates. This isn't a fault.
429You've used your key's lookups for the rolling 30-day period.Wait for older calls to drop out of the window, or request more access.
5xxSomething went wrong on our side.Retry with backoff. If it keeps happening, email us.
HTTP/1.1 404 Not Found
{"error": "Vehicle not found", "registration": "AB12CDE"}

Limits & keys

ProductSandbox (free)Production
REST API100 lookups per rolling 30 daysAgreed volume, priced per lookup
Embed widget1,000 views per dayAgreed volume
Bulk CSV100 rows per calendar month (portal account)Agreed volume

Sandbox and production return the same data in the same format. Every /v1/ response carries headers so you can track usage without checking the dashboard:

X-RateLimit-Limit: 100
X-RateLimit-Remaining: 73
X-RateLimit-Window: 30d
X-API-Version: 1.3

Each lookup counts towards your quota, including ones that return 404. Calls rejected with 401 or 429 don't count.

Embed widget

The widget shows the pass likelihood and top risk areas for a registration on your own page, for example a used-car listing. It's an iframe, so there's no script to install, and its styling can't clash with your site. It doesn't show Care Scores: those are for authenticated API use only.

<!-- Full: 340 × 420 -->
<iframe src="https://trade.hopeitpasses.com/widget/YA08FOT?key=hip_wid_your_widget_key&variant=full"
        width="340" height="420" frameborder="0" loading="lazy"
        title="MOT pass likelihood"></iframe>

<!-- Compact: 180 × 160 -->
<iframe src="https://trade.hopeitpasses.com/widget/YA08FOT?key=hip_wid_your_widget_key&variant=compact"
        width="180" height="160" frameborder="0" loading="lazy"
        title="MOT pass likelihood"></iframe>
ParameterDescription
{registration}In the path. Same normalising rules as the API.
keyYour public widget key (starts hip_wid_). Never use your hip_sand_ API key here: widget keys only work for the widget. It's on your signup page and the portal's MOT API page. Widget views are counted separately from API lookups.
variantfull (default) or compact.

When there's nothing to show (unknown registration, not enough data, or the daily view limit has been reached), the widget shows a short neutral message rather than an error page. The widget key is visible in your page source, which is why it can't call the API and why widget views have their own daily cap. Only the registration is sent to us, never anything about your visitor.

Bulk CSV upload

For teams who'd rather work in a spreadsheet. Create a portal account, upload a .csv or .xlsx file, and download it again with the scores added as new columns. Your own columns are kept exactly as they were. Download the template.

The registration column can be called VRM, Registration, Reg, Plate, Reg_Number, RegNo or Vehicle_Registration (not case-sensitive). The following columns are added:

ColumnDescription
HIP_StatusFound, Not Found or Invalid
HIP_Pass_ProbabilitySame as pass_probability
HIP_Car_Care_Score_LifetimeSame as care_score
HIP_Car_Care_Score_OwnerSame as owner_care_score
HIP_Car_Care_Score_Owner_ConfidenceConfidence for the keeper score
HIP_Avg_Service_Interval_MilesTypical annual mileage, from the vehicle's MOT odometer readings. (The column name is kept for compatibility.)
HIP_MOT_ExpirySame as mot_expiry
HIP_Current_MileageSame as last_recorded_mileage
HIP_GenerationThe model and generation used for the prediction
HIP_Top_Risk_1 … 3The top three at_risk components
HIP_Owner_Score_StatusSame as owner_score_status: why HIP_Car_Care_Score_Owner is or isn't filled in
HIP_V5C_Issued_DateSame as v5c_issued_date

Blank cells mean the same as null in the API: not enough data. If a file has more rows than your remaining monthly allowance, we score the rows that fit and mark the job limit reached.

Files can be up to 10 MB and 5,000 rows, and we process one file per account at a time. Split larger lists into several files.

We delete your uploaded file as soon as it's processed, and the enriched file 30 days later, so download it before then.

Methodology

Source. Everything comes from DVSA MOT history: 138M+ vehicles and more than 15 years of tests. New MOT results are added and scored continuously, so a vehicle's scores update soon after each new test is published. It contains public sector information licensed under the Open Government Licence v3.0.

Coverage. Every quarter we check the dataset against the Department for Transport's vehicle licensing statistics, model by model, and publish the result: see data coverage.

Pass likelihood is the historical MOT pass rate for vehicles of the same model and generation in the same mileage band. It describes how comparable cars have done, not the condition of this particular car.

At-risk components. The DVSA records defects as free text. We map 7,300+ distinct defect descriptions onto 213 components, then count failures per component within each cohort.

Car Care Score is calculated from the vehicle's own MOT history and combines five factors: first-time pass rate, advisories per test, advisories repeated and not fixed, major and dangerous defects, and annual mileage. The keeper score repeats the calculation for the MOTs since the current V5C was issued, which is usually when the current keeper took over (a new V5C also follows a change of address or a plate transfer). Both scores are worked out each time you look a vehicle up, so they always include the latest MOT and describe the vehicle currently on the registration. More than 44M vehicles have a score, and the number grows daily.

Limits. This is history-based evidence, not an inspection. Vehicles under three years old (four in Northern Ireland, where the DVA runs MOTs) have no MOT history yet.

Changelog

Additive changes (new fields, new headers) can be released at any time. Breaking changes will get a new version path (/v2/), with /v1/ kept running alongside it and at least 90 days' notice by email.

VersionDateChanges
1.3October 2026care_score is now worked out at the time of each request too, so it always describes the vehicle currently on the registration (previously, after a private plate moved to another car, it could describe the previous car) and is filled in for every vehicle with an MOT history.
1.2October 2026Added owner_score_status, owner_data_confidence, v5c_issued_date and keeper_data_checked_at, plus the HIP_Owner_Score_Status and HIP_V5C_Issued_Date bulk columns (at the end of the file). owner_care_score is now worked out at the time of each request, so it includes the latest MOTs and is filled in for many more vehicles.
1.1September 2026Added last_recorded_mileage, cohort and the X-RateLimit-* / X-API-Version headers. pass_probability and at_risk are now filled in for many more vehicles, using the latest MOT odometer reading when no other mileage is held. model no longer repeats the make.
1.0July 2026First release: GET /v1/vehicle/{registration}, embed widget, bulk CSV upload.

Support

Email hip@hopeitpasses.com. We reply within one UK business day. If you're reporting a problem, include the registration and the time of the request. Please don't send your API key.

Service health: /health returns {"status": "ok"} when the API and database are up. You can point your own monitoring at it.