API reference
Every endpoint of the DealerPricing Developer API with its request fields, an example request and answer, every response field and the errors it can return. Generated from the same description as the OpenAPI file, updated 6 November 2026.
Base URL and authentication
Base URL: https://www.dealerpricing.co.uk/api. Paths below start with /v1.
Send your key in the X-API-Key header on every request. Live keys start dp_live_ and are charged; sandbox keys start dp_sandbox_ and are free. Create keys on the Keys page of the developer portal. A key is shown once, when it is created, so store it somewhere safe. Call the API from your server: never put a key in browser code, a mobile app or a public repository.
Requests are JSON (Content-Type: application/json). Registrations are case-insensitive and spaces are ignored.
Sandbox
Sandbox keys answer only for a published set of test registrations, listed with the case each one exercises at GET /v1/sandbox/vrms (no key needed). Any other registration answers 404 SANDBOX_VEHICLE_NOT_AVAILABLE (version 1: SANDBOX_VRM_NOT_ALLOWED). Sandbox data may not be accurate and is for testing only: never show it to end users or use it for decisions. Every sandbox answer carries meta.sandbox: true and a notice.
The examples on this page use sandbox registrations: SA22TLZ (general record), RO21PAD (write-off and finance), SA22LZH (market data) and SK22AOM (service history). Sandbox valuations are frozen and ignore mileage; sandbox service history requests complete about 20 seconds after they are made. For SK22AOM the service history is a generic sample report: made-up records marked sample: true with a sample_notice, and a sample PDF headed "SAMPLE DATA - NOT A REAL VEHICLE". To test the other endings, RE22MOA ends with no records and RO22AXH ends as failed.
Sandbox limits: 2 a second, 30 a minute and 300 a day per key, and 1,000 a day across all sandbox keys on an account.
Answers, charging and nulls
A successful answer is { "success": true, "data": ..., "meta": ... }. meta carries request_id, cost_pence, balance_pence and is_sandbox, plus plan_credit_pence and from_plan_credit_pence while you hold plan credit. An error is { "success": false, "error": { "code", "message", "details", "type", "retryable", "charged", "doc_url" }, "meta": { "request_id", "error_codes_version" } }. Successful answers can carry meta.warnings: facts about the answer that are not errors, such as a field that was not used.
- Live calls are charged on success at your package price. The price is taken when the request starts and refunded before the answer goes out if it ends in an error, so errors cost nothing.
- Service history is charged when the request is accepted and refunded in full when the search finds no records or fails because of a fault. Its callbacks and webhook events send fields with no value as
nullinstead of leaving them out. - Fields with no value are left out of the answer. An empty list is a real answer (for example no plate changes).
- Provenance is the exception: its fields are always present and
nullmeans "not held for this check", never "no". rawblocks pass the source record through for reference. Their layout can change without notice: build on the documented fields.
Rate limits and headers
Live calls count towards your package limits across all live keys on the account. A key can be set lower than its package, never higher. Daily limits reset at midnight UK time. Some endpoints also have a daily cap per account (see pricing).
| Package | Per second | Per minute | Per day |
|---|---|---|---|
| Pay As You Go | 2 | 60 | 500 |
| Starter | 5 | 100 | 2,000 |
| Pro | 10 | 300 | 5,000 |
| Enterprise | 20 | 600 | 15,000 |
| Sandbox (per key) | 2 | 30 | 300 |
Request headers
| Header | Meaning | Where |
|---|---|---|
| DP-Error-Codes | Optional. 1 or 2: the error code version for this request, overriding your account setting. From 1 February 2027 version 2 is the default for everyone; version 1 can still be asked for until 2 May 2027. | Every keyed endpoint |
Response headers
| Header | Meaning | Sent |
|---|---|---|
| X-Request-Id | Id of this request (also in the body). | Every response |
| X-RateLimit-Limit-Second | This key's per-second limit. | After the key is accepted |
| X-RateLimit-Limit-Minute | This key's per-minute limit. | After the key is accepted |
| X-RateLimit-Limit-Day | This key's daily limit (UK calendar day). | After the key is accepted |
| X-RateLimit-Remaining-Second | Requests left this second on this key. | After the key is accepted |
| X-RateLimit-Remaining-Minute | Requests left this minute on this key. | After the key is accepted |
| X-RateLimit-Remaining-Day | Requests left today on this key. | After the key is accepted |
| X-RateLimit-Reset-Second | Seconds until the per-second window resets. | After the key is accepted |
| X-RateLimit-Reset-Minute | Seconds until the per-minute window resets. | After the key is accepted |
| X-RateLimit-Reset-Day | Seconds until midnight UK time. | After the key is accepted |
| X-Package-Limit-Second | Your package's per-second limit across all live keys. | Live keys |
| X-Package-Limit-Minute | Your package's per-minute limit across all live keys. | Live keys |
| X-Package-Limit-Day | Your package's daily limit across all live keys. | Live keys |
| X-Package-Remaining-Day | Requests left today on your package. | Live keys |
| X-Endpoint-Limit-Day | Your daily cap for this endpoint. | Live keys, capped endpoints |
| X-Endpoint-Remaining-Day | Requests left today on this endpoint. | Live keys, capped endpoints |
| Retry-After | Seconds to wait before retrying. | 429 responses |
| DP-Error-Codes | The error code version used for this answer: 1 or 2. | Every response from a keyed endpoint |
Errors
Match on error.code, not on the message. Every error carries details.request_id, type (whose side the fault is on), retryable, charged and doc_url. The table lists the version 2 codes: the default for new accounts and, from 1 February 2027, for everyone. Accounts still on version 1 get the older code with next_code; choose per request with the DP-Error-Codes header. Every code, the version 1 codes they replace and the timetable are at error codes.
| Status | Codes | Meaning |
|---|---|---|
| 400 | MISSING_PARAMETERINVALID_PARAMETERINVALID_VRMINVALID_VINVIN_NOT_SUPPORTEDINVALID_REQUEST_ID | The request needs changing before it can succeed. Not charged. |
| 401 | MISSING_API_KEYINVALID_API_KEYAPI_KEY_REVOKED | The API key is missing, not recognised or revoked. |
| 402 | INSUFFICIENT_BALANCE | The balance (plus any plan credit) does not cover the price. Top up and retry. |
| 403 | ACCOUNT_DISABLEDEMAIL_NOT_VERIFIEDSANDBOX_NOT_SUPPORTED | The key is recognised but this call is not allowed for the account or key. |
| 404 | VEHICLE_NOT_FOUNDSANDBOX_VEHICLE_NOT_AVAILABLEREQUEST_NOT_FOUNDVALUATION_NOT_AVAILABLESPEC_NOT_AVAILABLEHISTORY_NOT_AVAILABLEMARKET_DATA_NOT_AVAILABLESERVICE_HISTORY_NOT_AVAILABLE | No such vehicle or request, or a known vehicle with no data of this kind. Not charged. |
| 410 | REQUEST_EXPIRED | A service history request finished more than 7 days ago and can no longer be read. |
| 429 | RATE_LIMIT_EXCEEDEDSANDBOX_DAILY_LIMIT_REACHEDPACKAGE_LIMIT_EXCEEDEDENDPOINT_DAILY_CAP_REACHEDTOO_MANY_OPEN_REQUESTS | A limit was reached. Wait for Retry-After seconds. Never charged. |
| 500 | INTERNAL_ERROR | Something failed on our side. Not charged. Safe to retry; quote the request id if it persists. |
| 502 | SOURCE_UNAVAILABLE | A data source failed or did not answer in time, after our automatic retries. Not charged. Safe to retry. |
| 503 | SOURCE_LIMIT_REACHED | A data source will not answer us again until its daily reset. Not charged. Retry later. |
Webhooks
Add HTTPS endpoints on the Webhooks page of the developer portal and choose the events. Every event, and every service history callback, is signed with the DP-Signature header: verify it before you trust the body. The OpenAPI file describes each event under webhooks.
| Event | Meaning |
|---|---|
| balance.low | Your prepaid balance has dropped below your low balance level. |
| balance.topped_up | A top-up has been paid and added to your balance. |
| key.created | An API key was created on your account. |
| key.revoked | An API key on your account was revoked. |
| plan.changed | Your API plan changed and the new per-call prices apply now. |
| plan.change_scheduled | A plan change has been booked for the end of the current period. |
| subscription.payment_failed | A subscription renewal payment did not go through. |
| service_history.completed | A service history request finished. Same body as the request callback. |
| service_history.failed | A service history request failed and was refunded. Same body as the request callback. |
| limit.daily_cap_reached | A daily limit on your plan was reached; further live requests are refused until midnight UK time. |
| account.email_changed | The sign-in email address of your developer account changed. |
| api.notice | An announcement about the API, for example a change to error codes. |
Vehicle data
/v1/vehicle/decodeVehicle decode
The vehicle's identity and registration record: make, model, engine, power, keepers, plate changes, colour history, import and export, scrapped status and vehicle tax rates. Not charged when the answer is an error (404, 502 and so on): the price taken up front is refunded before the response is sent.
Response versions: the fields below are the version 2 shape. Version 1 also sends insurance_group, acceleration_0_62_mph, import_export.import_date and tax.first_year_six_months_gbp (never filled), and sends registration_date and date_of_manufacture as timestamps (registration_date falls back to the first date abroad for an import). Choose with the DP-Error-Codes header or the account setting (API version 2).
Price per successful call: Pay As You Go £0.16, Starter £0.10, Pro £0.08, Enterprise £0.06. Sandbox keys are free.
Request body (JSON)
Send vrm (UK registration, 2 to 8 letters and digits, spaces ignored) or vin (5 to 17 letters and digits, no I, O or Q). When both are sent, the VIN is used.
| Field | Type | Notes |
|---|---|---|
| vrm | string | UK registration. 2 to 8 letters and digits after spaces are removed. |
| vin | string | Vehicle identification number. 5 to 17 letters and digits, no I, O or Q. |
Example request
curl -X POST https://www.dealerpricing.co.uk/api/v1/vehicle/decode \
-H "X-API-Key: your_sandbox_key" \
-H "Content-Type: application/json" \
-d '{"vrm":"SA22TLZ"}'Example answer
{
"success": true,
"data": {
"vehicle": {
"vrm": "SA22TLZ",
"make": "Volkswagen",
"model": "Polo",
"model_description": "Polo Life TSi",
"variant": "Life TSI",
"series": "AW/BZ",
"year": 2022,
"registration_date": "2022-04-23",
"date_of_manufacture": "2022-04-23",
"colour": "BLUE",
"fuel_type": "PETROL",
"powertrain_type": "ICE",
"transmission": "Manual",
"gears": 5,
"drive_type": "4x2",
"body_type": "Hatchback",
"wheelplan": "2 AXLE RIGID BODY",
"engine_capacity_cc": 999,
"engine_capacity_litres": 1,
"engine_number": "DLAC899781",
"vin": "WVWZZZAWZNU040975",
"doors": 5,
"seats": 5,
"co2_emissions": 106,
"euro_status": "6d",
"power_bhp": 93.7,
"power_kw": 70,
"torque_nm": 175,
"top_speed_mph": 116,
"fuel_consumption_combined": 54.3,
"gross_weight_kg": 1620,
"kerb_weight_kg": 1097,
"mass_in_service_kg": 1219,
"previous_keepers": 0,
"colour_changes": 0,
"registration": {
"first_registered": "2022-04-23",
"first_registered_uk": "2022-04-23",
"used_before_first_registration": false,
"cherished_transfer_marker": false
},
"keeper_history": [],
"v5c_issue_dates": [
"2022-04-23"
],
"plate_changes": [],
"colour_history": {
"current": "BLUE",
"original": "BLUE",
"changes": 0
},
"import_export": {
"is_imported": false,
"imported_from_ni": false,
"imported_from_outside_eu": false,
"is_exported": false
},
"scrapped": {
"is_scrapped": false,
"is_unscrapped": false,
"certificate_of_destruction": false
},
"tax": {
"co2_g_per_km": 118,
"first_year_twelve_months_gbp": 455,
"standard_six_months_gbp": 110,
"standard_twelve_months_gbp": 200,
"premium_vehicle_six_months_gbp": 352,
"premium_vehicle_twelve_months_gbp": 640
},
"dvla": {
"make": "VOLKSWAGEN",
"model": "POLO LIFE TSI",
"body_type": "5 DOOR HATCHBACK",
"fuel_type": "PETROL",
"wheelplan": "2 AXLE RIGID BODY",
"engine_capacity_cc": 999,
"power_kw": 70,
"seats": 5,
"gross_weight_kg": 1620,
"mass_in_service_kg": 1219,
"power_to_weight_ratio": 0,
"max_braked_trailer_mass_kg": 1000,
"max_unbraked_trailer_mass_kg": 580
},
"power_ps": 95,
"torque_lb_ft": 129.2,
"top_speed_kph": 187,
"acceleration_0_100_kph": 10.8,
"fuel_consumption_combined_l_100km": 5.2,
"country_of_origin": "South Africa",
"model_introduction_date": "2021-07-05",
"model_start_date": "2021-10-13",
"classification": {
"type_approval_category": "M1",
"vehicle_class": "Car",
"taxation_class": "Car",
"market_sector_code": "AA"
},
"engine": {
"code": "1.0 95PS EU6",
"manufacturer": "Volkswagen Cars",
"cylinders": 3,
"cylinder_arrangement": "Inline",
"aspiration": "Turbocharged",
"valves_per_cylinder": 4,
"valve_gear": "DOHC",
"bore_mm": 74,
"stroke_mm": 76,
"location": "Front"
},
"dimensions": {
"length_mm": 4074,
"width_mm": 1751,
"height_mm": 1451,
"wheelbase_mm": 2552
},
"weights": {
"gross_combined_weight_kg": 1620,
"gross_train_weight_kg": 2620
},
"body": {
"wheelbase_type": "Short Wheelbase",
"axles": 2,
"fuel_tank_capacity_litres": 40,
"platform_name": "MQB A0",
"platform_shared_across_models": true
},
"safety": {
"ncap_star_rating": 5,
"ncap_adult_percent": 96,
"ncap_child_percent": 85,
"ncap_pedestrian_percent": 76,
"ncap_safety_assist_percent": 59
},
"warranty": {
"manufacturer_warranty_months": 36,
"manufacturer_warranty_miles": 60000
},
"raw": {}
}
},
"meta": {
"request_id": "req_3f9a1c0d2b7e4a5f6c8d9e01",
"cost_pence": 16,
"balance_pence": 984,
"is_sandbox": false
}
}Illustrative values, shortened. Field names and types are exact.
Every field in data (198)
| Field | Type | Notes |
|---|---|---|
| vehicle | object | The vehicle. Fields and groups not held for this vehicle are omitted. |
| vehicle.vrm | string | Registration. |
| vehicle.make | string | |
| vehicle.model | string | |
| vehicle.model_description | string | |
| vehicle.variant | string | |
| vehicle.series | string | |
| vehicle.year | integer | Year of manufacture. |
| vehicle.registration_date | string (date) | Date first registered in the UK. Left out for an import with no UK date: registration.first_registered holds the first date abroad. |
| vehicle.date_of_manufacture | string (date) | |
| vehicle.colour | string | |
| vehicle.fuel_type | string | |
| vehicle.powertrain_type | string | |
| vehicle.transmission | string | |
| vehicle.gears | integer | |
| vehicle.drive_type | string | |
| vehicle.body_type | string | |
| vehicle.wheelplan | string | |
| vehicle.engine_capacity_cc | integer | |
| vehicle.engine_capacity_litres | number | |
| vehicle.engine_number | string | |
| vehicle.vin | string | |
| vehicle.doors | integer | |
| vehicle.seats | integer | |
| vehicle.co2_emissions | number | g/km: the manufacturer figure. The figure on the UK vehicle register is tax.co2_g_per_km and can differ. |
| vehicle.euro_status | string | |
| vehicle.power_bhp | number | |
| vehicle.power_kw | number | |
| vehicle.torque_nm | number | |
| vehicle.top_speed_mph | number | |
| vehicle.acceleration_0_60_mph | number | Seconds. |
| vehicle.fuel_consumption_combined | number | mpg. |
| vehicle.fuel_consumption_urban | number | mpg. |
| vehicle.fuel_consumption_extra_urban | number | mpg. |
| vehicle.gross_weight_kg | number | |
| vehicle.kerb_weight_kg | number | |
| vehicle.mass_in_service_kg | number | |
| vehicle.previous_keepers | integer | Previous keepers: the highest count in the keeper history, and 0 when the record holds a keeper history with no changes. Left out when the record holds no keeper history. |
| vehicle.colour_changes | integer | |
| vehicle.registration | object | |
| vehicle.registration.first_registered | string (date) | First registered anywhere (for an import, the first date abroad). |
| vehicle.registration.first_registered_uk | string (date) | |
| vehicle.registration.used_before_first_registration | boolean | |
| vehicle.registration.cherished_transfer_marker | boolean | |
| vehicle.keeper_history | array of object | One row per keeper change, latest first. An empty list is a real answer. |
| vehicle.keeper_history[].previous_keepers | integer | The number of previous keepers at this change. |
| vehicle.keeper_history[].keeper_start_date | string (date) | |
| vehicle.keeper_history[].previous_keeper_disposal_date | string (date) | |
| vehicle.v5c_issue_dates | array of string (date) | The date each registration document (V5C) was issued. |
| vehicle.plate_changes | array of object | An empty list is a real answer: the record holds no plate changes. |
| vehicle.plate_changes[].previous_vrm | string | |
| vehicle.plate_changes[].current_vrm | string | |
| vehicle.plate_changes[].date_of_transaction | string (date) | |
| vehicle.plate_changes[].date_of_receipt | string (date) | |
| vehicle.plate_changes[].transfer_type | string | |
| vehicle.colour_history | object | |
| vehicle.colour_history.current | string | |
| vehicle.colour_history.original | string | |
| vehicle.colour_history.previous | string | |
| vehicle.colour_history.changes | integer | |
| vehicle.colour_history.last_change_date | string (date) | |
| vehicle.import_export | object | |
| vehicle.import_export.is_imported | boolean | |
| vehicle.import_export.imported_from_ni | boolean | |
| vehicle.import_export.imported_from_outside_eu | boolean | |
| vehicle.import_export.is_exported | boolean | |
| vehicle.import_export.export_date | string (date) | |
| vehicle.scrapped | object | |
| vehicle.scrapped.is_scrapped | boolean | |
| vehicle.scrapped.is_unscrapped | boolean | |
| vehicle.scrapped.scrap_date | string (date) | |
| vehicle.scrapped.certificate_of_destruction | boolean | |
| vehicle.tax | object | Vehicle excise duty as registered. Rates are in pounds. |
| vehicle.tax.co2_g_per_km | number | The figure on the UK vehicle register. |
| vehicle.tax.co2_band | string | |
| vehicle.tax.tax_band | string | |
| vehicle.tax.first_year_twelve_months_gbp | number | The first-year rate for the vehicle's band. Filled for vehicles long past their first year too: it is the band's rate, not proof the vehicle paid it. |
| vehicle.tax.standard_six_months_gbp | number | |
| vehicle.tax.standard_twelve_months_gbp | number | |
| vehicle.tax.premium_vehicle_six_months_gbp | number | The rate a vehicle in this band pays IF it cost over £40,000 new. Filled for every vehicle registered from April 2017: it is not proof the vehicle pays it. |
| vehicle.tax.premium_vehicle_twelve_months_gbp | number | As premium_vehicle_six_months_gbp. |
| vehicle.dvla | object | The values exactly as held on the UK vehicle register. The top-level fields prefer manufacturer model data when it is held, so make, model, body_type, power_kw and seats can be written differently here. |
| vehicle.dvla.make | string | |
| vehicle.dvla.model | string | |
| vehicle.dvla.body_type | string | |
| vehicle.dvla.fuel_type | string | |
| vehicle.dvla.wheelplan | string | |
| vehicle.dvla.engine_capacity_cc | integer | |
| vehicle.dvla.power_kw | number | |
| vehicle.dvla.seats | integer | |
| vehicle.dvla.gross_weight_kg | number | |
| vehicle.dvla.mass_in_service_kg | number | |
| vehicle.dvla.power_to_weight_ratio | number | |
| vehicle.dvla.max_braked_trailer_mass_kg | number | |
| vehicle.dvla.max_unbraked_trailer_mass_kg | number | |
| vehicle.power_ps | number | From the manufacturer model data: left out when it is not held for this vehicle (meta.model_data_held is then false). |
| vehicle.torque_lb_ft | number | |
| vehicle.top_speed_kph | number | |
| vehicle.acceleration_0_100_kph | number | Seconds (0 to 100 km/h, the same test as 0 to 62 mph). |
| vehicle.fuel_consumption_combined_l_100km | number | |
| vehicle.fuel_consumption_urban_l_100km | number | |
| vehicle.fuel_consumption_extra_urban_l_100km | number | |
| vehicle.country_of_origin | string | |
| vehicle.model_introduction_date | string (date) | When this model was introduced. |
| vehicle.model_start_date | string (date) | First build date of this model version. |
| vehicle.model_end_date | string (date) | Last build date of this model version (left out while it is still built). |
| vehicle.classification | object | From the manufacturer model data: left out when it is not held for this vehicle (meta.model_data_held is then false). |
| vehicle.classification.type_approval_category | string | For example M1 (car) or N1 (light goods vehicle). |
| vehicle.classification.vehicle_class | string | |
| vehicle.classification.taxation_class | string | |
| vehicle.classification.market_sector_code | string | |
| vehicle.engine | object | Combustion engine details. From the manufacturer model data: left out when it is not held for this vehicle (meta.model_data_held is then false). |
| vehicle.engine.code | string | The manufacturer engine code. |
| vehicle.engine.manufacturer | string | |
| vehicle.engine.cylinders | integer | |
| vehicle.engine.cylinder_arrangement | string | |
| vehicle.engine.aspiration | string | |
| vehicle.engine.valves_per_cylinder | integer | |
| vehicle.engine.valve_gear | string | |
| vehicle.engine.bore_mm | number | |
| vehicle.engine.stroke_mm | number | |
| vehicle.engine.fuel_delivery | string | |
| vehicle.engine.location | string | |
| vehicle.dimensions | object | From the manufacturer model data: left out when it is not held for this vehicle (meta.model_data_held is then false). |
| vehicle.dimensions.length_mm | number | |
| vehicle.dimensions.width_mm | number | |
| vehicle.dimensions.height_mm | number | |
| vehicle.dimensions.wheelbase_mm | number | |
| vehicle.dimensions.load_length_mm | number | Vans. |
| vehicle.weights | object | From the manufacturer model data: left out when it is not held for this vehicle (meta.model_data_held is then false). |
| vehicle.weights.gross_combined_weight_kg | number | |
| vehicle.weights.gross_train_weight_kg | number | |
| vehicle.weights.payload_kg | number | |
| vehicle.weights.unladen_weight_kg | number | |
| vehicle.body | object | From the manufacturer model data: left out when it is not held for this vehicle (meta.model_data_held is then false). |
| vehicle.body.body_shape | string | |
| vehicle.body.cab_type | string | Vans and pick-ups. |
| vehicle.body.wheelbase_type | string | |
| vehicle.body.axles | integer | |
| vehicle.body.fuel_tank_capacity_litres | number | |
| vehicle.body.load_volume_litres | number | Load space of a van. |
| vehicle.body.platform_name | string | |
| vehicle.body.platform_shared_across_models | boolean | |
| vehicle.safety | object | Euro NCAP ratings. From the manufacturer model data: left out when it is not held for this vehicle (meta.model_data_held is then false). |
| vehicle.safety.ncap_star_rating | number | |
| vehicle.safety.ncap_adult_percent | number | |
| vehicle.safety.ncap_child_percent | number | |
| vehicle.safety.ncap_pedestrian_percent | number | |
| vehicle.safety.ncap_safety_assist_percent | number | |
| vehicle.warranty | object | The manufacturer's new-vehicle warranty. From the manufacturer model data: left out when it is not held for this vehicle (meta.model_data_held is then false). |
| vehicle.warranty.manufacturer_warranty_months | integer | |
| vehicle.warranty.manufacturer_warranty_miles | integer | |
| vehicle.supports_over_the_air_updates | boolean | |
| vehicle.drag_coefficient | number | |
| vehicle.ev | object | Electric and plug-in hybrid vehicles only. From the manufacturer model data: left out when it is not held for this vehicle (meta.model_data_held is then false). |
| vehicle.ev.powertrain_type | string | For example BEV or PHEV. |
| vehicle.ev.batteries | array of object | |
| vehicle.ev.batteries[].description | string | |
| vehicle.ev.batteries[].total_capacity_kwh | number | |
| vehicle.ev.batteries[].usable_capacity_kwh | number | |
| vehicle.ev.batteries[].chemistry | string | |
| vehicle.ev.batteries[].voltage | number | |
| vehicle.ev.batteries[].location | string | |
| vehicle.ev.batteries[].warranty_months | integer | |
| vehicle.ev.batteries[].warranty_miles | integer | |
| vehicle.ev.range_tests | array of object | |
| vehicle.ev.range_tests[].test_type | string | For example WLTP. |
| vehicle.ev.range_tests[].combined_range_miles | number | |
| vehicle.ev.range_tests[].combined_range_km | number | |
| vehicle.ev.range_tests[].city_range_miles | number | |
| vehicle.ev.range_tests[].city_range_km | number | |
| vehicle.ev.real_range_miles | number | Expected real-world range. |
| vehicle.ev.real_range_km | number | |
| vehicle.ev.zero_emission_miles | number | Electric-only range (plug-in hybrids). |
| vehicle.ev.miles_per_charge_hour | number | |
| vehicle.ev.wh_per_mile | number | |
| vehicle.ev.max_charge_input_kw | number | |
| vehicle.ev.number_of_charge_ports | integer | |
| vehicle.ev.charge_ports | array of object | |
| vehicle.ev.charge_ports[].port_type | string | |
| vehicle.ev.charge_ports[].location | string | |
| vehicle.ev.charge_ports[].max_charge_kw | number | |
| vehicle.ev.charge_ports[].is_standard | boolean | |
| vehicle.ev.charge_ports[].charge_times_10_to_80_percent | array of object | Average time to charge from 10% to 80% on a charger of this power. |
| vehicle.ev.charge_ports[].charge_times_10_to_80_percent[].charger_kw | number | |
| vehicle.ev.charge_ports[].charge_times_10_to_80_percent[].minutes | number | |
| vehicle.ev.motors | array of object | |
| vehicle.ev.motors[].type | string | |
| vehicle.ev.motors[].location | string | |
| vehicle.ev.motors[].axle_driven | string | |
| vehicle.ev.motors[].power_kw | number | |
| vehicle.ev.motors[].max_torque_nm | number | |
| vehicle.ev.motors[].max_power_rpm | number | |
| vehicle.ev.motors[].manufacturer | string | |
| vehicle.ev.motors[].model | string | |
| vehicle.ev.motors[].regenerative_braking | boolean | |
| vehicle.ev.tesla_supercharger_compatible | boolean | |
| vehicle.raw | object | The source record as received, with supplier bookkeeping removed. Its layout can change without notice: rely on the documented fields instead. |
Errors: 400, 401, 402, 403, 404, 429, 500, 502, 503. Success: 200.
/v1/vehicle/specSpecification
Manufacturer specification: engine, performance, transmission, dimensions, economy, practicality, safety ratings and factory-fitted equipment. Not charged when the answer is an error (404, 502 and so on): the price taken up front is refunded before the response is sent.
Response versions: the fields below are the version 2 shape. Version 1 also sends the engine, performance, transmission, dimensions, economy, practicality, safety and insurance groups and vehicle.variant, series, body_type and powertrain_type. They are empty on live answers (the specification source does not send them) but filled in version 1 sandbox answers. vehicle.year is text in version 1. Choose with the DP-Error-Codes header or the account setting (API version 2).
Price per successful call: Pay As You Go £0.22, Starter £0.20, Pro £0.18, Enterprise £0.15. Sandbox keys are free.
Request body (JSON)
Send vrm (UK registration, 2 to 8 letters and digits, spaces ignored) or vin (5 to 17 letters and digits, no I, O or Q). When both are sent, the VIN is used.
| Field | Type | Notes |
|---|---|---|
| vrm | string | UK registration. 2 to 8 letters and digits after spaces are removed. |
| vin | string | Vehicle identification number. 5 to 17 letters and digits, no I, O or Q. |
Example request
curl -X POST https://www.dealerpricing.co.uk/api/v1/vehicle/spec \
-H "X-API-Key: your_sandbox_key" \
-H "Content-Type: application/json" \
-d '{"vrm":"SA22TLZ"}'Example answer
{
"success": true,
"data": {
"specification": {
"vrm": "SA22TLZ",
"vehicle": {
"make": "Volkswagen",
"model": "Polo Life TSi",
"model_description": "Polo Life TSi",
"year": 2022
},
"factory_equipment": {
"count": 64,
"items": [
{
"category": "Audio and Communications",
"name": "Satellite Navigation",
"description": "Discover Navigation Pro",
"fitment": "Option",
"type": "Item",
"price": 1415
},
{
"category": "Audio and Communications",
"name": "Satellite Navigation",
"description": "Discovr Navigation System",
"fitment": "Option",
"type": "Item",
"price": 570
},
{
"category": "Exterior Features",
"name": "Front Foglights",
"description": "Front Fog Light + Cornering Function",
"fitment": "Option",
"type": "Package",
"price": 210,
"contents": [
"Front Fog Light + Cornering Function"
]
}
]
},
"raw": {}
}
},
"meta": {
"request_id": "req_3f9a1c0d2b7e4a5f6c8d9e01",
"cost_pence": 22,
"balance_pence": 978,
"is_sandbox": false
}
}Illustrative values, shortened. Field names and types are exact.
Every field in data (18)
| Field | Type | Notes |
|---|---|---|
| specification | object | The specification: the vehicle and its factory equipment. Engine, performance, dimensions, economy, safety and the rest of the model data are on /v1/vehicle/decode. |
| specification.vrm | string | Registration (or the VIN when the request was made by VIN). |
| specification.vehicle | object | |
| specification.vehicle.make | string | |
| specification.vehicle.model | string | The full model description, the same as model_description. |
| specification.vehicle.model_description | string | |
| specification.vehicle.year | integer | Year of manufacture. |
| specification.factory_equipment | object | Equipment fitted at the factory for this model. |
| specification.factory_equipment.count | integer | Number of items. |
| specification.factory_equipment.items | array of object | |
| specification.factory_equipment.items[].category | string | |
| specification.factory_equipment.items[].name | string | |
| specification.factory_equipment.items[].description | string | |
| specification.factory_equipment.items[].fitment | string | Standard, Option or Free. |
| specification.factory_equipment.items[].type | string | |
| specification.factory_equipment.items[].price | string or number | Pounds. On options only; can be negative (a credit option). |
| specification.factory_equipment.items[].contents | array of string | What an option pack contains, one line per item. |
| specification.raw | object | The source record as received, with supplier bookkeeping removed. Its layout can change without notice: rely on the documented fields instead. |
Errors: 400, 401, 402, 403, 404, 429, 500, 502, 503. Success: 200.
/v1/vehicle/valuationValuation
Retail, trade, private, part exchange and auction values in pounds. Send mileage for a mileage-adjusted figure; without it the source's standard mileage is used. Sandbox valuations are frozen and ignore mileage (mileage_used shows the mileage the figure was produced at). Not charged when the answer is an error (404, 502 and so on): the price taken up front is refunded before the response is sent.
Response versions: the fields below are the version 2 shape. Version 1 also sends condition_adjustments, mileage_adjustment and vehicle_summary make, model, variant, year, fuel_type and transmission (never filled), and sends valuation_date and vehicle_summary.first_registered as timestamps. Choose with the DP-Error-Codes header or the account setting (API version 2).
Price per successful call: Pay As You Go £0.25, Starter £0.21, Pro £0.19, Enterprise £0.16. Sandbox keys are free.
Request body (JSON)
Send vrm (UK registration, 2 to 8 letters and digits, spaces ignored) or vin (5 to 17 letters and digits, no I, O or Q). When both are sent, the VIN is used.
| Field | Type | Notes |
|---|---|---|
| vrm | string | UK registration. 2 to 8 letters and digits after spaces are removed. |
| vin | string | Vehicle identification number. 5 to 17 letters and digits, no I, O or Q. |
| mileage | integer | Current mileage in miles. Optional. |
Example request
curl -X POST https://www.dealerpricing.co.uk/api/v1/vehicle/valuation \
-H "X-API-Key: your_sandbox_key" \
-H "Content-Type: application/json" \
-d '{"vrm":"SA22TLZ"}'Example answer
{
"success": true,
"data": {
"valuation": {
"vrm": "SA22TLZ",
"mileage_used": 14424,
"currency": "GBP",
"valuations": {
"retail": 15186,
"trade": 12680,
"private": 13360,
"part_exchange": 13303,
"auction": 13286
},
"figures": {
"on_the_road": 19225,
"dealer_forecourt": 15186,
"trade_retail": 14596,
"trade_average": 12680,
"trade_poor": 11769,
"private_clean": 13512,
"private_average": 13360,
"part_exchange": 13303,
"auction": 13286
},
"valuation_date": "2026-09-06",
"valued_at": "2026-09-06T21:11:19.4516511Z",
"valuation_book": "Direct",
"vehicle_summary": {
"description": "Volkswagen Polo Life TSi Life TSI [Petrol / Manual]",
"first_registered": "2022-04-23"
},
"raw": {}
}
},
"meta": {
"request_id": "req_3f9a1c0d2b7e4a5f6c8d9e01",
"cost_pence": 25,
"balance_pence": 975,
"is_sandbox": false
}
}Illustrative values, shortened. Field names and types are exact.
Every field in data (27)
| Field | Type | Notes |
|---|---|---|
| valuation | object | The valuation. Fields not held are omitted. |
| valuation.vrm | string | Registration (or the VIN when the request was made by VIN). |
| valuation.mileage_used | integer | The mileage the figures were produced at, as the source reports it (your mileage only when it reports none). |
| valuation.currency | "GBP" | |
| valuation.valuations | object | Headline figures in pounds. |
| valuation.valuations.retail | number | |
| valuation.valuations.trade | number | |
| valuation.valuations.private | number | |
| valuation.valuations.part_exchange | number | |
| valuation.valuations.auction | number | |
| valuation.figures | object | Every figure held, in pounds. |
| valuation.figures.on_the_road | number | The price when new, not today's value. |
| valuation.figures.dealer_forecourt | number | |
| valuation.figures.trade_retail | number | |
| valuation.figures.trade_average | number | |
| valuation.figures.trade_poor | number | |
| valuation.figures.private_clean | number | |
| valuation.figures.private_average | number | |
| valuation.figures.part_exchange | number | |
| valuation.figures.auction | number | |
| valuation.valuation_date | string (date) | The UK day the valuation was produced. |
| valuation.valued_at | string (date-time) | The exact time the valuation was produced. |
| valuation.valuation_book | string | The source's own label for the valuation set it used (for example "Direct"). For reference only. |
| valuation.vehicle_summary | object | |
| valuation.vehicle_summary.description | string | |
| valuation.vehicle_summary.first_registered | string (date) | |
| valuation.raw | object | The source record as received, with supplier bookkeeping removed. Its layout can change without notice: rely on the documented fields instead. |
Errors: 400, 401, 402, 403, 404, 429, 500, 502, 503. Success: 200.
/v1/vehicle/provenanceKeeps nullsProvenance
Outstanding finance, police stolen record, insurer theft and write-off records, with alerts. Unlike every other endpoint, provenance keeps every field: null means "not held for this check", never "no"; false or 0 means the record was checked and is clear. Keeper history, plate changes, colour and import or export come from /v1/vehicle/decode; mileage and MOT from /v1/vehicle/tax-mot. Not charged when the answer is an error (404, 502 and so on): the price taken up front is refunded before the response is sent.
Response versions: the fields below are the version 2 shape. Version 1 also sends the vehicle record groups (vehicle make, model, variant, year and colour, registration, ownership, plate_changes, colour, import_export, scrapped) and mileage, all always null on this check; the flat police fields stolen.police_recorded, stolen_date, police_force, record_status and record_added_date; and dates as the source writes them (timestamps), with the day corrected where the source wrote a UK midnight as 23:00 the day before. Choose with the DP-Error-Codes header or the account setting (API version 2).
Price per successful call: Pay As You Go £2.99, Starter £2.75, Pro £2.50, Enterprise £2.20. Sandbox keys are free.
Request body (JSON)
Send vrm (UK registration, 2 to 8 letters and digits, spaces ignored) or vin (5 to 17 letters and digits, no I, O or Q). When both are sent, the VIN is used.
| Field | Type | Notes |
|---|---|---|
| vrm | string | UK registration. 2 to 8 letters and digits after spaces are removed. |
| vin | string | Vehicle identification number. 5 to 17 letters and digits, no I, O or Q. |
Example request
curl -X POST https://www.dealerpricing.co.uk/api/v1/vehicle/provenance \
-H "X-API-Key: your_sandbox_key" \
-H "Content-Type: application/json" \
-d '{"vrm":"RO21PAD"}'Example answer
{
"success": true,
"data": {
"provenance": {
"vrm": "RO21PAD",
"check_date": "2026-11-06T10:15:00.000Z",
"vehicle": {
"vin": "LSJWP4396SZ244868"
},
"finance": {
"has_outstanding_finance": true,
"finance_records": 1,
"finance_details": [
{
"agreement_date": "2026-05-01",
"agreement_type": "PCP",
"agreement_number": "REDACTED",
"agreement_term_months": 49,
"finance_company": "BLACK HORSE LTD",
"contact_number": "REDACTED",
"vehicle_description": "MG MOTOR UK MG3 HATCHBACK",
"is_stock_funding": false
}
]
},
"stolen": {
"is_stolen": false,
"police_marked_stolen": false,
"insurer_theft_recorded": false,
"police": {
"marked_stolen": false,
"date_reported_stolen": null,
"police_force": null,
"record_status": null,
"record_added_at": null,
"vrm": null,
"vin": null
},
"insurer_theft_records": []
},
"write_off": {
"is_write_off": true,
"category": "B",
"date": "2026-04-27",
"categories": [
"B"
],
"write_off_records": [
{
"category": "B",
"status": "CAT B BODY CRUSHED",
"loss_date": "2026-04-27",
"entry_date": "2026-04-27",
"insurer": "FIRST CENTRAL UNDERWRITING LIM",
"insurer_branch": "00",
"insurer_code": "487",
"claim_number": "REDACTED",
"contact_number": "03330432022",
"theft_indicator": "66",
"theft_indicator_literal": "NOT STOLEN",
"loss_type": "0"
}
]
},
"alerts": {
"has_alerts": true,
"alert_count": 2,
"alert_list": [
"Outstanding finance recorded",
"Insurance write-off (Category B)"
]
},
"raw": {}
}
},
"meta": {
"request_id": "req_3f9a1c0d2b7e4a5f6c8d9e01",
"cost_pence": 299,
"balance_pence": 701,
"is_sandbox": false
}
}Illustrative values, shortened. Field names and types are exact.
Every field in data (67)
| Field | Type | Notes |
|---|---|---|
| provenance | object | Provenance keeps every field: null means "not held for this check", never "no". false or 0 is a real answer. Rows inside lists omit null fields as elsewhere. Keepers, plate changes, colour, import and export and scrapping are on /v1/vehicle/decode; mileage and MOT on /v1/vehicle/tax-mot. |
| provenance.vrm | string or null | |
| provenance.check_date | string (date-time) or null | When this check ran. |
| provenance.vehicle | object or null | |
| provenance.vehicle.vin | string or null | |
| provenance.finance | object or null | |
| provenance.finance.has_outstanding_finance | boolean or null | true: finance recorded. false: the finance register was checked and holds none. null: not held for this check. |
| provenance.finance.finance_records | integer or null | |
| provenance.finance.finance_details | array of object or null | |
| provenance.finance.finance_details[].agreement_date | string (date) | |
| provenance.finance.finance_details[].agreement_type | string | |
| provenance.finance.finance_details[].agreement_number | string | |
| provenance.finance.finance_details[].agreement_term_months | string or number | |
| provenance.finance.finance_details[].finance_company | string | |
| provenance.finance.finance_details[].contact_number | string | The finance company's phone number. |
| provenance.finance.finance_details[].vehicle_description | string | |
| provenance.finance.finance_details[].is_stock_funding | boolean | true for a UNIT STOCKING agreement: a dealer's stock funding, not a keeper's finance agreement. |
| provenance.stolen | object or null | |
| provenance.stolen.is_stolen | boolean or null | true when at least one source holds a stolen marker (the police record or an insurer theft record); false only when both were checked and are clear. Read police_marked_stolen and insurer_theft_recorded to see which: an insurer theft record can be for a vehicle that was later recovered. |
| provenance.stolen.police_marked_stolen | boolean or null | The police record on its own: true when it marks the vehicle as stolen. null when it could not be checked. |
| provenance.stolen.insurer_theft_recorded | boolean or null | The insurer register on its own: true when it holds a theft record. null when it could not be checked. |
| provenance.stolen.police | object or null | The police record in full. null when the police record could not be checked. |
| provenance.stolen.police.marked_stolen | boolean or null | true: the police record marks the vehicle as stolen. |
| provenance.stolen.police.date_reported_stolen | string (date) or null | |
| provenance.stolen.police.police_force | string or null | |
| provenance.stolen.police.record_status | string or null | The current status on the police record. |
| provenance.stolen.police.record_added_at | string (date-time) or null | When the police record was created. |
| provenance.stolen.police.vrm | string or null | The registration on the police record. |
| provenance.stolen.police.vin | string or null | The VIN on the police record. |
| provenance.stolen.insurer_theft_records | array of object or null | Every insurer register theft record, with all its fields. |
| provenance.stolen.insurer_theft_records[].category | string | Write-off category (A, B, S, N, or the older C and D) or the theft category. |
| provenance.stolen.insurer_theft_records[].status | string | The register's description, for example "CAT S STRUCTURAL DAMAGE". |
| provenance.stolen.insurer_theft_records[].loss_date | string (date) | Date of the loss. |
| provenance.stolen.insurer_theft_records[].entry_date | string (date) | When the record was added to the insurers' register. |
| provenance.stolen.insurer_theft_records[].insurer | string | |
| provenance.stolen.insurer_theft_records[].insurer_branch | string | |
| provenance.stolen.insurer_theft_records[].insurer_code | string | |
| provenance.stolen.insurer_theft_records[].claim_number | string | |
| provenance.stolen.insurer_theft_records[].contact_number | string | The insurer's phone number. |
| provenance.stolen.insurer_theft_records[].theft_indicator | string or number | The register's own theft marker code, as received. Use theft_indicator_literal to read it. |
| provenance.stolen.insurer_theft_records[].theft_indicator_literal | string | "STOLEN" or "NOT STOLEN". |
| provenance.stolen.insurer_theft_records[].loss_type | string | The register's loss type code, as received ("0" on every record seen so far). |
| provenance.stolen.insurer_theft_records[].removed_date | string (date) | When the record was taken off the register, if it was. |
| provenance.write_off | object or null | |
| provenance.write_off.is_write_off | boolean or null | |
| provenance.write_off.category | string or null | The category of the most recent write-off record (latest loss date, else latest entry date). Every record is in write_off_records; every category held is in categories. |
| provenance.write_off.date | string (date) or null | Loss date of the most recent record (its entry date when it has no loss date). |
| provenance.write_off.categories | array of string or null | Every write-off category held, each once, from the newest record. |
| provenance.write_off.write_off_records | array of object or null | Every write-off record, newest first, with all its fields. |
| provenance.write_off.write_off_records[].category | string | Write-off category (A, B, S, N, or the older C and D) or the theft category. |
| provenance.write_off.write_off_records[].status | string | The register's description, for example "CAT S STRUCTURAL DAMAGE". |
| provenance.write_off.write_off_records[].loss_date | string (date) | Date of the loss. |
| provenance.write_off.write_off_records[].entry_date | string (date) | When the record was added to the insurers' register. |
| provenance.write_off.write_off_records[].insurer | string | |
| provenance.write_off.write_off_records[].insurer_branch | string | |
| provenance.write_off.write_off_records[].insurer_code | string | |
| provenance.write_off.write_off_records[].claim_number | string | |
| provenance.write_off.write_off_records[].contact_number | string | The insurer's phone number. |
| provenance.write_off.write_off_records[].theft_indicator | string or number | The register's own theft marker code, as received. Use theft_indicator_literal to read it. |
| provenance.write_off.write_off_records[].theft_indicator_literal | string | "STOLEN" or "NOT STOLEN". |
| provenance.write_off.write_off_records[].loss_type | string | The register's loss type code, as received ("0" on every record seen so far). |
| provenance.write_off.write_off_records[].removed_date | string (date) | When the record was taken off the register, if it was. |
| provenance.alerts | object or null | |
| provenance.alerts.has_alerts | boolean or null | true when any alert below is known; false only when finance, stolen and write-off were all checked and are clear; otherwise null. |
| provenance.alerts.alert_count | integer or null | |
| provenance.alerts.alert_list | array of string or null | Plain-language alerts. Stolen alerts name their source: "Police record: marked as stolen", "Insurer register: theft record". Match on the fields above, not on this text. |
| provenance.raw | object or null | The police, insurer, finance and vehicle code records as received, with supplier bookkeeping removed. Nulls inside are removed. Layout can change without notice. |
Errors: 400, 401, 402, 403, 404, 429, 500, 502, 503. Success: 200.
/v1/vehicle/tax-motTax and MOT
Vehicle tax status, MOT status and full MOT test history from the two free government sources. sources says what each one answered. When the vehicle register holds no record (common for classic vehicles and plates on retention) the vehicle is identified from the MOT record and tax and mot are omitted. Charged whenever either source returns data; 404 VEHICLE_NOT_FOUND (not charged) when neither knows the vehicle; 502 SOURCE_UNAVAILABLE (version 1: UPSTREAM_ERROR; not charged, retry) when neither returns data and one could not be reached.
Price per successful call: Pay As You Go £0.05, Starter £0.04, Pro £0.03, Enterprise £0.02. Sandbox keys are free.
Request body (JSON)
Send vrm (UK registration, 2 to 8 letters and digits, spaces ignored). A vin is refused with 400 VIN_NOT_SUPPORTED (version 1: INVALID_REQUEST): use an endpoint that accepts VINs.
| Field | Type | Notes |
|---|---|---|
| vrm * | string | UK registration. 2 to 8 letters and digits after spaces are removed. |
* required
Example request
curl -X POST https://www.dealerpricing.co.uk/api/v1/vehicle/tax-mot \
-H "X-API-Key: your_sandbox_key" \
-H "Content-Type: application/json" \
-d '{"vrm":"SA22TLZ"}'Example answer
{
"success": true,
"data": {
"vrm": "SA22TLZ",
"sources": {
"dvla": "found",
"mot": "found"
},
"tax": {
"status": "Taxed",
"due_date": "2027-04-01"
},
"mot": {
"status": "Valid",
"expiry_date": "2027-04-23"
},
"vehicle": {
"make": "VOLKSWAGEN",
"model": "POLO LIFE TSI",
"colour": "BLUE",
"year_of_manufacture": 2022,
"first_registration_date": "2022-04-23",
"engine_capacity": 999,
"co2_emissions": 118,
"fuel_type": "PETROL",
"marked_for_export": false,
"type_approval": "M1",
"wheelplan": "2 AXLE RIGID BODY",
"euro_status": "EURO 6 AP",
"date_of_last_v5c_issued": "2022-04-23",
"month_of_first_registration": "2022-04",
"revenue_weight": 1620,
"has_outstanding_recall": "Unknown",
"manufacture_date": "2022-04-23",
"source": "dvla"
},
"mot_history": {
"total_tests": 3,
"tests": [
{
"test_date": "2026-04-24T09:16:59.000Z",
"expiry_date": "2027-04-23",
"result": "PASSED",
"odometer_value": "10088",
"odometer_unit": "MI",
"mot_test_number": "976134633448",
"odometer_result_type": "READ",
"defects": []
},
{
"test_date": "2025-04-08T08:11:26.000Z",
"expiry_date": "2026-04-22",
"result": "PASSED",
"odometer_value": "7909",
"odometer_unit": "MI",
"mot_test_number": "211085358720",
"odometer_result_type": "READ",
"defects": []
},
{
"test_date": "2025-04-08T08:11:25.000Z",
"result": "FAILED",
"odometer_value": "7909",
"odometer_unit": "MI",
"mot_test_number": "736735947582",
"odometer_result_type": "READ",
"defects": [
{
"type": "PRS",
"text": "Headlamp aim too high (4.1.2 (a))",
"dangerous": false
}
]
}
]
}
},
"meta": {
"request_id": "req_3f9a1c0d2b7e4a5f6c8d9e01",
"cost_pence": 5,
"balance_pence": 995,
"is_sandbox": false
}
}Illustrative values, shortened. Field names and types are exact.
Every field in data (47)
| Field | Type | Notes |
|---|---|---|
| vrm | string | |
| sources | object | What each government source answered: dvla is the vehicle register (tax status, MOT status, vehicle details), mot the MOT history service. |
| sources.dvla | "found" | "not_found" | "unavailable" | |
| sources.mot | "found" | "not_found" | "unavailable" | |
| tax | object | Omitted when the vehicle register holds no record. |
| tax.status | string | |
| tax.due_date | string (date) | |
| mot | object | Omitted when the vehicle register holds no record. |
| mot.status | string | |
| mot.expiry_date | string (date) | |
| vehicle | object | |
| vehicle.make | string | |
| vehicle.model | string | |
| vehicle.colour | string | |
| vehicle.year_of_manufacture | integer | |
| vehicle.first_registration_date | string | From the MOT history: the date of first registration, or when it does not hold one, the date of first use (these can differ for imports). See month_of_first_registration for the month on the vehicle register. |
| vehicle.engine_capacity | integer | cc. |
| vehicle.co2_emissions | number | g/km as on the vehicle register. 0 is sent for electric vehicles; for other fuels a 0 on the register means no figure is held, and the field is left out. |
| vehicle.fuel_type | string | |
| vehicle.marked_for_export | boolean | |
| vehicle.type_approval | string | |
| vehicle.wheelplan | string | |
| vehicle.euro_status | string | |
| vehicle.date_of_last_v5c_issued | string (date) | |
| vehicle.month_of_first_registration | string | YYYY-MM, from the vehicle register. |
| vehicle.month_of_first_uk_registration | string | YYYY-MM: when the vehicle was first registered in the UK, sent only when it differs from the first registration (usually an import). |
| vehicle.revenue_weight | integer | kg, from the vehicle register (mostly goods vehicles). |
| vehicle.real_driving_emissions | string | Real driving emissions standard, from the vehicle register (diesel company car tax). |
| vehicle.art_end_date | string (date) | When the additional rate of vehicle tax (expensive car supplement) ends. |
| vehicle.has_outstanding_recall | "Yes" | "No" | "Unknown" | Outstanding safety recall, exactly as the MOT history service says it. "Unknown" is common: it is not "No". Left out when the MOT history holds no record. |
| vehicle.manufacture_date | string (date) | From the MOT history. |
| vehicle.source | "dvla" | "dvsa" | Where the vehicle block came from: dvla (the vehicle register) or dvsa (the MOT history record, used when the register holds no record, common for classic vehicles and plates on retention). |
| mot_history | object | Omitted when the MOT history service holds no record. total_tests 0 can be normal (new or exempt vehicles). |
| mot_history.total_tests | integer | |
| mot_history.first_test_due_date | string (date) | When the first MOT is due, for a vehicle too new to have had one. |
| mot_history.tests | array of object | |
| mot_history.tests[].test_date | string | A full timestamp: when the test was completed. |
| mot_history.tests[].expiry_date | string | YYYY-MM-DD. Left out for a failed test. |
| mot_history.tests[].result | string | PASSED or FAILED. |
| mot_history.tests[].odometer_value | string or number | The reading as text (for example "55995"). Left out when the odometer could not be read: see odometer_result_type. |
| mot_history.tests[].odometer_unit | string | "MI" or "KM". |
| mot_history.tests[].mot_test_number | string | The MOT test certificate number: check a test on GOV.UK or remove duplicates with it. |
| mot_history.tests[].odometer_result_type | string | READ, UNREADABLE or NO_ODOMETER: why a reading may be missing. |
| mot_history.tests[].defects | array of object | |
| mot_history.tests[].defects[].type | string | |
| mot_history.tests[].defects[].text | string | |
| mot_history.tests[].defects[].dangerous | boolean |
Errors: 400, 401, 402, 403, 404, 429, 500, 502, 503. Success: 200.
Service history
/v1/vehicle/service-historyService history: start a request
Digital service records from the manufacturer network. Asynchronous: the answer is usually 202 with status pending; poll status_url (free) or pass an https callback_url. Every request runs a fresh search. A request for the same vehicle made in the last 10 minutes, or still in progress, is shared, and when that search has already finished the answer is 200 with the records. Posting the same vehicle again while your request is open returns it (202) without a new charge, but your balance must still cover the price. Up to 25 requests may be open per account (429 beyond that).
Charging: the price is taken when the request is accepted, refunded in full when the search finds no records (including manufacturers outside coverage) or fails because of a fault. Covered manufacturers: Audi, BMW, Cupra, Ford, Jaguar, Land Rover, Lexus, Mazda, Mercedes-Benz, MG, MINI, Nissan, Porsche, Renault, Rolls-Royce, SEAT, Skoda, Smart, Toyota and Volkswagen.
Sandbox: free; 202 pending first, then completed about 20 seconds later with stored records. SK22AOM answers with a generic sample report: made-up records marked sample: true with a sample_notice (one of them kept in kilometres), and a sample PDF. RE22MOA ends with no records (record_count: 0, outcome: no_records) and RO22AXH ends as failed (outcome: failed, error_code: SOURCE_UNAVAILABLE), so you can test both. Sandbox requests are free, so nothing is charged or refunded. Only registrations in the "Service history available" sandbox group answer, and sandbox answers carry meta.data_captured_at.
Records: mileage_unit says whether mileage is in miles or kilometres (or unknown). workshop is usually not supplied by the manufacturer network. With version 2 of the error codes (see DP-Error-Codes) the "OEM data" workshop placeholder is omitted and a 0 mileage on a record that is not the delivery record is omitted (not recorded); mileage_raw is always sent as the record shows it. For Mercedes-Benz the mileage of each visit comes from the manufacturer's report, in its own unit.
Price per successful call: Pay As You Go £2.00, Starter £1.70, Pro £1.40, Enterprise £1.00. From 1 December 2026: Pay As You Go £4.00, Starter £3.00, Pro £2.00, Enterprise £1.00. Sandbox keys are free.
Request body (JSON)
Send vrm, or vin (the full 17 characters, no I, O or Q), or both. With both, the registration is searched and the VIN is stored on the request and returned in every answer.
| Field | Type | Notes |
|---|---|---|
| vrm | string | UK registration. 2 to 8 letters and digits after spaces are removed. |
| vin | string | Full 17 character VIN, no I, O or Q. |
| callback_url | string (uri) | Optional. An https URL we POST the finished request to (same JSON as the status endpoint, wrapped in event, request_id, sent_at, data). Retried after 1, 10 and 60 minutes. Treat a callback as a notification and confirm with the status endpoint before acting. 500 characters maximum. |
Example request
curl -X POST https://www.dealerpricing.co.uk/api/v1/vehicle/service-history \
-H "X-API-Key: your_sandbox_key" \
-H "Content-Type: application/json" \
-d '{"vrm":"SK22AOM","callback_url":"https://example.com/hooks/dealerpricing"}'Example answer (202 Accepted)
{
"success": true,
"data": {
"request_id": "6c1f4e2a-9b0d-4c3e-8f21-0a7d5b9e3c11",
"vrm": "SK22AOM",
"status": "pending",
"message": "Request accepted. The manufacturer is being asked for this vehicle's digital service records - most complete within a few minutes, some take hours. Poll status_url or wait for your callback.",
"requested_at": "2026-11-05T09:00:00.000Z",
"check_after_seconds": 30,
"status_url": "https://www.dealerpricing.co.uk/api/v1/vehicle/service-history/6c1f4e2a-9b0d-4c3e-8f21-0a7d5b9e3c11",
"callback_url": "https://example.com/hooks/dealerpricing",
"cost_pence": 200,
"refunded_pence": 0
},
"meta": {
"request_id": "req_3f9a1c0d2b7e4a5f6c8d9e01",
"cost_pence": 200,
"balance_pence": 800,
"is_sandbox": false
}
}Illustrative values, shortened. Field names and types are exact.
Every field in data (33)
| Field | Type | Notes |
|---|---|---|
| request_id | string (uuid) | Id of this service history request (UUID). Use it with the status endpoint. |
| vrm | string | Omitted for a request made by VIN only. |
| vin | string | Sent only when the request carried a VIN. |
| status | "pending" | "processing" | "completed" | "failed" | |
| message | string | |
| requested_at | string (date-time) | |
| completed_at | string (date-time) | |
| check_after_seconds | integer | While open: wait at least this long before polling again. |
| status_url | string (uri) | Absolute URL of the status endpoint for this request. |
| callback_url | string (uri) | |
| cost_pence | integer | What was charged when the request was accepted. |
| refunded_pence | integer | What was given back (a search with no records, or a fault). |
| error | string | Failed requests: a fixed message for the error_code. Match on error_code, not on this text. |
| outcome | "records" | "no_records" | "failed" | Finished requests: records found, no records (refunded in full, not an error) or failed (refunded). refunded_pence shows the refund. |
| error_code | "SOURCE_UNAVAILABLE" | "INTERNAL_ERROR" | Failed requests: the version 2 error code for why. |
| service_history | object | Present once completed. |
| service_history.record_count | integer | |
| service_history.services | array of object | |
| service_history.services[].date | string (date) | Record date when it could be read. |
| service_history.services[].date_raw | string | The date as the manufacturer wrote it. |
| service_history.services[].type | string | Almost always "Service / Repair": the work done is described in notes. |
| service_history.services[].mileage | integer | Mileage as a number, in the unit given by mileage_unit (check it before comparing with MOT miles). Version 2: omitted when the record shows 0 and is not the delivery record (the mileage was not recorded). |
| service_history.services[].mileage_raw | string | The mileage as the record shows it, for example "7,265 mi", "18,424 km" or "0". |
| service_history.services[].mileage_unit | "mi" | "km" | "unknown" | The unit of mileage, read from mileage_raw: "mi", "km", or "unknown" when the record shows no unit. About a third of records show no unit. Mercedes-Benz: the mileage and unit of each visit are taken from the manufacturer report; a visit that cannot be matched to it has no mileage (unknown) rather than a figure that may be wrong. |
| service_history.services[].workshop | string | Where the work was done: for Mercedes-Benz (and smart) the servicing workshop name and address as printed in the manufacturer report, for example "Example Motors Limited, 1 Sample Street, Exampleton". For other makes usually not supplied: version 1 then sends the placeholder text "OEM data" (or an empty string); version 2 omits the field. |
| service_history.services[].notes | string | The work done, as the manufacturer recorded it. Sometimes in another language. |
| service_history.message | string | Why there are no records, when there are none. |
| service_history.retrieved_at | string (date-time) | |
| service_history.sample | boolean | Sandbox only: true when these are made-up sample records, not the records of a real vehicle. Never sent by live keys. |
| service_history.sample_notice | string | Sandbox only: sent with sample: true, says the records are sample data. |
| pdf_url | string (uri) | Completed requests with a stored manufacturer PDF: a signed link to it, valid for pdf_url_expires_in_seconds. Each status call returns a new link, free. |
| pdf_url_expires_in_seconds | integer | |
| readable_until | string (date-time) | Finished requests: after this time the status endpoint answers 410 REQUEST_EXPIRED. |
Errors: 400, 401, 402, 403, 404, 429, 500, 502, 503. Success: 200 or 202.
/v1/vehicle/service-history/{request_id}Service history: request status
The current state of a service history request made on your account, and once completed the records and a fresh pdf_url when a manufacturer PDF is stored. Free, never runs a new search. Wait at least check_after_seconds between polls. A finished request can be read for 7 days (readable_until); after that the answer is 410 REQUEST_EXPIRED. POST to the same URL works the same way.
Free.
Path
request_id: The request_id from the start request.
Example request
curl https://www.dealerpricing.co.uk/api/v1/vehicle/service-history/6c1f4e2a-9b0d-4c3e-8f21-0a7d5b9e3c11 \
-H "X-API-Key: your_sandbox_key"Example answer
{
"success": true,
"data": {
"request_id": "6c1f4e2a-9b0d-4c3e-8f21-0a7d5b9e3c11",
"vrm": "SK22AOM",
"status": "completed",
"message": "Service history retrieved. Sample data for testing. These are not the records of this or any real vehicle.",
"requested_at": "2026-11-05T09:00:00.000Z",
"completed_at": "2026-11-05T09:00:20.000Z",
"status_url": "https://www.dealerpricing.co.uk/api/v1/vehicle/service-history/6c1f4e2a-9b0d-4c3e-8f21-0a7d5b9e3c11",
"cost_pence": 0,
"refunded_pence": 0,
"outcome": "records",
"service_history": {
"record_count": 7,
"services": [
{
"date": "2022-04-21",
"date_raw": "21/04/2022",
"type": "Service / Repair",
"mileage": 12,
"mileage_raw": "12 mi",
"mileage_unit": "mi",
"workshop": "Sample Motors (Main Dealer), Exampleton",
"notes": "Pre-delivery inspection"
},
{
"date": "2023-03-14",
"date_raw": "14/03/2023",
"type": "Service / Repair",
"mileage": 9214,
"mileage_raw": "9,214 mi",
"mileage_unit": "mi",
"workshop": "Sample Motors (Main Dealer), Exampleton",
"notes": "Oil change service"
},
{
"date": "2024-02-02",
"date_raw": "02/02/2024",
"type": "Service / Repair",
"mileage": 18905,
"mileage_raw": "18,905 mi",
"mileage_unit": "mi",
"workshop": "Sample Motors (Main Dealer), Exampleton",
"notes": "Inspection with oil change"
},
{
"date": "2024-07-15",
"date_raw": "15/07/2024",
"type": "Service / Repair",
"mileage": 33600,
"mileage_raw": "33,600 km",
"mileage_unit": "km",
"workshop": "Example Garage Europe, Exampleville",
"notes": "Puncture repair"
},
{
"date": "2024-09-11",
"date_raw": "11/09/2024",
"type": "Service / Repair",
"mileage": 23410,
"mileage_raw": "23,410 mi",
"mileage_unit": "mi",
"workshop": "Example Autos Service Centre, Exampleton",
"notes": "Brake fluid change"
},
{
"date": "2025-03-28",
"date_raw": "28/03/2025",
"type": "Service / Repair",
"mileage": 29876,
"mileage_raw": "29,876 mi",
"mileage_unit": "mi",
"workshop": "Example Autos Service Centre, Exampleton",
"notes": "Oil change service"
},
{
"date": "2026-03-06",
"date_raw": "06/03/2026",
"type": "Service / Repair",
"mileage": 38120,
"mileage_raw": "38,120 mi",
"mileage_unit": "mi",
"workshop": "Sample Motors (Main Dealer), Exampleton",
"notes": "Inspection with oil change and pollen filter"
}
],
"retrieved_at": "2026-11-05T09:00:20.000Z",
"sample": true,
"sample_notice": "Sample data for testing. These are not the records of this or any real vehicle."
},
"pdf_url": "https://www.dealerpricing.co.uk/api/service-history/pdf/5a3b1e00-5a3b-4e00-8000-000000000001?t=1765000000.EXAMPLE",
"pdf_url_expires_in_seconds": 604800,
"readable_until": "2026-11-12T09:00:20.000Z"
},
"meta": {
"request_id": "req_3f9a1c0d2b7e4a5f6c8d9e01",
"cost_pence": 0,
"balance_pence": 1000,
"is_sandbox": false
}
}Illustrative values, shortened. Field names and types are exact.
Every field in data (33)
| Field | Type | Notes |
|---|---|---|
| request_id | string (uuid) | Id of this service history request (UUID). Use it with the status endpoint. |
| vrm | string | Omitted for a request made by VIN only. |
| vin | string | Sent only when the request carried a VIN. |
| status | "pending" | "processing" | "completed" | "failed" | |
| message | string | |
| requested_at | string (date-time) | |
| completed_at | string (date-time) | |
| check_after_seconds | integer | While open: wait at least this long before polling again. |
| status_url | string (uri) | Absolute URL of the status endpoint for this request. |
| callback_url | string (uri) | |
| cost_pence | integer | What was charged when the request was accepted. |
| refunded_pence | integer | What was given back (a search with no records, or a fault). |
| error | string | Failed requests: a fixed message for the error_code. Match on error_code, not on this text. |
| outcome | "records" | "no_records" | "failed" | Finished requests: records found, no records (refunded in full, not an error) or failed (refunded). refunded_pence shows the refund. |
| error_code | "SOURCE_UNAVAILABLE" | "INTERNAL_ERROR" | Failed requests: the version 2 error code for why. |
| service_history | object | Present once completed. |
| service_history.record_count | integer | |
| service_history.services | array of object | |
| service_history.services[].date | string (date) | Record date when it could be read. |
| service_history.services[].date_raw | string | The date as the manufacturer wrote it. |
| service_history.services[].type | string | Almost always "Service / Repair": the work done is described in notes. |
| service_history.services[].mileage | integer | Mileage as a number, in the unit given by mileage_unit (check it before comparing with MOT miles). Version 2: omitted when the record shows 0 and is not the delivery record (the mileage was not recorded). |
| service_history.services[].mileage_raw | string | The mileage as the record shows it, for example "7,265 mi", "18,424 km" or "0". |
| service_history.services[].mileage_unit | "mi" | "km" | "unknown" | The unit of mileage, read from mileage_raw: "mi", "km", or "unknown" when the record shows no unit. About a third of records show no unit. Mercedes-Benz: the mileage and unit of each visit are taken from the manufacturer report; a visit that cannot be matched to it has no mileage (unknown) rather than a figure that may be wrong. |
| service_history.services[].workshop | string | Where the work was done: for Mercedes-Benz (and smart) the servicing workshop name and address as printed in the manufacturer report, for example "Example Motors Limited, 1 Sample Street, Exampleton". For other makes usually not supplied: version 1 then sends the placeholder text "OEM data" (or an empty string); version 2 omits the field. |
| service_history.services[].notes | string | The work done, as the manufacturer recorded it. Sometimes in another language. |
| service_history.message | string | Why there are no records, when there are none. |
| service_history.retrieved_at | string (date-time) | |
| service_history.sample | boolean | Sandbox only: true when these are made-up sample records, not the records of a real vehicle. Never sent by live keys. |
| service_history.sample_notice | string | Sandbox only: sent with sample: true, says the records are sample data. |
| pdf_url | string (uri) | Completed requests with a stored manufacturer PDF: a signed link to it, valid for pdf_url_expires_in_seconds. Each status call returns a new link, free. |
| pdf_url_expires_in_seconds | integer | |
| readable_until | string (date-time) | Finished requests: after this time the status endpoint answers 410 REQUEST_EXPIRED. |
Market data
/v1/market/activeActive listings
Similar vehicles for sale now on UK dealer websites, cheapest first, adverts with a price only. Always matched: make, model, year (the registration year when the car was registered the year after it was built, else the build year), matching trims and doors. Gearbox (transmission), body type (body_type) and engine size within 0.2 litres (engine_size) are matched unless you send match without them. The search is never widened. A search that finds no adverts is a successful answer and is charged. Fields we do not hold for an advert or a car are left out (adverts differ in what they say, and figures need enough sold cars), never sent as null or a placeholder. The schema shows version 2 answers; fields that differ in version 1 say how. Sandbox answers carry meta.data_captured_at, when the stored test data was captured. Not charged when the answer is an error (404, 502 and so on): the price taken up front is refunded before the response is sent.
Price per successful call: Pay As You Go £0.15, Starter £0.12, Pro £0.10, Enterprise £0.05. Sandbox keys are free.
Request body (JSON)
Send vrm (UK registration, 2 to 8 letters and digits, spaces ignored). A vin is refused with 400 VIN_NOT_SUPPORTED (version 1: INVALID_REQUEST): use an endpoint that accepts VINs.
| Field | Type | Notes |
|---|---|---|
| vrm * | string | UK registration. 2 to 8 letters and digits after spaces are removed. |
| rows | integer | How many listings to return. Default 50, maximum 200. |
| match | string or array of string | Optional. Comma list (or array) of the optional filters to keep: transmission, body_type, engine_size. Without it all three apply. Other names are reported in match_not_applied. |
* required
Example request
curl -X POST https://www.dealerpricing.co.uk/api/v1/market/active \
-H "X-API-Key: your_sandbox_key" \
-H "Content-Type: application/json" \
-d '{"vrm":"SA22LZH","rows":1}'Example answer
{
"success": true,
"data": {
"vrm": "SA22LZH",
"total_found": 31,
"total_estimated": false,
"listings_returned": 1,
"match_criteria": "make,model,year,variant,transmission,body_type,doors,engine_size",
"listings": [
{
"id": "lst_049e1f30bcd3e3be940917dc0e5f244c",
"price": 10358,
"miles": 92014,
"year": 2022,
"make": "KIA",
"model": "ProCeed",
"variant": "Pro Ceed Gt-line Isg",
"fuel_type": "Petrol",
"transmission": "Manual",
"drivetrain": "4x2",
"body_type": "Estate",
"engine_size": 1.5,
"colour": "Grey",
"doors": 5,
"days_on_market": 116,
"dealer": {
"name": "CARSA LIMITED",
"location": "Southampton",
"postcode": "SO31 7FL"
},
"last_seen": "2026-10-17"
}
]
},
"meta": {
"request_id": "req_3f9a1c0d2b7e4a5f6c8d9e01",
"cost_pence": 15,
"balance_pence": 985,
"is_sandbox": false
}
}Illustrative values, shortened. Field names and types are exact.
Every field in data (44)
| Field | Type | Notes |
|---|---|---|
| vrm | string | |
| total_found | integer | How many matching adverts there are on the market (not how many were returned). |
| total_estimated | boolean | true when total_found was worked out from the share of fetched adverts that matched. |
| listings_returned | integer | |
| match_criteria | string | Comma list of what was actually matched, for example "make,model,year,variant,transmission,body_type,doors,engine_size". |
| match_not_applied | array of string | Fields you listed in match that are not matched in this version (for example fuel_type). |
| listings | array of object | Cheapest first. Adverts with a price only. |
| listings[].id | string | Our listing id: lst_ and 32 letters and digits. The same advert keeps the same id. Version 1: a different 64 character id; do not mix the two. |
| listings[].vrm | string | |
| listings[].price | integer | Asking price in pounds. |
| listings[].miles | integer | Advertised mileage in miles. |
| listings[].year | integer | Year of first registration, as the advert gives it. |
| listings[].make | string | |
| listings[].model | string | |
| listings[].variant | string | Trim as the advert names it. |
| listings[].fuel_type | string | |
| listings[].transmission | string | Manual, Automatic, Semi Automatic or CVT. |
| listings[].drivetrain | string | For example 4x2 or 4x4. |
| listings[].body_type | string | |
| listings[].engine_size | number | Litres, one decimal place (for example 1.5). |
| listings[].power_bhp | integer | Engine power in bhp, when the advert says. |
| listings[].euro_status | string | Emissions standard, when the advert says. |
| listings[].ulez_compliant | boolean | Sent only when the advert says. |
| listings[].colour | string | |
| listings[].doors | integer | |
| listings[].owners | integer | Previous keepers, when the advert says. |
| listings[].first_registered | string (date) | Date of first registration, when the advert says. Version 1: an ISO 8601 date-time in UTC. |
| listings[].write_off_category | string | Insurance write-off category shown on the advert (for example S or N). Left out when the advert shows none. |
| listings[].previous_price | integer | An earlier advertised price for this car, in pounds, when one was seen. |
| listings[].previous_price_date | string (date) | When previous_price was seen. Version 1: an ISO 8601 date-time in UTC. |
| listings[].price_change_pct | number | Change from previous_price to the current price, in per cent (negative = reduced). |
| listings[].days_at_dealer | integer | Days the car has been advertised by this dealer. |
| listings[].days_on_market | integer | Days in the current advertising stretch. |
| listings[].dealer | object | The seller. Contact details are never sent. |
| listings[].dealer.name | string | Business name. For a dealer group this is the group name. |
| listings[].dealer.type | string | franchise or independent, when the advert says. |
| listings[].dealer.location | string | Town or city where the car is: the branch for a dealer group, else the dealer's address. |
| listings[].dealer.postcode | string | Postcode where the car is (same rule as location). |
| listings[].dealer.latitude | number | Where the car is, when known. |
| listings[].dealer.longitude | number | Where the car is, when known. |
| listings[].listing_url | string (uri) | The advert on the dealer's website. |
| listings[].images | array of string (uri) | Up to 5 photo links (photo links only). |
| listings[].first_seen | string (date) | When the advert first appeared. Version 1: an ISO 8601 date-time in UTC. |
| listings[].last_seen | string (date) | When the advert was last seen. Version 1: an ISO 8601 date-time in UTC. |
Errors: 400, 401, 402, 403, 404, 429, 500, 502, 503. Success: 200.
/v1/market/soldSold listings
Similar vehicles recently sold, cheapest first, with a summary of the listings returned. Same matching rules as /v1/market/active, with the trims taken from recently sold adverts. "Sold" means the advert ended and did not reappear; the price is the last asking price. A search that finds no adverts is a successful answer and is charged. Fields we do not hold for an advert or a car are left out (adverts differ in what they say, and figures need enough sold cars), never sent as null or a placeholder. The schema shows version 2 answers; fields that differ in version 1 say how. Sandbox answers carry meta.data_captured_at, when the stored test data was captured. Not charged when the answer is an error (404, 502 and so on): the price taken up front is refunded before the response is sent.
Price per successful call: Pay As You Go £0.15, Starter £0.12, Pro £0.10, Enterprise £0.05. Sandbox keys are free.
Request body (JSON)
Send vrm (UK registration, 2 to 8 letters and digits, spaces ignored). A vin is refused with 400 VIN_NOT_SUPPORTED (version 1: INVALID_REQUEST): use an endpoint that accepts VINs.
| Field | Type | Notes |
|---|---|---|
| vrm * | string | UK registration. 2 to 8 letters and digits after spaces are removed. |
| rows | integer | How many listings to return. Default 50, maximum 200. |
| match | string or array of string | Optional. Comma list (or array) of the optional filters to keep: transmission, body_type, engine_size. Without it all three apply. |
* required
Example request
curl -X POST https://www.dealerpricing.co.uk/api/v1/market/sold \
-H "X-API-Key: your_sandbox_key" \
-H "Content-Type: application/json" \
-d '{"vrm":"SA22LZH","rows":1}'Example answer
{
"success": true,
"data": {
"vrm": "SA22LZH",
"total_found": 60,
"total_estimated": false,
"listings_returned": 1,
"match_criteria": "make,model,year,variant,transmission,body_type,doors,engine_size",
"returned_listings_summary": {
"count": 1,
"price_stats": {
"min": 9995,
"max": 9995,
"average": 9995,
"median": 9995
},
"days_to_sell_stats": {
"min": 79,
"max": 79,
"average": 79,
"median": 79
}
},
"listings": [
{
"id": "lst_824f293e01ae03fecd9128985ed5db22",
"sold_price": 9995,
"miles": 94980,
"year": 2022,
"make": "KIA",
"model": "Proceed",
"variant": "Pro Ceed Gt-line Isg",
"fuel_type": "Petrol",
"transmission": "Manual",
"drivetrain": "4x2",
"body_type": "Estate",
"engine_size": 1.5,
"colour": "Grey",
"doors": 5,
"days_to_sell": 79,
"dealer": {
"name": "Beehive Cars",
"location": "Bolton",
"postcode": "BL6 6DT"
},
"sold_date": "2026-07-26"
}
]
},
"meta": {
"request_id": "req_3f9a1c0d2b7e4a5f6c8d9e01",
"cost_pence": 15,
"balance_pence": 985,
"is_sandbox": false
}
}Illustrative values, shortened. Field names and types are exact.
Every field in data (54)
| Field | Type | Notes |
|---|---|---|
| vrm | string | |
| total_found | integer | How many matching adverts there are on the market (not how many were returned). |
| total_estimated | boolean | true when total_found was worked out from the share of fetched adverts that matched. |
| listings_returned | integer | |
| match_criteria | string | Comma list of what was actually matched, for example "make,model,year,variant,transmission,body_type,doors,engine_size". |
| match_not_applied | array of string | Fields you listed in match that are not matched in this version (for example fuel_type). |
| returned_listings_summary | object | Covers only the listings in this response (the cheapest), not the market. Use the insights endpoints for whole-market sold figures. Version 1: named summary, and its median is the upper of the two middle values when the count is even. |
| returned_listings_summary.count | integer | |
| returned_listings_summary.price_stats | object | |
| returned_listings_summary.price_stats.min | integer | |
| returned_listings_summary.price_stats.max | integer | |
| returned_listings_summary.price_stats.average | integer | |
| returned_listings_summary.price_stats.median | integer | |
| returned_listings_summary.days_to_sell_stats | object | |
| returned_listings_summary.days_to_sell_stats.min | integer | |
| returned_listings_summary.days_to_sell_stats.max | integer | |
| returned_listings_summary.days_to_sell_stats.average | integer | |
| returned_listings_summary.days_to_sell_stats.median | integer | |
| listings | array of object | Cheapest first. Adverts with a price only. |
| listings[].id | string | Our listing id: lst_ and 32 letters and digits. The same advert keeps the same id. Version 1: a different 64 character id; do not mix the two. |
| listings[].vrm | string | |
| listings[].sold_price | integer | The last asking price in pounds (not a confirmed sale price). |
| listings[].miles | integer | Advertised mileage in miles. |
| listings[].year | integer | Year of first registration, as the advert gives it. |
| listings[].make | string | |
| listings[].model | string | |
| listings[].variant | string | Trim as the advert names it. |
| listings[].fuel_type | string | |
| listings[].transmission | string | Manual, Automatic, Semi Automatic or CVT. |
| listings[].drivetrain | string | For example 4x2 or 4x4. |
| listings[].body_type | string | |
| listings[].engine_size | number | Litres, one decimal place (for example 1.5). |
| listings[].power_bhp | integer | Engine power in bhp, when the advert says. |
| listings[].euro_status | string | Emissions standard, when the advert says. |
| listings[].ulez_compliant | boolean | Sent only when the advert says. |
| listings[].colour | string | |
| listings[].doors | integer | |
| listings[].owners | integer | Previous keepers, when the advert says. |
| listings[].first_registered | string (date) | Date of first registration, when the advert says. Version 1: an ISO 8601 date-time in UTC. |
| listings[].write_off_category | string | Insurance write-off category shown on the advert (for example S or N). Left out when the advert shows none. |
| listings[].previous_price | integer | An earlier advertised price for this car, in pounds, when one was seen. |
| listings[].previous_price_date | string (date) | When previous_price was seen. Version 1: an ISO 8601 date-time in UTC. |
| listings[].price_change_pct | number | Change from previous_price to the current price, in per cent (negative = reduced). |
| listings[].days_at_dealer | integer | Days the car has been advertised by this dealer. |
| listings[].days_to_sell | integer | Days in the last advertising stretch before the advert ended. |
| listings[].dealer | object | The seller. Contact details are never sent. |
| listings[].dealer.name | string | Business name. For a dealer group this is the group name. |
| listings[].dealer.type | string | franchise or independent, when the advert says. |
| listings[].dealer.location | string | Town or city where the car is: the branch for a dealer group, else the dealer's address. |
| listings[].dealer.postcode | string | Postcode where the car is (same rule as location). |
| listings[].dealer.latitude | number | Where the car is, when known. |
| listings[].dealer.longitude | number | Where the car is, when known. |
| listings[].first_listed | string (date) | When the advert first appeared. Version 1: an ISO 8601 date-time in UTC. |
| listings[].sold_date | string (date) | The last day the advert was seen. A car counts as sold when its advert ended and did not reappear. Version 1: an ISO 8601 date-time in UTC. |
Errors: 400, 401, 402, 403, 404, 429, 500, 502, 503. Success: 200.
/v1/market/insightsMarket insights
Supply (live adverts, matched as for /v1/market/active), demand (sold in the last 90 days, days to sell), sold prices across the whole market and a market rating out of 10. Sold figures are matched like the live adverts (gearbox, body and engine included) and count adverts with a price only. With no sold data there is no rating. Fields we do not hold for an advert or a car are left out (adverts differ in what they say, and figures need enough sold cars), never sent as null or a placeholder. The schema shows version 2 answers; fields that differ in version 1 say how. Sandbox answers carry meta.data_captured_at, when the stored test data was captured. Not charged when the answer is an error (404, 502 and so on): the price taken up front is refunded before the response is sent.
Price per successful call: Pay As You Go £0.10, Starter £0.08, Pro £0.06, Enterprise £0.04. Sandbox keys are free.
Request body (JSON)
Send vrm (UK registration, 2 to 8 letters and digits, spaces ignored). A vin is refused with 400 VIN_NOT_SUPPORTED (version 1: INVALID_REQUEST): use an endpoint that accepts VINs.
| Field | Type | Notes |
|---|---|---|
| vrm * | string | UK registration. 2 to 8 letters and digits after spaces are removed. |
* required
Example request
curl -X POST https://www.dealerpricing.co.uk/api/v1/market/insights \
-H "X-API-Key: your_sandbox_key" \
-H "Content-Type: application/json" \
-d '{"vrm":"SA22LZH"}'Example answer
{
"success": true,
"data": {
"insights": {
"vrm": "SA22LZH",
"supply": {
"active_listings": 31,
"estimated": false,
"level": "LOW",
"description": "Limited availability"
},
"demand": {
"sold_last_90_days": 68,
"sold_with_price": 67,
"median_days_to_sell": 20,
"mean_days_to_sell": 30,
"fast_sale_days": 10,
"slow_sale_days": 43,
"level": "HIGH",
"description": "Vehicles sell quickly"
},
"pricing": {
"sold_price_min": 9995,
"sold_price_max": 17999,
"sold_price_mean": 14410,
"sold_price_median": 14395,
"percentile_25": 13898,
"percentile_75": 15224,
"percentile_90": 16537
},
"market_rating": {
"score": 8.3,
"max_score": 10,
"interpretation": "Strong market"
},
"recommendations": [
"High demand with limited supply - good time to sell"
]
}
},
"meta": {
"request_id": "req_3f9a1c0d2b7e4a5f6c8d9e01",
"cost_pence": 10,
"balance_pence": 990,
"is_sandbox": false
}
}Illustrative values, shortened. Field names and types are exact.
Every field in data (29)
| Field | Type | Notes |
|---|---|---|
| insights | object | Fields we do not hold for this car are left out. |
| insights.vrm | string | |
| insights.supply | object | |
| insights.supply.active_listings | integer | |
| insights.supply.estimated | boolean | |
| insights.supply.level | "VERY_LOW" | "LOW" | "MODERATE" | "HIGH" | "VERY_HIGH" | "UNKNOWN" | |
| insights.supply.description | string | |
| insights.demand | object | Sold figures cover the last 90 days, matched like the live adverts (make, model, trims, year, gearbox, body and engine; doors cannot be matched on sold adverts), adverts with a price only. Prices are last asking prices in whole pounds. |
| insights.demand.sold_last_90_days | integer | Sold adverts with a price that match the car. |
| insights.demand.sold_with_price | integer | How many sold prices the price figures are based on. |
| insights.demand.median_days_to_sell | integer | |
| insights.demand.mean_days_to_sell | integer | |
| insights.demand.fast_sale_days | integer | A quarter of the sold cars sold within this many days. |
| insights.demand.slow_sale_days | integer | Three quarters of the sold cars sold within this many days. |
| insights.demand.level | "VERY_LOW" | "LOW" | "MODERATE" | "HIGH" | "VERY_HIGH" | "UNKNOWN" | UNKNOWN when there is no sold data, or fewer than 5 sold cars. Version 1: the fewer-than-5 rule does not apply. |
| insights.demand.description | string | |
| insights.pricing | object | Sold prices across the whole market for the same car, in whole pounds. |
| insights.pricing.sold_price_min | integer | |
| insights.pricing.sold_price_max | integer | |
| insights.pricing.sold_price_mean | integer | |
| insights.pricing.sold_price_median | integer | |
| insights.pricing.percentile_25 | integer | |
| insights.pricing.percentile_75 | integer | |
| insights.pricing.percentile_90 | integer | |
| insights.market_rating | object | |
| insights.market_rating.score | number | 0 to 10. Left out when there is too little sold data to rate the market (no days to sell, or in version 2 fewer than 5 sold cars). |
| insights.market_rating.max_score | integer | |
| insights.market_rating.interpretation | string | Strong market, Average market, Challenging market, or Insufficient data when there is no score. |
| insights.recommendations | array of string |
Errors: 400, 401, 402, 403, 404, 429, 500, 502, 503. Success: 200.
Analysis
/v1/pricing/insightsPricing insights
The vehicle summary, market overview, sold prices, a recommended price range and, when you send price, where your asking price sits against every matching advert with a price. Matching and sold figures as for /v1/market/insights. Fields we do not hold for an advert or a car are left out (adverts differ in what they say, and figures need enough sold cars), never sent as null or a placeholder. The schema shows version 2 answers; fields that differ in version 1 say how. Sandbox answers carry meta.data_captured_at, when the stored test data was captured. Not charged when the answer is an error (404, 502 and so on): the price taken up front is refunded before the response is sent.
Price per successful call: Pay As You Go £0.10, Starter £0.08, Pro £0.06, Enterprise £0.04. Sandbox keys are free.
Request body (JSON)
Send vrm (UK registration, 2 to 8 letters and digits, spaces ignored). A vin is refused with 400 VIN_NOT_SUPPORTED (version 1: INVALID_REQUEST): use an endpoint that accepts VINs.
| Field | Type | Notes |
|---|---|---|
| vrm * | string | UK registration. 2 to 8 letters and digits after spaces are removed. |
| price | integer | Optional. Your asking price in pounds. |
* required
Example request
curl -X POST https://www.dealerpricing.co.uk/api/v1/pricing/insights \
-H "X-API-Key: your_sandbox_key" \
-H "Content-Type: application/json" \
-d '{"vrm":"SA22LZH","price":14500}'Example answer
{
"success": true,
"data": {
"insights": {
"vrm": "SA22LZH",
"vehicle": {
"make": "Kia",
"model": "Proceed",
"variant": "GT-Line ISG",
"year": 2022,
"fuel_type": "PETROL",
"transmission": "Manual"
},
"market_overview": {
"active_supply": 31,
"active_supply_estimated": false,
"sold_last_90_days": 68,
"median_days_to_sell": 20,
"supply_level": "LOW",
"demand_level": "HIGH"
},
"pricing_analysis": {
"asking_price": 14500,
"market_position": {
"position": "AVERAGE",
"percentile": 65,
"comparison": "Priced competitively with market",
"compared_with": 31,
"estimated": false
},
"sold_prices": {
"count": 67,
"min": 9995,
"max": 17999,
"average": 14410,
"median": 14395,
"percentile_25": 13898,
"percentile_75": 15224
},
"lowest_active_prices": [
{
"price": 10358,
"miles": 92014,
"year": 2022,
"days_on_market": 116,
"dealer": "CARSA LIMITED",
"location": "Southampton"
},
{
"price": 10590,
"miles": 92779,
"year": 2022,
"days_on_market": 42,
"dealer": "Dani Motors",
"location": "Luton"
},
{
"price": 11795,
"miles": 65000,
"year": 2022,
"days_on_market": 36,
"dealer": "Gcs Thrapston",
"location": "Thrapston"
},
{
"price": 12100,
"miles": 58085,
"year": 2022,
"days_on_market": 44,
"dealer": "Hilton Garage",
"location": "Derby"
},
{
"price": 12190,
"miles": 63509,
"year": 2022,
"days_on_market": 93,
"dealer": "Westbrook Car Sales",
"location": "Peterborough"
}
],
"recommended_price_range": {
"competitive": 13898,
"market": 14395,
"premium": 15224
}
},
"selling_estimate": {
"expected_days": 20,
"fast_sale_days": 10,
"slow_sale_days": 43,
"factors": [
"High demand market"
]
},
"recommendations": [
"Price is in line with market",
"Strong demand - vehicles selling quickly"
]
}
},
"meta": {
"request_id": "req_3f9a1c0d2b7e4a5f6c8d9e01",
"cost_pence": 10,
"balance_pence": 990,
"is_sandbox": false
}
}Illustrative values, shortened. Field names and types are exact.
Every field in data (49)
| Field | Type | Notes |
|---|---|---|
| insights | object | Fields we do not hold for this car are left out. |
| insights.vrm | string | |
| insights.vehicle | object | |
| insights.vehicle.make | string | |
| insights.vehicle.model | string | |
| insights.vehicle.variant | string | |
| insights.vehicle.year | integer | Year of manufacture. |
| insights.vehicle.fuel_type | string | |
| insights.vehicle.transmission | string | |
| insights.market_overview | object | Sold figures cover the last 90 days, matched like the live adverts (make, model, trims, year, gearbox, body and engine; doors cannot be matched on sold adverts), adverts with a price only. Prices are last asking prices in whole pounds. |
| insights.market_overview.active_supply | integer | |
| insights.market_overview.active_supply_estimated | boolean | |
| insights.market_overview.sold_last_90_days | integer | Sold adverts with a price that match the car. |
| insights.market_overview.median_days_to_sell | integer | |
| insights.market_overview.supply_level | "VERY_LOW" | "LOW" | "MODERATE" | "HIGH" | "VERY_HIGH" | "UNKNOWN" | The same bands as /v1/market/insights. Version 1: LOW, MODERATE or HIGH (under 50, under 150, 150 or more). |
| insights.market_overview.demand_level | "VERY_LOW" | "LOW" | "MODERATE" | "HIGH" | "VERY_HIGH" | "UNKNOWN" | The same bands as /v1/market/insights (UNKNOWN with fewer than 5 sold cars). Version 1: LOW, MODERATE, HIGH or UNKNOWN. |
| insights.pricing_analysis | object | |
| insights.pricing_analysis.asking_price | integer | Your price, when sent. |
| insights.pricing_analysis.market_position | object | Sent when price was sent. |
| insights.pricing_analysis.market_position.position | "VERY_LOW" | "LOW" | "AVERAGE" | "HIGH" | "VERY_HIGH" | "UNKNOWN" | |
| insights.pricing_analysis.market_position.percentile | integer | Share of matching adverts priced below yours. Left out when position is UNKNOWN (no adverts with a price to compare with). |
| insights.pricing_analysis.market_position.comparison | string | |
| insights.pricing_analysis.market_position.compared_with | integer | How many adverts with a price your price was ranked against. |
| insights.pricing_analysis.market_position.estimated | boolean | true when compared_with is an estimate. |
| insights.pricing_analysis.sold_prices | object | Sold prices in whole pounds. |
| insights.pricing_analysis.sold_prices.count | integer | How many sold prices these figures are based on. |
| insights.pricing_analysis.sold_prices.min | integer | |
| insights.pricing_analysis.sold_prices.max | integer | |
| insights.pricing_analysis.sold_prices.average | integer | |
| insights.pricing_analysis.sold_prices.median | integer | |
| insights.pricing_analysis.sold_prices.percentile_25 | integer | |
| insights.pricing_analysis.sold_prices.percentile_75 | integer | |
| insights.pricing_analysis.lowest_active_prices | array of object | The five cheapest matching adverts. |
| insights.pricing_analysis.lowest_active_prices[].price | integer | |
| insights.pricing_analysis.lowest_active_prices[].miles | integer | |
| insights.pricing_analysis.lowest_active_prices[].year | integer | |
| insights.pricing_analysis.lowest_active_prices[].days_on_market | integer | |
| insights.pricing_analysis.lowest_active_prices[].dealer | string | Left out when the advert gives no name. |
| insights.pricing_analysis.lowest_active_prices[].location | string | Where the car is (the branch for a dealer group). |
| insights.pricing_analysis.recommended_price_range | object | From sold prices: competitive = lower quartile, market = median, premium = upper quartile. Left out when we hold too few sold prices. Version 1: the median less 5%, the median, the median plus 5%. |
| insights.pricing_analysis.recommended_price_range.competitive | integer | |
| insights.pricing_analysis.recommended_price_range.market | integer | |
| insights.pricing_analysis.recommended_price_range.premium | integer | |
| insights.selling_estimate | object | |
| insights.selling_estimate.expected_days | integer | |
| insights.selling_estimate.fast_sale_days | integer | |
| insights.selling_estimate.slow_sale_days | integer | |
| insights.selling_estimate.factors | array of string | |
| insights.recommendations | array of string |
Errors: 400, 401, 402, 403, 404, 429, 500, 502, 503. Success: 200.
Sandbox
/v1/sandbox/vrmsNo key neededSandbox registrations
Every registration a sandbox key can query, with the scenario it exercises and the markers its record carries. Public: no API key needed. Sandbox data may not be accurate and is for testing only.
Example request
curl https://www.dealerpricing.co.uk/api/v1/sandbox/vrmsExample answer
{
"success": true,
"data": {
"count": 177,
"notice": "Sandbox data may not be accurate and is for testing purposes only. It must not be shown to end users or used in production.",
"vrms": [
{
"vrm": "RO21PAD",
"vin": "LSJWP4396SZ244868",
"scenario": "write-off",
"categories": [
"General"
],
"markers": [
"write-off (Cat B)",
"finance",
"scrapped",
"plate change",
"hybrid",
"valuation",
"spec"
],
"make": "MG",
"model": "3 TROPHY HEV AUTO",
"year": 2025,
"fuel_type": "HYBRID ELECTRIC"
}
]
}
}Illustrative values, shortened. Field names and types are exact.
Every field in the answer (14)
| Field | Type | Notes |
|---|---|---|
| success | true | |
| data | object | |
| data.count | integer | |
| data.notice | string | |
| data.vrms | array of object | |
| data.vrms[].vrm | string | |
| data.vrms[].vin | string or null | |
| data.vrms[].scenario | "stolen-police" | "stolen-insurer" | "write-off" | "scrapped" | "finance" | "plate-change" | "electric" | "hybrid" | "general" | The main case this registration exercises. |
| data.vrms[].categories | array of string | |
| data.vrms[].markers | array of string | Everything this record carries, for example "write-off (Cat B)", "finance", "service history". |
| data.vrms[].make | string or null | |
| data.vrms[].model | string or null | |
| data.vrms[].year | integer or null | |
| data.vrms[].fuel_type | string or null |
Reference
/v1/openapi.jsonNo key neededThis OpenAPI description
This document, as JSON. Public: no API key needed. Also at /developer/openapi.json.
Example request
curl https://www.dealerpricing.co.uk/api/v1/openapi.jsonExample answer
{
"openapi": "3.1.0"
}Illustrative values, shortened. Field names and types are exact.
Questions: support@dealerpricing.co.uk. Terms: API Terms of Use.