Switch from Synsbasen API to Motorbay: endpoints, fields and a checklist
You call the Synsbasen API today and are considering moving plate and VIN lookups to Motorbay. Here are the differences in access, response shape and fields, what Synsbasen offers beyond Motorbay, code before and after, and a plan for switching without guessing.
By Motorbay · Last checked
Who this is for and what changes
This guide is for you if you call the Synsbasen API and mostly use a vehicle's basic data: plate, VIN, status, make, model, variant, latest inspection and technical data. Then you can usually move to Motorbay in an afternoon. If you use Synsbasen's debt data, insurance, appraisals, monitoring or statistics, read the section on what Synsbasen offers beyond Motorbay before you go on.
We read Synsbasen's documentation and front page on 9 Oct 2026, and everything here about Synsbasen comes from there. Motorbay is a REST API for Danish vehicle data from Motorstyrelsen's public weekly extract. The Danish Motor Register as an API explains what the extract is. Every Motorbay field we name is in the OpenAPI document, which is the API's contract.
| Synsbasen | Motorbay | |
|---|---|---|
| Base URL | https://api.synsbasen.dk/v1 | https://api.motorbay.app/v1 |
| Key | Authorization: Bearer <APIKEY> and Content-Type: application/json | X-Api-Key: <key> |
| Plate lookup | GET /v1/vehicles/registration/:registration | GET /v1/vehicles/plate/{plate} |
| VIN lookup | GET /v1/vehicles/vin/:vin | GET /v1/vehicles/vin/{vin} |
| ID lookup | GET /v1/vehicles/:id, the ID is an integer | GET /v1/vehicles/{id}, the ID is a string |
| Response shape | The object is inside data | The vehicle is the response itself. Lists have data and metadata |
| Field names | snake_case, for example first_registration_date | camelCase, for example firstRegisteredOn |
| More information | expand[]=emission and other expansions in the same call | Separate endpoints per vehicle, for example /engine and /inspection |
| Dates | Strings like yyyy-mm-dd | Days as …On and UTC instants as …At |
| Errors | HTTP status codes 400, 401, 403, 404, 429 and 500 | application/problem+json with a stable code, for example VehicleNotFound |
Same source, different corrections
Both build on Motorstyrelsen's weekly extract. Synsbasen says it mainly relies on the extract and adds debt, insurance and inspections from other sources. Motorbay imports the extract as it is, without correcting or filling in values. Synsbasen, on the other hand, says it corrects and fills in values such as model year and EC type approval. So a value can differ between the two even though both come from the same file.
Data is data as of the extract's date, not real-time. Every Motorbay response has the Motorbay-Data-As-Of header with the time of the extract you are getting. Motorstyrelsen gives no support on using the extract and can't guarantee that the updates happen.
Motorbay is not affiliated with Motorstyrelsen. Motorbay serves technical data about vehicles, not about people.
Field map: from Synsbasen to Motorbay
The tables follow Synsbasen's vehicle object and its expansions. The Endpoint column says where the Motorbay field lives: Vehicle is the response to a lookup by plate, VIN or ID, and the others are endpoints under /v1/vehicles/{vehicleId}. Where it says Closest field, it is the best match in both sets of documentation, but neither says it is the same thing. Compare them on a sample before you rely on them.
The vehicle
| Synsbasen | Motorbay | Endpoint | Note |
|---|---|---|---|
id | id | Vehicle | An integer at Synsbasen, a string at Motorbay. Don't assume the values are equal. Look up by plate or VIN and store Motorbay's ID. |
registration | registrationNumber | Vehicle | null when the vehicle has no plate. |
vin | vin | Vehicle | Same name. |
first_registration_date | firstRegisteredOn | Vehicle | A day, or null. |
status | status | Vehicle | Danish strings at Synsbasen, fixed values at Motorbay. See the value table. |
status_updated_at | statusUpdatedAt | Vehicle | An instant in UTC, not only a day. |
registration_status | registrationStatus | Vehicle | See the value table. |
registration_status_updated_at | registrationStatusUpdatedAt | Vehicle | An instant in UTC, or null. |
kind | kind | Vehicle | An object with id, name and originalName. The name is Danish, for example Personbil, so read kind.name. |
usage | primaryUsage | Vehicle | Closest field. An object like kind. Motorbay also has GET /v1/vehicles/{vehicleId}/secondary-usages. |
brand | make | Vehicle | An object, read make.name. |
model, variant | model, variant | Vehicle | Objects, or null when the vehicle has none. |
version | designationType | Vehicle | Closest field. Motorstyrelsen's type designation, an object or null. |
brand_id, model_id, variant_id | makeId, modelId, variantId | Vehicle | Strings in both. Compare the values before you use them as keys. |
color (under expand[]=body) | color | Vehicle | An object, read color.name. At Motorbay the color is on the vehicle itself. |
model_year | modelYear | /technical-details | At Motorbay the value from the extract. Synsbasen corrects and fills it in. |
category | typeApprovedCategory | /technical-details | Closest field. |
fuel_type | fuelType.name | /fuel-types | One value at Synsbasen, a list at Motorbay with the primary fuel first. The names are Danish, for example Benzin. |
mileage | odometerReading | /engine | Closest field. Motorbay gives thousands of km: 228 means about 228,000 km. Synsbasen's unit is km. |
ncap_five | hasFiveStarNcapRating | /technical-details | True or false. |
eu_version, eu_variant | euVersion, euVariant | /technical-details | Same content, new names. |
ec_type_approval | typeApprovalNumber | /technical-details | Closest field. Motorbay has no corrected version like ec_type_approval_corrected. |
extra_equipment | additionalEquipment | /technical-details | Closest field. |
body_type | bodyType | /technical-details | A fixed list of values, for example sedan, hatchback and stationcar. |
Latest inspection
| Synsbasen | Motorbay | Endpoint | Note |
|---|---|---|---|
last_inspection_date | inspectedOn | /inspection | A day. |
last_inspection_result | result | /inspection | See the value table. |
last_inspection_kind | type | /inspection | See the value table. |
mileage (on /v1/inspections/:id) | odometerReading | /inspection | Closest field. Synsbasen gives km, Motorbay thousands of km. |
| Not in the same object | status, statusUpdatedOn | /inspection | The inspection's status and the day it changed. The value is always active today. |
At Synsbasen the three last_inspection_* fields sit on the vehicle. At Motorbay the latest inspection is a call of its own, and it answers 204 with no content when there is no inspection to show. Treat 204 as "no inspection", not as an error.
Engine, environment and fuels
| Synsbasen | Motorbay | Endpoint | Note |
|---|---|---|---|
emission.co2 | co2Emission | /fuel-types | Closest field. g/km per fuel. Don't use co2Emission from /environmental-information, it is a figure for lorries in g/tkm. |
emission.co | coEmission | /environmental-information | mg/km. |
emission.hc_plus_nox | hcPlusNoxEmission | /environmental-information | mg/km. |
emission.nox | noxEmission | /environmental-information | mg/km. |
emission.particles | particleEmission | /environmental-information | mg/km. |
emission.particle_filter | hasParticleFilter | /environmental-information | True or false. |
emission.smoke_density, emission.smoke_density_engine_speed | smokeDensity, smokeDensityRpm | /environmental-information | The same two quantities. |
emission.euronorm | emissionStandard | Vehicle | An object, read emissionStandard.name. The spelling can differ: Synsbasen's documentation shows Euro V, Motorbay's OpenAPI gives Euro 6 as an example. |
engine.engine_power | maximumPower | /engine | kW in both. |
engine.engine_displacement | displacement | /engine | cm³ in both. |
engine.cylinders | cylinderCount | /engine | Same content. |
engine.engine_code | engineMarking | /engine | Closest field. |
engine.top_speed | maximumSpeed | /technical-details | km/h. |
engine.gears | numberOfGears | /technical-details | Same content. |
engine.fuel_efficiency | kilometersPerLiter | /fuel-types | km/l. Synsbasen gives one combined figure, Motorbay one per fuel. |
engine.electricity_efficiency | electricConsumption | /fuel-types | Wh/km at Motorbay. Synsbasen's documentation uses Wh/km under engine and km/kWh under fuel_types, so check the unit. |
fuel_types[].name, primary_fuel, fuel_cell | fuelType.name, isPrimary, hasFuelCell | /fuel-types | A paged list, primary fuel first. |
Weight, dimensions and equipment
| Synsbasen | Motorbay | Endpoint | Note |
|---|---|---|---|
weight.technical_total_weight | technicalTotalWeight | /technical-details | kg. |
weight.total_weight | totalWeight | /technical-details | kg. |
weight.vehicle_weight | ownWeight | /technical-details | Closest field. kg. |
weight.driveable_weight_minimum, weight.driveable_weight_maximum | minimumRoadWorthyWeight, maximumRoadWorthyWeight | /technical-details | Closest field. Kerb weight, kg. |
weight.road_train_weight | roadTrainWeight | /technical-details | kg. |
weight.coupling_device | isCouplingPossible | /technical-details | Closest field. True or false. |
weight.trailer_with_brakes_weight_maximum, weight.trailer_without_brakes_weight_maximum | couplingWeightWithBrakes, couplingWeightWithoutBrakes | /technical-details | kg. |
weight.trailer_total_weight_maximum | trailerTotalWeight | /technical-details | kg. |
axle.axles | axleCount | /technical-details | Same content. |
axle.axle_track | axleDistance | /technical-details | Closest field. mm. |
axle.pulling_axles | drivingAxles | /technical-details | Closest field. String. |
body.doors, body.passengers | numberOfDoors, passengerCount | /technical-details | Integers. |
body.seats_minimum, body.seats_maximum | minimumSeats, maximumSeats | /technical-details | Integers. |
body.track_width_front, body.track_width_rear | frontTrackWidth, rearTrackWidth | /technical-details | mm. |
body.vin_placement, body.rims_and_tires | vinPlacement, wheelAndTire | /technical-details | Closest field for rims and tires. Strings. |
equipment[].name, equipment[].quantity | name, count | /equipment | A paged list (page, pageSize). The name is Motorstyrelsen's, and originalName never changes. Compare the spelling before you match on text. |
Values
Synsbasen uses Motorstyrelsen's Danish strings as values. Motorbay uses fixed values in camelCase. The names of kind, usage, make, color and fuel are still Danish strings in name. Motorbay's lists of values are in the OpenAPI document.
| Field | Synsbasen | Motorbay |
|---|---|---|
status | Oprettet | created |
status | Registreret | registered |
status | Afmeldt | deregistered |
status | Skrottet | scrapped |
status | Eksporteret | exported |
status | Slettet | deleted |
status | UnderOprettelse | underCreation |
status | DelvistOprettet | partiallyCreated |
status | AdministrativOprettelse | administrativeCreation |
status | HarGennemførtRegistreringssyn | completedRegistrationInspection |
registration_status | Registreret | registered |
registration_status | Afmeldt | deregistered |
registration_status | UnderUdarbejdelse | pending |
last_inspection_result | Godkendt | approved |
last_inspection_result | IkkeFuldstaendigtSyn | incomplete |
last_inspection_result | IkkeGodkendt | rejected |
last_inspection_result | IkkeGodkendtKanGenfremstilles | rejectedReinspectionAllowed |
last_inspection_result | KanGodkendesVedOmsynAfOmsynsvirksomhed | reinspectionCompanyReinspectionAllowed |
last_inspection_result | KanGodkendesVedOmsynAfSynsvirksomhed | inspectionCompanyReinspectionAllowed |
last_inspection_kind | KontrolSyn | scrutiny |
last_inspection_kind | MOT | mot |
last_inspection_kind | PeriodiskSyn | periodic |
last_inspection_kind | RegistreringsSyn | registration |
last_inspection_kind | RegistreringssynToldsyn | customs |
Motorbay's status has three values that Synsbasen's documented list doesn't show: exportMarking, declaredByImporter and declaredByImporterAndDealer. Motorbay's registrationStatus also has unknown. So write a default branch when you translate the values in your code.
What Synsbasen offers beyond Motorbay
You may use these today. Motorbay doesn't have them, so read the list before you move anything.
- Debt:
tinglysning_datareturns liabilities and notices from Tinglysningen, for example about bankruptcy. Motorbay has no debt data. - Insurance and next inspection:
dmr_datalooks up DMR live and returns the insurance company and insurance status, the date of the next periodic inspection and inspection invocations. Motorbay has neither. - Appraisals:
appraisalsreturns valuations of value and registration tax. Motorbay has no valuations. - Same-week lookups:
dmr_fallback=truelooks up new or changed plates live before the next extract is fetched. Motorbay answers only from the latest extract and is not real-time. - Monitoring and webhooks: with
watched_vehiclesand webhooks you are told when a vehicle changes or a new inspection arrives. Motorbay has no webhooks. You can readMotorbay-Data-As-OforGET /v1/datasetto see whether a new extract has been imported. - Search and statistics:
POST /v1/vehicles/searchhas search predicates,COUNT, averages and grouping. Motorbay'sGET /v1/vehiclesfilters on make, kind, usage, color, model year and plate, but doesn't count or group. - All inspections:
/v1/inspectionsreturns each inspection with test center, error codes, PDF and documentation. Motorbay returns only the latest inspection, with type, day, result and odometer reading. - Blocks, permits and leasing:
registration_blocks,permits,leasing_periodsandleasing_period_start. Motorbay doesn't have them yet. They are being built. - Corrected and calculated fields: corrected model year and EC type approval, energy class, environmental zone status, horsepower, kilometers per year, age and estimated next inspection. Motorbay gives the values as they are in the extract.
- Other expansions: periodic taxes, EU Safety Gate alerts, plate recognition from images, dealer plates and widgets for dealer sites. Motorbay has none of these.
Synsbasen also offers a Postman collection and a Ruby gem. Motorbay has an OpenAPI document, so you can generate a client in your own language. If you use something from the list in production, you can run both: keep Synsbasen for what Motorbay lacks, and move the basic-data lookups.
A plate lookup before and after
The examples use the plate AB12345 as a placeholder. First curl. Synsbasen's response comes with everything you ask for through expand. At Motorbay you fetch engine and environment from one endpoint each, with the vehicle's id.
curl -i -H "Authorization: Bearer $SYNSBASEN_API_KEY" \
"https://api.synsbasen.dk/v1/vehicles/registration/AB12345" curl -i -H "X-Api-Key: $MOTORBAY_API_KEY" \
"https://api.motorbay.app/v1/vehicles/plate/AB12345"
# What Synsbasen returns with expand[]=engine and expand[]=emission
# you fetch per vehicle, with the id from the response above:
curl -H "X-Api-Key: $MOTORBAY_API_KEY" \
"https://api.motorbay.app/v1/vehicles/$VEHICLE_ID/engine"
curl -H "X-Api-Key: $MOTORBAY_API_KEY" \
"https://api.motorbay.app/v1/vehicles/$VEHICLE_ID/environmental-information" Then JavaScript with fetch. Both functions return the same object, and both return null when the plate isn't found. At Motorbay the code waits for Retry-After once on RateLimitExceeded (60 a minute per key) and gives up on QuotaExceeded, because the month's requests are then used.
async function lookupPlate(plate) {
const res = await fetch("https://api.synsbasen.dk/v1/vehicles/registration/" + encodeURIComponent(plate), {
headers: {
Authorization: "Bearer " + process.env.SYNSBASEN_API_KEY,
"Content-Type": "application/json",
},
});
if (res.status === 404) return null;
if (res.status === 429) throw new Error("Synsbasen: too many requests");
if (!res.ok) throw new Error("Synsbasen: HTTP " + res.status);
const { data } = await res.json();
return {
plate: data.registration,
vin: data.vin,
firstRegistered: data.first_registration_date,
make: data.brand,
model: data.model,
};
} const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
async function lookupPlate(plate, retried = false) {
const res = await fetch("https://api.motorbay.app/v1/vehicles/plate/" + encodeURIComponent(plate), {
headers: { "X-Api-Key": process.env.MOTORBAY_API_KEY },
});
if (res.status === 404) return null; // VehicleNotFound
if (res.status === 429) {
const problem = await res.json();
if (problem.code === "QuotaExceeded" || retried) throw new Error("Motorbay: " + problem.code);
await sleep(Number(res.headers.get("Retry-After") ?? 1) * 1000);
return lookupPlate(plate, true);
}
if (!res.ok) throw new Error("Motorbay: HTTP " + res.status);
const vehicle = await res.json(); // no data wrapper
return {
plate: vehicle.registrationNumber,
vin: vehicle.vin,
firstRegistered: vehicle.firstRegisteredOn,
make: vehicle.make.name,
model: vehicle.model?.name ?? null,
};
} Plates are reused. At Motorbay the lookup returns the vehicle registered with the plate now. Synsbasen says its lookup returns the most recent vehicle with the plate, so compare the two on plates that have been on several vehicles. To see every vehicle that has carried the plate, use GET /v1/vehicles?registrationNumber=.
Price at your volume
Synsbasen sells monthly plans with 500, 2,500 or 12,500 requests included. Requests beyond that cost a set amount each, and some expansions cost tokens. Current prices are on Synsbasen's site. Synsbasen says only requests that return 200 count toward the quota.
At Motorbay you start free with no card, and the paid plans have a set price per month with requests included and then a set price per request. The prices and a calculator are on the pricing page, and they are read from the API, so they aren't typed here.
- Count calls, not lookups. One Synsbasen call with
expandcan be several calls at Motorbay. A plate lookup plus engine and latest inspection is three calls, and each call is a request. - Call only the endpoints you use. If you only need make, model and VIN, the plate lookup is enough.
- Enter the numbers in the calculator. Take the number of calls per month from your own log, not from an estimate.
Switch in an afternoon: checklist
Run both APIs side by side until you have seen that the differences are the ones you expect.
- Create an account and an API key, and run the curl example with a plate from your own tests.
- Put the lookup in one place. Write a function that returns your own field names, so the rest of your code knows neither Synsbasen's nor Motorbay's names.
- Translate the fields with the tables here, and add only the sub-endpoints you use:
/inspection,/engine,/technical-details,/environmental-information,/fuel-typesand/equipment. - Run both in parallel. Look up a sample of a few hundred plates from your own log at both, and compare field by field.
- Sort the differences. Typical causes are the extract's date, fields corrected at Synsbasen, thousands of km, the spelling of emission standards and equipment, and plates with several vehicles.
- Handle 404, 429 and 204. 404 is
VehicleNotFound, 429 can beRateLimitExceededorQuotaExceeded, and 204 means the sub-resource is empty. - **Store
Motorbay-Data-As-Of** next to what you store, so you know how old your data is. - Move traffic gradually, for example behind a feature flag, and leave the Synsbasen key in place until at least one new extract has arrived.
- Cancel afterwards, if you use nothing Synsbasen offers beyond Motorbay.
The sources are Motorstyrelsen's own pages, Motorstyrelsen's extract and Motorbay's API docs.