Plate Lookup

GET/api/v1/lookup

Look up a vehicle by registration plate and state. Returns structured vehicle data including the model year range, make, model, body type, and engine displacement for cars, motorcycles, and commercial vehicles. Pass detailed=true to include an extended vehicle description when available. The first lookup of a plate is performed live at request time. A successful result is then cached for up to 3 days, so repeat lookups of the same plate and state in that window return instantly; a plate change or a newly registered vehicle can take up to 3 days to show. Not-found results are not cached.

Headers

HeaderRequiredDescription
X-API-KeyRequiredYour API key (e.g. pk_live_...). Available from your dashboard.

Query parameters

ParameterTypeRequiredDescription
platestringRequiredRegistration plate number (e.g. ABC123). Case-insensitive. Letters, numbers, dashes, and spaces only, maximum 10 characters -- dashes and spaces are stripped before lookup.
statestringRequiredAustralian state or territory code: ACT, NSW, NT, QLD, SA, TAS, VIC, WA
detailedbooleanOptionalSet to true to include detailed_description in the vehicle object. This field contains extended vehicle data (chassis codes, drivetrain, fuel type, engine code, and power output) when available. Defaults to false. May return null for some vehicles.

Example response

200 OK -- application/json
{
  "success": true,
  "vehicle": {
    "year_range": "2003-2006",
    "lowest_year": 2003,
    "highest_year": 2006,
    "make": "TOYOTA",
    "model": "COROLLA",
    "description": "03~06 TOYOTA COROLLA SPORTIVO 1.8L PETROL",
    "body": "HATCHBACK",
    "engine": "1.8L",
    "vehicle_id": 5152708
  },
  "source": "plateapi",
  "duration_ms": 1509.96,
  "request_id": "req_7f3a9c1b4e8d2f6a0c5e3d1b",
  "alternatives": [
    {
      "year_range": "2003-2006",
      "lowest_year": 2003,
      "highest_year": 2006,
      "make": "TOYOTA",
      "model": "COROLLA",
      "description": "03~06 TOYOTA COROLLA ASCENT 1.8L PETROL",
      "body": "SEDAN",
      "engine": "1.8L",
      "vehicle_id": 1857621
    }
  ],
  "match": "multiple"
}

Response fields

FieldTypeDescription
successbooleanWhether a vehicle was found
vehicle.year_rangestringModel year range (e.g. 2003-2006). Vehicles still in production return an open range like 2020-ON.
vehicle.lowest_yearintegerFirst model year of the range
vehicle.highest_yearinteger|nullLast model year of the range. null when the model is still in production.
vehicle.makestringManufacturer (e.g. TOYOTA, HOLDEN, FORD)
vehicle.modelstringModel name
vehicle.descriptionstringFull vehicle description string
vehicle.bodystring|nullBody type (SEDAN, HATCHBACK, UTE, etc.)
vehicle.enginestring|nullEngine displacement (e.g. 1.8L)
vehicle.vehicle_id NEWinteger|nullPlateAPI ID of this exact vehicle variant. Stable: the same variant always has the same ID, on every lookup and in the vehicle database (/api/v1/vehicles). null when the variant could not be identified with certainty -- an ID is never guessed. With match: "multiple" it identifies the first candidate, not necessarily the plate's own trim: check the alternatives too.
vehicle.detailed_descriptionstring|nullExtended vehicle description including chassis codes, drivetrain, fuel type, engine code, and power output. Only present when detailed=true is passed. May be null even when requested -- not all vehicles have this data.
sourcestringAlways "plateapi". Present on successful lookups.
duration_msnumberTotal lookup time in milliseconds
alternativesarrayOther possible vehicle matches (e.g. different engine variants). Each has the same fields as vehicle, including its own vehicle_id.
match NEWstring"exact": one variant. "multiple": several trims of one make and model, all on sale at the same time; vehicle is the first with its vehicle_id, the rest are in alternatives. "ambiguous": the candidates are different makes or models, or were not all on sale at the same time (for example different generations), so vehicle carries no vehicle_id (it would be a guess) and alternatives lists every candidate, including the one shown as vehicle, each with its ID. Present on successful lookups.
request_idstringUnique identifier for this request (e.g. req_7f3a9c1b4e...), also sent as the X-Request-ID response header. Include when contacting support.

Handling multiple matches

Some plates map to more than one variant -- typically trims of the same model. The first candidate (in a fixed order, so the same plate always gives the same one) is returned as vehicle and the rest arrive in alternatives, each with the same fields and its own vehicle_id. When every candidate is a trim of one make and model and all of them were on sale at the same time, match is "multiple":

200 OK -- plate with three variants
{
  "success": true,
  "vehicle": {
    "year_range": "2020-ON",
    "lowest_year": 2020,
    "highest_year": null,
    "make": "ISUZU",
    "model": "D-MAX",
    "description": "20~ ISUZU D-MAX 3.0L DIESEL",
    "body": null,
    "engine": "3.0L  DIE 4JJ3-TCX I4 16v DOHC I/C Turbo CRD {140kW}",
    "vehicle_id": 1981765
  },
  "source": "plateapi",
  "duration_ms": 1219.6,
  "request_id": "req_4f1a9c2e7b3d5a8f0c6e1b2d",
  "alternatives": [
    {
      "year_range": "2020-ON",
      "lowest_year": 2020,
      "highest_year": null,
      "make": "ISUZU",
      "model": "D-MAX LS-U, SX SPACECAB",
      "description": "20~ ISUZU D-MAX LS-U, SX SPACECAB 3.0L DIESEL",
      "body": null,
      "engine": "3.0L  DIE 4JJ3-TCX I4 16v DOHC I/C Turbo CRD {140kW}",
      "vehicle_id": 1989953
    },
    {
      "year_range": "2020-ON",
      "lowest_year": 2020,
      "highest_year": null,
      "make": "ISUZU",
      "model": "D-MAX SX",
      "description": "20~ ISUZU D-MAX SX 3.0L DIESEL",
      "body": null,
      "engine": "3.0L  DIE 4JJ3-TCX I4 16v DOHC I/C Turbo CRD {140kW}",
      "vehicle_id": 2393225
    }
  ],
  "match": "multiple"
}

When the candidates are different makes or models, or were not all on sale at the same time (a generation change, or trims that only overlapped for one model year), match is "ambiguous": vehicle has vehicle_id null and alternatives lists every candidate, including the one shown as vehicle, each with its ID.

For parts fitment, do not silently pick the first match -- showing a quick "which one is yours?" list avoids wrong-part orders:

javascript
const data = await res.json();

if (data.success && data.match === "multiple") {
  // Trims of one model: ask the customer to confirm theirs
  const options = [data.vehicle, ...data.alternatives];
  showVariantPicker(options);   // e.g. radio list of descriptions
} else if (data.success && data.match === "ambiguous") {
  // Different models or generations: vehicle has no vehicle_id,
  // alternatives already contains every candidate with its ID
  showVariantPicker(data.alternatives);
} else if (data.success) {
  confirmVehicle(data.vehicle);
}

Integrate on vehicle_id, not on text. A lookup's make, model and description can be worded differently from the vehicle record the ID points to: the sandbox plate TEST123 returns model HILUX, while record 1380801 has model HILUX SR. The ID is the same either way.

Every option carries its own vehicle_id, so once the customer picks one you can store that ID, or fetch the full record from /api/v1/vehicles/{vehicle_id}. If you need every vehicle the plate could be (for example to show parts that fit any of them), use all the IDs.


Error Responses

When no vehicle is found, the API returns 200 OK with a JSON object containing success: false, a machine-readable code, and an error field describing what went wrong. Invalid requests return 400, missing or invalid API keys return 401, keys that are not allowed to make the request return 403, and rate or quota limits return 429. When a lookup cannot be completed right now, the API returns 503: retry the request.

Note: lookups that complete but find no vehicle still count toward your monthly quota -- each lookup performs a full search regardless of the outcome. Requests rejected with 400, 401, 403, or 429 do not consume quota, and neither do lookups that end in 503 or internal_error.

StatusBodyWhenUses quota
200code: "not_found"No vehicle was found for the plate and state.Yes
200code: "internal_error"The lookup failed on our side. Try again.No
400detailInvalid plate or state, or a missing parameter.No
401detailNo API key, an invalid or deactivated key, or an account whose email is not verified yet.No
403detailThe request comes from an IP address that is not on your key's IP allowlist (set in your dashboard), or the account is banned.No
429detail + Retry-AfterPer-minute rate limit or monthly quota reached.No
503code: "lookup_failed"The lookup could not be completed right now. Retry after a few seconds; repeated failures for one plate are worth reporting with the request_id.No
503detail + Retry-AfterThe service is briefly unavailable (rare). Retry after the Retry-After seconds.No

The vehicle database endpoints have their own error responses (Free and Lite keys get 403, an unknown vehicle_id gets 404, and an outage gets 503 with Retry-After): see Vehicle database.

200 OK -- vehicle not found
{
  "success": false,
  "vehicle": null,
  "code": "not_found",
  "error": "Vehicle not found. Check your plate and try again.",
  "duration_ms": 4521.33,
  "request_id": "req_2c1d9e7a4b3f8e6d0a1b5c7e"
}

Error examples

400 Bad Request -- invalid plate characters
{
  "detail": "Invalid plate. Plates may only contain letters, numbers, dashes, and spaces."
}
401 Unauthorized -- invalid key
{
  "detail": "Invalid API key"
}
403 Forbidden -- IP address not on the key's allowlist
{
  "detail": "This API key is not allowed from your IP address."
}
403 Forbidden -- banned account
{
  "detail": "This account has been permanently banned for violating our Terms of Service."
}
429 Too Many Requests -- per-minute rate limit
Retry-After: 42

{
  "detail": "Per-minute rate limit exceeded (10/min on starter plan). Slow down."
}
429 Too Many Requests -- monthly quota exhausted
Retry-After: 3600

{
  "detail": "Monthly quota exceeded. Used 600/600 lookups on starter plan. Upgrade at plateapi.com.au"
}
503 Service Unavailable -- lookup could not be completed
{
  "success": false,
  "vehicle": null,
  "code": "lookup_failed",
  "error": "Lookup temporarily unavailable. Please retry.",
  "duration_ms": 6012.4,
  "request_id": "req_9b2e4c1d7a3f5e8b0c6d2a4f"
}
503 Service Unavailable -- service briefly unavailable
Retry-After: 5

{
  "detail": "Service temporarily unavailable. Please retry."
}

Rate Limits

Requests are rate-limited per minute based on your plan, over a sliding 60-second window: each request counts against the limit for 60 seconds after it was made, not until the top of the next minute. Exceeding the limit returns a 429 Too Many Requests response. Its Retry-After header is the number of seconds until your oldest request in the window expires (1 to 60), so waiting that long always frees a slot. A 429 for an exhausted monthly quota sends Retry-After: 3600 instead.

PlanRate LimitMonthly Lookups
Free1 request/min20
Lite5 requests/min200
Starter10 requests/min600
Growth30 requests/min2,500
Enterprise60 requests/min18,000
Enterprise+CustomCustom (contact us)

Tracking your usage from response headers

Every successful lookup includes rate limit headers, so your application can track quota without extra API calls:

HeaderDescription
X-RateLimit-LimitYour monthly lookup allowance
X-RateLimit-RemainingLookups left in the current billing period
X-RateLimit-PlanYour current plan slug (e.g. starter)
python -- warn before the quota runs out
resp = requests.get(
    "https://api.plateapi.com.au/api/v1/lookup",
    params={"plate": "ABC123", "state": "VIC"},
    headers={"X-API-Key": "pk_live_YOUR_API_KEY"},
)
remaining = resp.headers.get("X-RateLimit-Remaining")
if int(remaining) < 50:
    notify_team(f"PlateAPI quota low: {remaining} lookups left")