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 null instead 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 null means "not held for this check", never "no".
  • raw blocks 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).

PackagePer secondPer minutePer day
Pay As You Go260500
Starter51002,000
Pro103005,000
Enterprise2060015,000
Sandbox (per key)230300

Request headers

HeaderMeaningWhere
DP-Error-CodesOptional. 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

HeaderMeaningSent
X-Request-IdId of this request (also in the body).Every response
X-RateLimit-Limit-SecondThis key's per-second limit.After the key is accepted
X-RateLimit-Limit-MinuteThis key's per-minute limit.After the key is accepted
X-RateLimit-Limit-DayThis key's daily limit (UK calendar day).After the key is accepted
X-RateLimit-Remaining-SecondRequests left this second on this key.After the key is accepted
X-RateLimit-Remaining-MinuteRequests left this minute on this key.After the key is accepted
X-RateLimit-Remaining-DayRequests left today on this key.After the key is accepted
X-RateLimit-Reset-SecondSeconds until the per-second window resets.After the key is accepted
X-RateLimit-Reset-MinuteSeconds until the per-minute window resets.After the key is accepted
X-RateLimit-Reset-DaySeconds until midnight UK time.After the key is accepted
X-Package-Limit-SecondYour package's per-second limit across all live keys.Live keys
X-Package-Limit-MinuteYour package's per-minute limit across all live keys.Live keys
X-Package-Limit-DayYour package's daily limit across all live keys.Live keys
X-Package-Remaining-DayRequests left today on your package.Live keys
X-Endpoint-Limit-DayYour daily cap for this endpoint.Live keys, capped endpoints
X-Endpoint-Remaining-DayRequests left today on this endpoint.Live keys, capped endpoints
Retry-AfterSeconds to wait before retrying.429 responses
DP-Error-CodesThe 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.

StatusCodesMeaning
400MISSING_PARAMETERINVALID_PARAMETERINVALID_VRMINVALID_VINVIN_NOT_SUPPORTEDINVALID_REQUEST_IDThe request needs changing before it can succeed. Not charged.
401MISSING_API_KEYINVALID_API_KEYAPI_KEY_REVOKEDThe API key is missing, not recognised or revoked.
402INSUFFICIENT_BALANCEThe balance (plus any plan credit) does not cover the price. Top up and retry.
403ACCOUNT_DISABLEDEMAIL_NOT_VERIFIEDSANDBOX_NOT_SUPPORTEDThe key is recognised but this call is not allowed for the account or key.
404VEHICLE_NOT_FOUNDSANDBOX_VEHICLE_NOT_AVAILABLEREQUEST_NOT_FOUNDVALUATION_NOT_AVAILABLESPEC_NOT_AVAILABLEHISTORY_NOT_AVAILABLEMARKET_DATA_NOT_AVAILABLESERVICE_HISTORY_NOT_AVAILABLENo such vehicle or request, or a known vehicle with no data of this kind. Not charged.
410REQUEST_EXPIREDA service history request finished more than 7 days ago and can no longer be read.
429RATE_LIMIT_EXCEEDEDSANDBOX_DAILY_LIMIT_REACHEDPACKAGE_LIMIT_EXCEEDEDENDPOINT_DAILY_CAP_REACHEDTOO_MANY_OPEN_REQUESTSA limit was reached. Wait for Retry-After seconds. Never charged.
500INTERNAL_ERRORSomething failed on our side. Not charged. Safe to retry; quote the request id if it persists.
502SOURCE_UNAVAILABLEA data source failed or did not answer in time, after our automatic retries. Not charged. Safe to retry.
503SOURCE_LIMIT_REACHEDA 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.

EventMeaning
balance.lowYour prepaid balance has dropped below your low balance level.
balance.topped_upA top-up has been paid and added to your balance.
key.createdAn API key was created on your account.
key.revokedAn API key on your account was revoked.
plan.changedYour API plan changed and the new per-call prices apply now.
plan.change_scheduledA plan change has been booked for the end of the current period.
subscription.payment_failedA subscription renewal payment did not go through.
service_history.completedA service history request finished. Same body as the request callback.
service_history.failedA service history request failed and was refunded. Same body as the request callback.
limit.daily_cap_reachedA daily limit on your plan was reached; further live requests are refused until midnight UK time.
account.email_changedThe sign-in email address of your developer account changed.
api.noticeAn announcement about the API, for example a change to error codes.

Vehicle data

POST/v1/vehicle/decode

Vehicle 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.

FieldTypeNotes
vrmstringUK registration. 2 to 8 letters and digits after spaces are removed.
vinstringVehicle 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)
FieldTypeNotes
vehicleobjectThe vehicle. Fields and groups not held for this vehicle are omitted.
vehicle.vrmstringRegistration.
vehicle.makestring
vehicle.modelstring
vehicle.model_descriptionstring
vehicle.variantstring
vehicle.seriesstring
vehicle.yearintegerYear of manufacture.
vehicle.registration_datestring (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_manufacturestring (date)
vehicle.colourstring
vehicle.fuel_typestring
vehicle.powertrain_typestring
vehicle.transmissionstring
vehicle.gearsinteger
vehicle.drive_typestring
vehicle.body_typestring
vehicle.wheelplanstring
vehicle.engine_capacity_ccinteger
vehicle.engine_capacity_litresnumber
vehicle.engine_numberstring
vehicle.vinstring
vehicle.doorsinteger
vehicle.seatsinteger
vehicle.co2_emissionsnumberg/km: the manufacturer figure. The figure on the UK vehicle register is tax.co2_g_per_km and can differ.
vehicle.euro_statusstring
vehicle.power_bhpnumber
vehicle.power_kwnumber
vehicle.torque_nmnumber
vehicle.top_speed_mphnumber
vehicle.acceleration_0_60_mphnumberSeconds.
vehicle.fuel_consumption_combinednumbermpg.
vehicle.fuel_consumption_urbannumbermpg.
vehicle.fuel_consumption_extra_urbannumbermpg.
vehicle.gross_weight_kgnumber
vehicle.kerb_weight_kgnumber
vehicle.mass_in_service_kgnumber
vehicle.previous_keepersintegerPrevious 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_changesinteger
vehicle.registrationobject
vehicle.registration.first_registeredstring (date)First registered anywhere (for an import, the first date abroad).
vehicle.registration.first_registered_ukstring (date)
vehicle.registration.used_before_first_registrationboolean
vehicle.registration.cherished_transfer_markerboolean
vehicle.keeper_historyarray of objectOne row per keeper change, latest first. An empty list is a real answer.
vehicle.keeper_history[].previous_keepersintegerThe number of previous keepers at this change.
vehicle.keeper_history[].keeper_start_datestring (date)
vehicle.keeper_history[].previous_keeper_disposal_datestring (date)
vehicle.v5c_issue_datesarray of string (date)The date each registration document (V5C) was issued.
vehicle.plate_changesarray of objectAn empty list is a real answer: the record holds no plate changes.
vehicle.plate_changes[].previous_vrmstring
vehicle.plate_changes[].current_vrmstring
vehicle.plate_changes[].date_of_transactionstring (date)
vehicle.plate_changes[].date_of_receiptstring (date)
vehicle.plate_changes[].transfer_typestring
vehicle.colour_historyobject
vehicle.colour_history.currentstring
vehicle.colour_history.originalstring
vehicle.colour_history.previousstring
vehicle.colour_history.changesinteger
vehicle.colour_history.last_change_datestring (date)
vehicle.import_exportobject
vehicle.import_export.is_importedboolean
vehicle.import_export.imported_from_niboolean
vehicle.import_export.imported_from_outside_euboolean
vehicle.import_export.is_exportedboolean
vehicle.import_export.export_datestring (date)
vehicle.scrappedobject
vehicle.scrapped.is_scrappedboolean
vehicle.scrapped.is_unscrappedboolean
vehicle.scrapped.scrap_datestring (date)
vehicle.scrapped.certificate_of_destructionboolean
vehicle.taxobjectVehicle excise duty as registered. Rates are in pounds.
vehicle.tax.co2_g_per_kmnumberThe figure on the UK vehicle register.
vehicle.tax.co2_bandstring
vehicle.tax.tax_bandstring
vehicle.tax.first_year_twelve_months_gbpnumberThe 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_gbpnumber
vehicle.tax.standard_twelve_months_gbpnumber
vehicle.tax.premium_vehicle_six_months_gbpnumberThe 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_gbpnumberAs premium_vehicle_six_months_gbp.
vehicle.dvlaobjectThe 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.makestring
vehicle.dvla.modelstring
vehicle.dvla.body_typestring
vehicle.dvla.fuel_typestring
vehicle.dvla.wheelplanstring
vehicle.dvla.engine_capacity_ccinteger
vehicle.dvla.power_kwnumber
vehicle.dvla.seatsinteger
vehicle.dvla.gross_weight_kgnumber
vehicle.dvla.mass_in_service_kgnumber
vehicle.dvla.power_to_weight_rationumber
vehicle.dvla.max_braked_trailer_mass_kgnumber
vehicle.dvla.max_unbraked_trailer_mass_kgnumber
vehicle.power_psnumberFrom the manufacturer model data: left out when it is not held for this vehicle (meta.model_data_held is then false).
vehicle.torque_lb_ftnumber
vehicle.top_speed_kphnumber
vehicle.acceleration_0_100_kphnumberSeconds (0 to 100 km/h, the same test as 0 to 62 mph).
vehicle.fuel_consumption_combined_l_100kmnumber
vehicle.fuel_consumption_urban_l_100kmnumber
vehicle.fuel_consumption_extra_urban_l_100kmnumber
vehicle.country_of_originstring
vehicle.model_introduction_datestring (date)When this model was introduced.
vehicle.model_start_datestring (date)First build date of this model version.
vehicle.model_end_datestring (date)Last build date of this model version (left out while it is still built).
vehicle.classificationobjectFrom 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_categorystringFor example M1 (car) or N1 (light goods vehicle).
vehicle.classification.vehicle_classstring
vehicle.classification.taxation_classstring
vehicle.classification.market_sector_codestring
vehicle.engineobjectCombustion 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.codestringThe manufacturer engine code.
vehicle.engine.manufacturerstring
vehicle.engine.cylindersinteger
vehicle.engine.cylinder_arrangementstring
vehicle.engine.aspirationstring
vehicle.engine.valves_per_cylinderinteger
vehicle.engine.valve_gearstring
vehicle.engine.bore_mmnumber
vehicle.engine.stroke_mmnumber
vehicle.engine.fuel_deliverystring
vehicle.engine.locationstring
vehicle.dimensionsobjectFrom the manufacturer model data: left out when it is not held for this vehicle (meta.model_data_held is then false).
vehicle.dimensions.length_mmnumber
vehicle.dimensions.width_mmnumber
vehicle.dimensions.height_mmnumber
vehicle.dimensions.wheelbase_mmnumber
vehicle.dimensions.load_length_mmnumberVans.
vehicle.weightsobjectFrom 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_kgnumber
vehicle.weights.gross_train_weight_kgnumber
vehicle.weights.payload_kgnumber
vehicle.weights.unladen_weight_kgnumber
vehicle.bodyobjectFrom the manufacturer model data: left out when it is not held for this vehicle (meta.model_data_held is then false).
vehicle.body.body_shapestring
vehicle.body.cab_typestringVans and pick-ups.
vehicle.body.wheelbase_typestring
vehicle.body.axlesinteger
vehicle.body.fuel_tank_capacity_litresnumber
vehicle.body.load_volume_litresnumberLoad space of a van.
vehicle.body.platform_namestring
vehicle.body.platform_shared_across_modelsboolean
vehicle.safetyobjectEuro 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_ratingnumber
vehicle.safety.ncap_adult_percentnumber
vehicle.safety.ncap_child_percentnumber
vehicle.safety.ncap_pedestrian_percentnumber
vehicle.safety.ncap_safety_assist_percentnumber
vehicle.warrantyobjectThe 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_monthsinteger
vehicle.warranty.manufacturer_warranty_milesinteger
vehicle.supports_over_the_air_updatesboolean
vehicle.drag_coefficientnumber
vehicle.evobjectElectric 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_typestringFor example BEV or PHEV.
vehicle.ev.batteriesarray of object
vehicle.ev.batteries[].descriptionstring
vehicle.ev.batteries[].total_capacity_kwhnumber
vehicle.ev.batteries[].usable_capacity_kwhnumber
vehicle.ev.batteries[].chemistrystring
vehicle.ev.batteries[].voltagenumber
vehicle.ev.batteries[].locationstring
vehicle.ev.batteries[].warranty_monthsinteger
vehicle.ev.batteries[].warranty_milesinteger
vehicle.ev.range_testsarray of object
vehicle.ev.range_tests[].test_typestringFor example WLTP.
vehicle.ev.range_tests[].combined_range_milesnumber
vehicle.ev.range_tests[].combined_range_kmnumber
vehicle.ev.range_tests[].city_range_milesnumber
vehicle.ev.range_tests[].city_range_kmnumber
vehicle.ev.real_range_milesnumberExpected real-world range.
vehicle.ev.real_range_kmnumber
vehicle.ev.zero_emission_milesnumberElectric-only range (plug-in hybrids).
vehicle.ev.miles_per_charge_hournumber
vehicle.ev.wh_per_milenumber
vehicle.ev.max_charge_input_kwnumber
vehicle.ev.number_of_charge_portsinteger
vehicle.ev.charge_portsarray of object
vehicle.ev.charge_ports[].port_typestring
vehicle.ev.charge_ports[].locationstring
vehicle.ev.charge_ports[].max_charge_kwnumber
vehicle.ev.charge_ports[].is_standardboolean
vehicle.ev.charge_ports[].charge_times_10_to_80_percentarray of objectAverage time to charge from 10% to 80% on a charger of this power.
vehicle.ev.charge_ports[].charge_times_10_to_80_percent[].charger_kwnumber
vehicle.ev.charge_ports[].charge_times_10_to_80_percent[].minutesnumber
vehicle.ev.motorsarray of object
vehicle.ev.motors[].typestring
vehicle.ev.motors[].locationstring
vehicle.ev.motors[].axle_drivenstring
vehicle.ev.motors[].power_kwnumber
vehicle.ev.motors[].max_torque_nmnumber
vehicle.ev.motors[].max_power_rpmnumber
vehicle.ev.motors[].manufacturerstring
vehicle.ev.motors[].modelstring
vehicle.ev.motors[].regenerative_brakingboolean
vehicle.ev.tesla_supercharger_compatibleboolean
vehicle.rawobjectThe 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.

POST/v1/vehicle/spec

Specification

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.

FieldTypeNotes
vrmstringUK registration. 2 to 8 letters and digits after spaces are removed.
vinstringVehicle 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)
FieldTypeNotes
specificationobjectThe 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.vrmstringRegistration (or the VIN when the request was made by VIN).
specification.vehicleobject
specification.vehicle.makestring
specification.vehicle.modelstringThe full model description, the same as model_description.
specification.vehicle.model_descriptionstring
specification.vehicle.yearintegerYear of manufacture.
specification.factory_equipmentobjectEquipment fitted at the factory for this model.
specification.factory_equipment.countintegerNumber of items.
specification.factory_equipment.itemsarray of object
specification.factory_equipment.items[].categorystring
specification.factory_equipment.items[].namestring
specification.factory_equipment.items[].descriptionstring
specification.factory_equipment.items[].fitmentstringStandard, Option or Free.
specification.factory_equipment.items[].typestring
specification.factory_equipment.items[].pricestring or numberPounds. On options only; can be negative (a credit option).
specification.factory_equipment.items[].contentsarray of stringWhat an option pack contains, one line per item.
specification.rawobjectThe 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.

POST/v1/vehicle/valuation

Valuation

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.

FieldTypeNotes
vrmstringUK registration. 2 to 8 letters and digits after spaces are removed.
vinstringVehicle identification number. 5 to 17 letters and digits, no I, O or Q.
mileageintegerCurrent 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)
FieldTypeNotes
valuationobjectThe valuation. Fields not held are omitted.
valuation.vrmstringRegistration (or the VIN when the request was made by VIN).
valuation.mileage_usedintegerThe mileage the figures were produced at, as the source reports it (your mileage only when it reports none).
valuation.currency"GBP"
valuation.valuationsobjectHeadline figures in pounds.
valuation.valuations.retailnumber
valuation.valuations.tradenumber
valuation.valuations.privatenumber
valuation.valuations.part_exchangenumber
valuation.valuations.auctionnumber
valuation.figuresobjectEvery figure held, in pounds.
valuation.figures.on_the_roadnumberThe price when new, not today's value.
valuation.figures.dealer_forecourtnumber
valuation.figures.trade_retailnumber
valuation.figures.trade_averagenumber
valuation.figures.trade_poornumber
valuation.figures.private_cleannumber
valuation.figures.private_averagenumber
valuation.figures.part_exchangenumber
valuation.figures.auctionnumber
valuation.valuation_datestring (date)The UK day the valuation was produced.
valuation.valued_atstring (date-time)The exact time the valuation was produced.
valuation.valuation_bookstringThe source's own label for the valuation set it used (for example "Direct"). For reference only.
valuation.vehicle_summaryobject
valuation.vehicle_summary.descriptionstring
valuation.vehicle_summary.first_registeredstring (date)
valuation.rawobjectThe 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.

POST/v1/vehicle/provenanceKeeps nulls

Provenance

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.

FieldTypeNotes
vrmstringUK registration. 2 to 8 letters and digits after spaces are removed.
vinstringVehicle 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)
FieldTypeNotes
provenanceobjectProvenance 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.vrmstring or null
provenance.check_datestring (date-time) or nullWhen this check ran.
provenance.vehicleobject or null
provenance.vehicle.vinstring or null
provenance.financeobject or null
provenance.finance.has_outstanding_financeboolean or nulltrue: finance recorded. false: the finance register was checked and holds none. null: not held for this check.
provenance.finance.finance_recordsinteger or null
provenance.finance.finance_detailsarray of object or null
provenance.finance.finance_details[].agreement_datestring (date)
provenance.finance.finance_details[].agreement_typestring
provenance.finance.finance_details[].agreement_numberstring
provenance.finance.finance_details[].agreement_term_monthsstring or number
provenance.finance.finance_details[].finance_companystring
provenance.finance.finance_details[].contact_numberstringThe finance company's phone number.
provenance.finance.finance_details[].vehicle_descriptionstring
provenance.finance.finance_details[].is_stock_fundingbooleantrue for a UNIT STOCKING agreement: a dealer's stock funding, not a keeper's finance agreement.
provenance.stolenobject or null
provenance.stolen.is_stolenboolean or nulltrue 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_stolenboolean or nullThe police record on its own: true when it marks the vehicle as stolen. null when it could not be checked.
provenance.stolen.insurer_theft_recordedboolean or nullThe insurer register on its own: true when it holds a theft record. null when it could not be checked.
provenance.stolen.policeobject or nullThe police record in full. null when the police record could not be checked.
provenance.stolen.police.marked_stolenboolean or nulltrue: the police record marks the vehicle as stolen.
provenance.stolen.police.date_reported_stolenstring (date) or null
provenance.stolen.police.police_forcestring or null
provenance.stolen.police.record_statusstring or nullThe current status on the police record.
provenance.stolen.police.record_added_atstring (date-time) or nullWhen the police record was created.
provenance.stolen.police.vrmstring or nullThe registration on the police record.
provenance.stolen.police.vinstring or nullThe VIN on the police record.
provenance.stolen.insurer_theft_recordsarray of object or nullEvery insurer register theft record, with all its fields.
provenance.stolen.insurer_theft_records[].categorystringWrite-off category (A, B, S, N, or the older C and D) or the theft category.
provenance.stolen.insurer_theft_records[].statusstringThe register's description, for example "CAT S STRUCTURAL DAMAGE".
provenance.stolen.insurer_theft_records[].loss_datestring (date)Date of the loss.
provenance.stolen.insurer_theft_records[].entry_datestring (date)When the record was added to the insurers' register.
provenance.stolen.insurer_theft_records[].insurerstring
provenance.stolen.insurer_theft_records[].insurer_branchstring
provenance.stolen.insurer_theft_records[].insurer_codestring
provenance.stolen.insurer_theft_records[].claim_numberstring
provenance.stolen.insurer_theft_records[].contact_numberstringThe insurer's phone number.
provenance.stolen.insurer_theft_records[].theft_indicatorstring or numberThe register's own theft marker code, as received. Use theft_indicator_literal to read it.
provenance.stolen.insurer_theft_records[].theft_indicator_literalstring"STOLEN" or "NOT STOLEN".
provenance.stolen.insurer_theft_records[].loss_typestringThe register's loss type code, as received ("0" on every record seen so far).
provenance.stolen.insurer_theft_records[].removed_datestring (date)When the record was taken off the register, if it was.
provenance.write_offobject or null
provenance.write_off.is_write_offboolean or null
provenance.write_off.categorystring or nullThe 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.datestring (date) or nullLoss date of the most recent record (its entry date when it has no loss date).
provenance.write_off.categoriesarray of string or nullEvery write-off category held, each once, from the newest record.
provenance.write_off.write_off_recordsarray of object or nullEvery write-off record, newest first, with all its fields.
provenance.write_off.write_off_records[].categorystringWrite-off category (A, B, S, N, or the older C and D) or the theft category.
provenance.write_off.write_off_records[].statusstringThe register's description, for example "CAT S STRUCTURAL DAMAGE".
provenance.write_off.write_off_records[].loss_datestring (date)Date of the loss.
provenance.write_off.write_off_records[].entry_datestring (date)When the record was added to the insurers' register.
provenance.write_off.write_off_records[].insurerstring
provenance.write_off.write_off_records[].insurer_branchstring
provenance.write_off.write_off_records[].insurer_codestring
provenance.write_off.write_off_records[].claim_numberstring
provenance.write_off.write_off_records[].contact_numberstringThe insurer's phone number.
provenance.write_off.write_off_records[].theft_indicatorstring or numberThe register's own theft marker code, as received. Use theft_indicator_literal to read it.
provenance.write_off.write_off_records[].theft_indicator_literalstring"STOLEN" or "NOT STOLEN".
provenance.write_off.write_off_records[].loss_typestringThe register's loss type code, as received ("0" on every record seen so far).
provenance.write_off.write_off_records[].removed_datestring (date)When the record was taken off the register, if it was.
provenance.alertsobject or null
provenance.alerts.has_alertsboolean or nulltrue 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_countinteger or null
provenance.alerts.alert_listarray of string or nullPlain-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.rawobject or nullThe 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.

POST/v1/vehicle/tax-mot

Tax 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.

FieldTypeNotes
vrm *stringUK 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)
FieldTypeNotes
vrmstring
sourcesobjectWhat 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"
taxobjectOmitted when the vehicle register holds no record.
tax.statusstring
tax.due_datestring (date)
motobjectOmitted when the vehicle register holds no record.
mot.statusstring
mot.expiry_datestring (date)
vehicleobject
vehicle.makestring
vehicle.modelstring
vehicle.colourstring
vehicle.year_of_manufactureinteger
vehicle.first_registration_datestringFrom 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_capacityintegercc.
vehicle.co2_emissionsnumberg/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_typestring
vehicle.marked_for_exportboolean
vehicle.type_approvalstring
vehicle.wheelplanstring
vehicle.euro_statusstring
vehicle.date_of_last_v5c_issuedstring (date)
vehicle.month_of_first_registrationstringYYYY-MM, from the vehicle register.
vehicle.month_of_first_uk_registrationstringYYYY-MM: when the vehicle was first registered in the UK, sent only when it differs from the first registration (usually an import).
vehicle.revenue_weightintegerkg, from the vehicle register (mostly goods vehicles).
vehicle.real_driving_emissionsstringReal driving emissions standard, from the vehicle register (diesel company car tax).
vehicle.art_end_datestring (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_datestring (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_historyobjectOmitted when the MOT history service holds no record. total_tests 0 can be normal (new or exempt vehicles).
mot_history.total_testsinteger
mot_history.first_test_due_datestring (date)When the first MOT is due, for a vehicle too new to have had one.
mot_history.testsarray of object
mot_history.tests[].test_datestringA full timestamp: when the test was completed.
mot_history.tests[].expiry_datestringYYYY-MM-DD. Left out for a failed test.
mot_history.tests[].resultstringPASSED or FAILED.
mot_history.tests[].odometer_valuestring or numberThe reading as text (for example "55995"). Left out when the odometer could not be read: see odometer_result_type.
mot_history.tests[].odometer_unitstring"MI" or "KM".
mot_history.tests[].mot_test_numberstringThe MOT test certificate number: check a test on GOV.UK or remove duplicates with it.
mot_history.tests[].odometer_result_typestringREAD, UNREADABLE or NO_ODOMETER: why a reading may be missing.
mot_history.tests[].defectsarray of object
mot_history.tests[].defects[].typestring
mot_history.tests[].defects[].textstring
mot_history.tests[].defects[].dangerousboolean

Errors: 400, 401, 402, 403, 404, 429, 500, 502, 503. Success: 200.

Service history

POST/v1/vehicle/service-history

Service 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.

FieldTypeNotes
vrmstringUK registration. 2 to 8 letters and digits after spaces are removed.
vinstringFull 17 character VIN, no I, O or Q.
callback_urlstring (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)
FieldTypeNotes
request_idstring (uuid)Id of this service history request (UUID). Use it with the status endpoint.
vrmstringOmitted for a request made by VIN only.
vinstringSent only when the request carried a VIN.
status"pending" | "processing" | "completed" | "failed"
messagestring
requested_atstring (date-time)
completed_atstring (date-time)
check_after_secondsintegerWhile open: wait at least this long before polling again.
status_urlstring (uri)Absolute URL of the status endpoint for this request.
callback_urlstring (uri)
cost_penceintegerWhat was charged when the request was accepted.
refunded_penceintegerWhat was given back (a search with no records, or a fault).
errorstringFailed 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_historyobjectPresent once completed.
service_history.record_countinteger
service_history.servicesarray of object
service_history.services[].datestring (date)Record date when it could be read.
service_history.services[].date_rawstringThe date as the manufacturer wrote it.
service_history.services[].typestringAlmost always "Service / Repair": the work done is described in notes.
service_history.services[].mileageintegerMileage 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_rawstringThe 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[].workshopstringWhere 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[].notesstringThe work done, as the manufacturer recorded it. Sometimes in another language.
service_history.messagestringWhy there are no records, when there are none.
service_history.retrieved_atstring (date-time)
service_history.samplebooleanSandbox only: true when these are made-up sample records, not the records of a real vehicle. Never sent by live keys.
service_history.sample_noticestringSandbox only: sent with sample: true, says the records are sample data.
pdf_urlstring (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_secondsinteger
readable_untilstring (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.

GET/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)
FieldTypeNotes
request_idstring (uuid)Id of this service history request (UUID). Use it with the status endpoint.
vrmstringOmitted for a request made by VIN only.
vinstringSent only when the request carried a VIN.
status"pending" | "processing" | "completed" | "failed"
messagestring
requested_atstring (date-time)
completed_atstring (date-time)
check_after_secondsintegerWhile open: wait at least this long before polling again.
status_urlstring (uri)Absolute URL of the status endpoint for this request.
callback_urlstring (uri)
cost_penceintegerWhat was charged when the request was accepted.
refunded_penceintegerWhat was given back (a search with no records, or a fault).
errorstringFailed 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_historyobjectPresent once completed.
service_history.record_countinteger
service_history.servicesarray of object
service_history.services[].datestring (date)Record date when it could be read.
service_history.services[].date_rawstringThe date as the manufacturer wrote it.
service_history.services[].typestringAlmost always "Service / Repair": the work done is described in notes.
service_history.services[].mileageintegerMileage 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_rawstringThe 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[].workshopstringWhere 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[].notesstringThe work done, as the manufacturer recorded it. Sometimes in another language.
service_history.messagestringWhy there are no records, when there are none.
service_history.retrieved_atstring (date-time)
service_history.samplebooleanSandbox only: true when these are made-up sample records, not the records of a real vehicle. Never sent by live keys.
service_history.sample_noticestringSandbox only: sent with sample: true, says the records are sample data.
pdf_urlstring (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_secondsinteger
readable_untilstring (date-time)Finished requests: after this time the status endpoint answers 410 REQUEST_EXPIRED.

Errors: 400, 401, 403, 404, 410, 429, 500. Success: 200.

Market data

POST/v1/market/active

Active 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.

FieldTypeNotes
vrm *stringUK registration. 2 to 8 letters and digits after spaces are removed.
rowsintegerHow many listings to return. Default 50, maximum 200.
matchstring or array of stringOptional. 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)
FieldTypeNotes
vrmstring
total_foundintegerHow many matching adverts there are on the market (not how many were returned).
total_estimatedbooleantrue when total_found was worked out from the share of fetched adverts that matched.
listings_returnedinteger
match_criteriastringComma list of what was actually matched, for example "make,model,year,variant,transmission,body_type,doors,engine_size".
match_not_appliedarray of stringFields you listed in match that are not matched in this version (for example fuel_type).
listingsarray of objectCheapest first. Adverts with a price only.
listings[].idstringOur 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[].vrmstring
listings[].priceintegerAsking price in pounds.
listings[].milesintegerAdvertised mileage in miles.
listings[].yearintegerYear of first registration, as the advert gives it.
listings[].makestring
listings[].modelstring
listings[].variantstringTrim as the advert names it.
listings[].fuel_typestring
listings[].transmissionstringManual, Automatic, Semi Automatic or CVT.
listings[].drivetrainstringFor example 4x2 or 4x4.
listings[].body_typestring
listings[].engine_sizenumberLitres, one decimal place (for example 1.5).
listings[].power_bhpintegerEngine power in bhp, when the advert says.
listings[].euro_statusstringEmissions standard, when the advert says.
listings[].ulez_compliantbooleanSent only when the advert says.
listings[].colourstring
listings[].doorsinteger
listings[].ownersintegerPrevious keepers, when the advert says.
listings[].first_registeredstring (date)Date of first registration, when the advert says. Version 1: an ISO 8601 date-time in UTC.
listings[].write_off_categorystringInsurance write-off category shown on the advert (for example S or N). Left out when the advert shows none.
listings[].previous_priceintegerAn earlier advertised price for this car, in pounds, when one was seen.
listings[].previous_price_datestring (date)When previous_price was seen. Version 1: an ISO 8601 date-time in UTC.
listings[].price_change_pctnumberChange from previous_price to the current price, in per cent (negative = reduced).
listings[].days_at_dealerintegerDays the car has been advertised by this dealer.
listings[].days_on_marketintegerDays in the current advertising stretch.
listings[].dealerobjectThe seller. Contact details are never sent.
listings[].dealer.namestringBusiness name. For a dealer group this is the group name.
listings[].dealer.typestringfranchise or independent, when the advert says.
listings[].dealer.locationstringTown or city where the car is: the branch for a dealer group, else the dealer's address.
listings[].dealer.postcodestringPostcode where the car is (same rule as location).
listings[].dealer.latitudenumberWhere the car is, when known.
listings[].dealer.longitudenumberWhere the car is, when known.
listings[].listing_urlstring (uri)The advert on the dealer's website.
listings[].imagesarray of string (uri)Up to 5 photo links (photo links only).
listings[].first_seenstring (date)When the advert first appeared. Version 1: an ISO 8601 date-time in UTC.
listings[].last_seenstring (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.

POST/v1/market/sold

Sold 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.

FieldTypeNotes
vrm *stringUK registration. 2 to 8 letters and digits after spaces are removed.
rowsintegerHow many listings to return. Default 50, maximum 200.
matchstring or array of stringOptional. 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)
FieldTypeNotes
vrmstring
total_foundintegerHow many matching adverts there are on the market (not how many were returned).
total_estimatedbooleantrue when total_found was worked out from the share of fetched adverts that matched.
listings_returnedinteger
match_criteriastringComma list of what was actually matched, for example "make,model,year,variant,transmission,body_type,doors,engine_size".
match_not_appliedarray of stringFields you listed in match that are not matched in this version (for example fuel_type).
returned_listings_summaryobjectCovers 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.countinteger
returned_listings_summary.price_statsobject
returned_listings_summary.price_stats.mininteger
returned_listings_summary.price_stats.maxinteger
returned_listings_summary.price_stats.averageinteger
returned_listings_summary.price_stats.medianinteger
returned_listings_summary.days_to_sell_statsobject
returned_listings_summary.days_to_sell_stats.mininteger
returned_listings_summary.days_to_sell_stats.maxinteger
returned_listings_summary.days_to_sell_stats.averageinteger
returned_listings_summary.days_to_sell_stats.medianinteger
listingsarray of objectCheapest first. Adverts with a price only.
listings[].idstringOur 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[].vrmstring
listings[].sold_priceintegerThe last asking price in pounds (not a confirmed sale price).
listings[].milesintegerAdvertised mileage in miles.
listings[].yearintegerYear of first registration, as the advert gives it.
listings[].makestring
listings[].modelstring
listings[].variantstringTrim as the advert names it.
listings[].fuel_typestring
listings[].transmissionstringManual, Automatic, Semi Automatic or CVT.
listings[].drivetrainstringFor example 4x2 or 4x4.
listings[].body_typestring
listings[].engine_sizenumberLitres, one decimal place (for example 1.5).
listings[].power_bhpintegerEngine power in bhp, when the advert says.
listings[].euro_statusstringEmissions standard, when the advert says.
listings[].ulez_compliantbooleanSent only when the advert says.
listings[].colourstring
listings[].doorsinteger
listings[].ownersintegerPrevious keepers, when the advert says.
listings[].first_registeredstring (date)Date of first registration, when the advert says. Version 1: an ISO 8601 date-time in UTC.
listings[].write_off_categorystringInsurance write-off category shown on the advert (for example S or N). Left out when the advert shows none.
listings[].previous_priceintegerAn earlier advertised price for this car, in pounds, when one was seen.
listings[].previous_price_datestring (date)When previous_price was seen. Version 1: an ISO 8601 date-time in UTC.
listings[].price_change_pctnumberChange from previous_price to the current price, in per cent (negative = reduced).
listings[].days_at_dealerintegerDays the car has been advertised by this dealer.
listings[].days_to_sellintegerDays in the last advertising stretch before the advert ended.
listings[].dealerobjectThe seller. Contact details are never sent.
listings[].dealer.namestringBusiness name. For a dealer group this is the group name.
listings[].dealer.typestringfranchise or independent, when the advert says.
listings[].dealer.locationstringTown or city where the car is: the branch for a dealer group, else the dealer's address.
listings[].dealer.postcodestringPostcode where the car is (same rule as location).
listings[].dealer.latitudenumberWhere the car is, when known.
listings[].dealer.longitudenumberWhere the car is, when known.
listings[].first_listedstring (date)When the advert first appeared. Version 1: an ISO 8601 date-time in UTC.
listings[].sold_datestring (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.

POST/v1/market/insights

Market 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.

FieldTypeNotes
vrm *stringUK 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)
FieldTypeNotes
insightsobjectFields we do not hold for this car are left out.
insights.vrmstring
insights.supplyobject
insights.supply.active_listingsinteger
insights.supply.estimatedboolean
insights.supply.level"VERY_LOW" | "LOW" | "MODERATE" | "HIGH" | "VERY_HIGH" | "UNKNOWN"
insights.supply.descriptionstring
insights.demandobjectSold 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_daysintegerSold adverts with a price that match the car.
insights.demand.sold_with_priceintegerHow many sold prices the price figures are based on.
insights.demand.median_days_to_sellinteger
insights.demand.mean_days_to_sellinteger
insights.demand.fast_sale_daysintegerA quarter of the sold cars sold within this many days.
insights.demand.slow_sale_daysintegerThree 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.descriptionstring
insights.pricingobjectSold prices across the whole market for the same car, in whole pounds.
insights.pricing.sold_price_mininteger
insights.pricing.sold_price_maxinteger
insights.pricing.sold_price_meaninteger
insights.pricing.sold_price_medianinteger
insights.pricing.percentile_25integer
insights.pricing.percentile_75integer
insights.pricing.percentile_90integer
insights.market_ratingobject
insights.market_rating.scorenumber0 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_scoreinteger
insights.market_rating.interpretationstringStrong market, Average market, Challenging market, or Insufficient data when there is no score.
insights.recommendationsarray of string

Errors: 400, 401, 402, 403, 404, 429, 500, 502, 503. Success: 200.

Analysis

POST/v1/pricing/insights

Pricing 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.

FieldTypeNotes
vrm *stringUK registration. 2 to 8 letters and digits after spaces are removed.
priceintegerOptional. 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)
FieldTypeNotes
insightsobjectFields we do not hold for this car are left out.
insights.vrmstring
insights.vehicleobject
insights.vehicle.makestring
insights.vehicle.modelstring
insights.vehicle.variantstring
insights.vehicle.yearintegerYear of manufacture.
insights.vehicle.fuel_typestring
insights.vehicle.transmissionstring
insights.market_overviewobjectSold 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_supplyinteger
insights.market_overview.active_supply_estimatedboolean
insights.market_overview.sold_last_90_daysintegerSold adverts with a price that match the car.
insights.market_overview.median_days_to_sellinteger
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_analysisobject
insights.pricing_analysis.asking_priceintegerYour price, when sent.
insights.pricing_analysis.market_positionobjectSent when price was sent.
insights.pricing_analysis.market_position.position"VERY_LOW" | "LOW" | "AVERAGE" | "HIGH" | "VERY_HIGH" | "UNKNOWN"
insights.pricing_analysis.market_position.percentileintegerShare 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.comparisonstring
insights.pricing_analysis.market_position.compared_withintegerHow many adverts with a price your price was ranked against.
insights.pricing_analysis.market_position.estimatedbooleantrue when compared_with is an estimate.
insights.pricing_analysis.sold_pricesobjectSold prices in whole pounds.
insights.pricing_analysis.sold_prices.countintegerHow many sold prices these figures are based on.
insights.pricing_analysis.sold_prices.mininteger
insights.pricing_analysis.sold_prices.maxinteger
insights.pricing_analysis.sold_prices.averageinteger
insights.pricing_analysis.sold_prices.medianinteger
insights.pricing_analysis.sold_prices.percentile_25integer
insights.pricing_analysis.sold_prices.percentile_75integer
insights.pricing_analysis.lowest_active_pricesarray of objectThe five cheapest matching adverts.
insights.pricing_analysis.lowest_active_prices[].priceinteger
insights.pricing_analysis.lowest_active_prices[].milesinteger
insights.pricing_analysis.lowest_active_prices[].yearinteger
insights.pricing_analysis.lowest_active_prices[].days_on_marketinteger
insights.pricing_analysis.lowest_active_prices[].dealerstringLeft out when the advert gives no name.
insights.pricing_analysis.lowest_active_prices[].locationstringWhere the car is (the branch for a dealer group).
insights.pricing_analysis.recommended_price_rangeobjectFrom 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.competitiveinteger
insights.pricing_analysis.recommended_price_range.marketinteger
insights.pricing_analysis.recommended_price_range.premiuminteger
insights.selling_estimateobject
insights.selling_estimate.expected_daysinteger
insights.selling_estimate.fast_sale_daysinteger
insights.selling_estimate.slow_sale_daysinteger
insights.selling_estimate.factorsarray of string
insights.recommendationsarray of string

Errors: 400, 401, 402, 403, 404, 429, 500, 502, 503. Success: 200.

Sandbox

GET/v1/sandbox/vrmsNo key needed

Sandbox 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/vrms

Example 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)
FieldTypeNotes
successtrue
dataobject
data.countinteger
data.noticestring
data.vrmsarray of object
data.vrms[].vrmstring
data.vrms[].vinstring 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[].categoriesarray of string
data.vrms[].markersarray of stringEverything this record carries, for example "write-off (Cat B)", "finance", "service history".
data.vrms[].makestring or null
data.vrms[].modelstring or null
data.vrms[].yearinteger or null
data.vrms[].fuel_typestring or null

Reference

GET/v1/openapi.jsonNo key needed

This 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.json

Example answer

{
  "openapi": "3.1.0"
}

Illustrative values, shortened. Field names and types are exact.

Questions: support@dealerpricing.co.uk. Terms: API Terms of Use.