
You need to turn a Dutch license plate (kenteken) into dependable vehicle attributes you can use in dealer tools, marketplace listings, fleet intake forms, insurance quotes, or AI agents. By the end of this guide, you will POST a Dutch plate to Vehicle API, receive a normalized JSON payload with make, model, body, seats, engine, APK expiry and more, and ship it with correct error handling, caching, and coverage expectations.
What the Dutch plate endpoint does (and doesn’t)
The Dutch License Plate endpoint resolves a Netherlands plate to technical and administrative attributes from RDW open data. It is a simple JSON POST to a single endpoint:
- Endpoint: POST https://vehicles-api.com/api/v1/plate/nl
- Input: plate string (with or without dashes, case-insensitive; for example, K875HJ or K-875-HJ)
- Output: normalized, structured vehicle data
Important coverage notes you must account for in production:
- VIN is never provided by RDW. The vin field is always null for Dutch plates.
- Dutch plate data is published by RDW open data; values can change as RDW updates their datasets.
- US-market safety datasets (recalls, owner complaints, NCAP crash ratings) are not returned by the NL plate lookup. Those datasets exist in Vehicle API for US-market vehicles and are matched by make + model + year, but they are out-of-scope for this endpoint.
- This is not a vehicle history report. It does not include title events, ownership history, or accident history.
You can explore the broader platform and pricing at vehicles-api.com. Every plan includes a 7-day free trial, with Starter at $19/mo.
Authentication, base URL, and response envelope
All requests go to the base URL https://vehicles-api.com/api/v1 and require an API key. Provide your key as X-API-Key: YOUR_API_KEY. Authorization: Bearer YOUR_API_KEY is also accepted if you prefer that header style.
Every response is wrapped in a consistent envelope. Successful responses include:
- status: always "success"
- source: which dataset or resolver powered the response. For Dutch plates, it is "plate_nl".
- data: the object with vehicle fields
Error responses include:
- status: "error"
- error.code: a machine-readable error code
- error.message: a human-readable explanation
For example, an unknown or invalid Dutch plate returns HTTP 404 with error.code not_found. Handle not_found specifically to give users actionable UI feedback (e.g., “Plate not found. Check the plate or try another.”).
Official cURL example
Use this copy-pasteable cURL as your first smoke test. It targets the live endpoint and demonstrates the minimal request body. Replace YOUR_API_KEY with your key.
curl -X POST "https://vehicles-api.com/api/v1/plate/nl" -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" -d '{"plate":"K875HJ"}'
JavaScript example: fetch and use fields
This example posts a plate, checks the response envelope, and extracts the fields most apps need to display or store.
async function lookupDutchPlate(plate) {
const url = 'https://vehicles-api.com/api/v1/plate/nl';
const payload = { plate }; // 'K875HJ' or 'K-875-HJ' both work
const res = await fetch(url, {
method: 'POST',
headers: {
'X-API-Key': 'YOUR_API_KEY',
'Content-Type': 'application/json'
// Alternatively: 'Authorization': 'Bearer YOUR_API_KEY'
},
body: JSON.stringify(payload)
});
// Handle HTTP status-level errors first
if (!res.ok) {
// Attempt to parse the structured error if present
let errPayload;
try {
errPayload = await res.json();
} catch {
throw new Error(`HTTP ${res.status} during plate lookup`);
}
const code = errPayload?.error?.code || 'unknown_error';
const msg = errPayload?.error?.message || `HTTP ${res.status}`;
if (res.status === 404 && code === 'not_found') {
throw new Error('Plate not found in RDW data');
}
throw new Error(`${code}: ${msg}`);
}
const body = await res.json();
if (body.status !== 'success' || !body.data) {
throw new Error('Unexpected response envelope');
}
// Extract the fields you will actually use
const {
plate: normalizedPlate,
vin, // Always null for NL plates
year,
make,
model,
body: bodyStyle,
color, // RDW color labels are typically in Dutch (e.g., GRIJS = gray)
doors,
seats,
vehicle_type, // e.g., Personenauto
engine,
mass_kg,
apk_expires,
first_registration,
open_recall // Boolean (RDW-sourced context; not a US NHTSA match)
} = body.data;
// Example: map to a simplified internal DTO
return {
plate: normalizedPlate,
year,
make,
model,
bodyStyle,
color,
doors,
seats,
vehicleType: vehicle_type,
engine: engine ? {
cylinders: engine.cylinders,
displacementCc: engine.displacement_cc,
fuel: engine.fuel
} : null,
massKg: mass_kg,
apkExpires: apk_expires,
firstRegistration: first_registration,
vin, // keep for schema consistency; always null for NL
openRecall: open_recall
};
}
// Example usage
lookupDutchPlate('K-875-HJ')
.then(v => console.log('Vehicle:', v))
.catch(err => console.error('Lookup failed:', err.message));
Official JSON response
This is the official example from the documentation. Use it for contract tests and field mapping.
{
"status": "success",
"source": "plate_nl",
"data": {
"plate": "K875HJ",
"vin": null,
"year": 2021,
"make": "TOYOTA",
"model": "AYGO",
"body": "hatchback",
"color": "GRIJS",
"doors": 4,
"seats": 4,
"vehicle_type": "Personenauto",
"engine": {
"cylinders": 3,
"displacement_cc": 998,
"fuel": "Benzine"
},
"mass_kg": 815,
"apk_expires": "2027-01-04",
"first_registration": "2021-01-04",
"open_recall": false
}
}
Fields you will actually use
Map these fields to your UI or storage schema. Expect nulls where noted and preserve types to avoid downstream bugs.
- data.plate: The normalized plate without dashes (e.g., "K875HJ"). You may want to store both the canonical form and the user-entered form.
- data.vin: Always null for RDW data. Do not expect a VIN from Dutch plates. If your schema requires a VIN, allow null.
- data.year, data.make, data.model: Core descriptors usable in search facets and listing titles.
- data.body: Body style string (e.g., "hatchback"). Localize for end users if needed.
- data.color: RDW-provided color label (e.g., "GRIJS"). Consider mapping to your taxonomy if you display color chips.
- data.doors, data.seats: Integers you can surface in filters and quote forms.
- data.vehicle_type: Often "Personenauto" (passenger car). Useful for validations or category filters.
- data.engine: Object with cylinders (int), displacement_cc (int), and fuel (string, e.g., "Benzine"). Preserve units (cc) if you compute power-to-weight or display engine sizes.
- data.mass_kg: Vehicle mass in kilograms. Keep the unit when storing.
- data.apk_expires: Date string (YYYY-MM-DD) for APK inspection expiry. Display this in local time formatting, but do not change the stored value.
- data.first_registration: Date string (YYYY-MM-DD). Good for age calculations.
- data.open_recall: Boolean flag. RDW context; this is not the US NHTSA match logic. Present it clearly in UI if you show it.
Envelope fields:
- status: "success" or "error". Always check this before using data.
- source: "plate_nl" for this endpoint. Useful for observability and routing.
Input normalization and idempotency
The endpoint accepts plates with or without dashes and in any case. For consistency, you can pre-normalize:
- Trim whitespace
- Uppercase
- Remove dashes and spaces before sending
Vehicle API returns a normalized plate in data.plate. Use that as your canonical cache key to avoid duplicate rows when users enter multiple formats (e.g., K-875-HJ vs K875HJ).
Caching strategy for production
To reduce latency and stay within plan quotas, cache decoded plates. Recommended practices:
- Key: "nl:" + normalized plate (data.plate).
- TTL: choose a duration aligned with how often your application needs fresh APK or attribute updates. RDW data can change; do not hardcode excessively long TTLs.
- Revalidation: if APK expiry is approaching or has passed, fetch fresh data on read.
- Null VIN: store vin as null explicitly to prevent repeated attempts to “discover” a VIN from the same plate.
If you use both plate-based and VIN-based features in the same app, remember that Dutch plates do not provide VINs. Do not attempt to chain this endpoint to VIN-dependent safety datasets. VIN decoding is available separately and works worldwide, with the fullest detail for US-market vehicles, but it requires a VIN you already have, not a Dutch plate.
Error handling details
Implement three tiers of error checks:
- HTTP status: 2xx means proceed to parse. 4xx/5xx means check for the structured error envelope.
- Envelope status: status === "success" to use data. If status === "error", inspect error.code and error.message.
- Known codes: Handle 404 with error.code "not_found" as a user-fixable input issue. Other codes should be logged and surfaced as generic errors.
Null fields are not errors. In particular, vin is always null by design. If a sub-object like engine is ever missing, treat it as null and guard your accessors to avoid runtime errors.
Rate limits and monthly quota
Vehicle API enforces rate limits and plan quotas. Exact numbers vary by plan; consult the Documentation for current limits. In application code:
- Cache successful lookups to minimize repeat calls.
- Batch or queue high-volume plate checks to smooth bursts.
- Detect 429 responses and backoff before retrying.
You can manage keys and trial activation directly at vehicles-api.com. Every plan includes a 7-day free trial.
Observability and schema stability
The source field is stable and helps you route telemetry per integration (e.g., "plate_nl"). Log the entire envelope for traceability. For analytics, preserve both the user-entered plate and data.plate. This makes it easy to de-duplicate, reproduce support issues, and demonstrate idempotent behavior to your stakeholders.
Integration patterns by product type
- Dealer tools and marketplaces: Autofill listing forms with year/make/model/body/seats/doors from the plate. Show APK expiry and mass_kg in seller disclosures.
- Fleet intake: Validate vehicle_type and mass_kg on onboarding. Schedule proactive inspections using apk_expires.
- Insurance quoting: Use year/make/model/body, engine.displacement_cc, and seats to pre-populate quotes. If your pricing model needs a VIN, prompt for manual VIN entry because Dutch plates cannot supply it.
- AI agents: Call the same REST endpoint or use the MCP server to ground responses in RDW data.
Using the MCP server for agents and tools
If you’re building an AI agent or tool runner that benefits from a toolcalling protocol, Vehicle API is also available as an MCP server at MCP. The same Dutch plate capability is exposed there, so you can integrate structured plate lookups without wiring raw HTTP in your agent loop.
Operational safeguards
- Input validation: Reject empty or non-alphanumeric input before calling the API. Optionally strip spaces/dashes client-side.
- Timeouts: Use reasonable request timeouts in your HTTP client to protect UX.
- Retries: Retry only idempotent failures (e.g., network timeouts). Do not retry 404 not_found.
- Localization: Translate RDW color labels and vehicle_type to your audience if needed.
- Schema evolution: Code defensively against additional fields the API may add in the future; ignore unknown fields safely.
Testing checklist
- Happy path: Known plate returns "status":"success" and data fields are mapped correctly.
- Normalization: K-875-HJ and K875HJ produce a single stored record keyed by data.plate.
- Error path: Unknown plate triggers HTTP 404 with error.code not_found; UI shows actionable message.
- Null handling: vin is null; engine object values read safely; optional fields guarded.
- Caching: Repeated lookups of the same plate hit your cache; expired APK dates trigger revalidation logic.
Security and compliance notes
Store only what your application needs. While RDW data is open, you should still secure your API key, confine it to server-side contexts when possible, and rotate keys if exposed. For client-side apps, consider a proxy or MCP-based tool execution model to keep the key off devices.
FAQ
Does the Dutch license plate lookup return a VIN?
No. RDW does not publish VINs in open data. The vin field is always null for Dutch plates.
Can I add US safety data (recalls, complaints, NCAP) to Dutch plates?
Those datasets are US-market only and matched by make + model + year. The Dutch plate endpoint does not return those fields. If you already have a VIN for a US-market vehicle, you can use Vehicle API’s VIN capabilities separately.
What format should I send the plate in?
Send the string with or without dashes (e.g., K-875-HJ or K875HJ). The API normalizes and returns data.plate without dashes.
What are the rate limits and monthly quotas?
They depend on your plan. Cache successfully decoded plates and backoff on 429s. Check the Documentation for current limits.
Is this a vehicle history report?
No. This endpoint returns current RDW-based attributes (e.g., body, engine, APK expiry) and does not include title events, accidents, or ownership history.
Start integrating the Dutch License Plate API now. Create your key and get the 7-day free trial at Register. For reference material and additional endpoints, see the Documentation, and consider the MCP server if you’re wiring up AI agents.




