Python SDK for PlateAPI -- Australian vehicle registration plate lookup.
pip install plateapiRequires Python 3.8+.
from plateapi import PlateAPI
client = PlateAPI("pk_live_your_api_key")
result = client.lookup("ABC123", "VIC")
if result.success:
print(result.vehicle.make)
print(result.vehicle.model)
print(result.vehicle.year)result = client.lookup("ABC123", "VIC")
print(result.success) # True if a vehicle was found
print(result.vehicle.make) # "TOYOTA"
print(result.vehicle.model) # "HILUX"
print(result.vehicle.year) # 2015
print(result.vehicle.year_range) # "2015 - 2023"
print(result.vehicle.lowest_year) # 2015
print(result.vehicle.highest_year) # 2023
print(result.vehicle.body) # "UTILITY"
print(result.vehicle.engine) # "2.8L"
print(result.vehicle.description) # "TOYOTA HILUX UTILITY 2.8L"
print(result.duration_ms) # 2451.3
print(result.source) # data source identifier
print(result.request_id) # "req_7f3a9c1b4e..." (include when contacting support)Valid states: NSW, VIC, QLD, SA, WA, TAS, NT, ACT.
Pass detailed=True to get extended vehicle descriptions when available.
result = client.lookup("ABC123", "NSW", detailed=True)
if result.success:
print(result.vehicle.detailed_description)
print(result.vehicle.series)Some plates match more than one vehicle record. The best match is in result.vehicle, and any alternatives are in result.alternatives.
result = client.lookup("ABC123", "VIC")
if result.alternatives:
for alt in result.alternatives:
print(f" Also matched: {alt.make} {alt.model} ({alt.year_range})")Browse the full vehicle database (32,000+ vehicles, 213 makes). Each call narrows the cascade -- call with no arguments to get all makes, then pass make to get models, and so on through all 7 levels. Paid plans only, no quota consumed.
Returns a VehiclesResult with success, type (the cascade level), data (list of values), total, and duration_ms.
# Step 1: All makes
result = client.vehicles()
print(result.type) # "make"
print(result.data[:5]) # ["ABARTH", "AC", "ALFA ROMEO", ...]
print(result.total) # 213
# Step 2: Models for a make
result = client.vehicles(make="TOYOTA")
print(result.type) # "model"
print(result.data[:5]) # ["86", "ALPHARD", "AURION", ...]
# Step 3: Years for a make + model
result = client.vehicles(make="TOYOTA", model="HILUX")
print(result.type) # "year"
print(result.data[:5]) # [2025, 2024, 2023, ...]
# Step 4: Series for a make + model + year
result = client.vehicles(make="TOYOTA", model="HILUX", year=2020)
print(result.type) # "series"
print(result.data) # ["SR", "SR5", "Rogue", "Rugged", ...]
# Step 5: Engines for a make + model + year + series
result = client.vehicles(make="TOYOTA", model="HILUX", year=2020, series="SR5")
print(result.type) # "engine"
print(result.data) # ["2.8L", "2.4L", ...]
# Step 6: Variants for a make + model + year + series + engine
result = client.vehicles(make="TOYOTA", model="HILUX", year=2020, series="SR5", engine="2.8L")
print(result.type) # "variant"
print(result.data) # ["4x4 Double Cab", "4x4 Extra Cab", ...]
# Step 7: Full vehicle details
result = client.vehicles(
make="TOYOTA", model="HILUX", year=2020, series="SR5",
engine="2.8L", variant="4x4 Double Cab",
)
print(result.type) # "vehicle"
print(result.data) # [{"make": "TOYOTA", "model": "HILUX", ...}]For vehicles without a series code, pass an empty string:
result = client.vehicles(make="TOYOTA", model="HILUX", year=2020, series="")usage = client.usage()
print(f"{usage.used_this_month}/{usage.monthly_limit} lookups used")
print(f"{usage.remaining} remaining")
print(f"{usage.percent_used}% used")
print(f"Plan: {usage.plan}")
print(f"Rate limit: {usage.rate_limit_per_min}/min")
print(f"Period: {usage.period_start} to {usage.period_end}")
print(f"Days remaining: {usage.days_remaining}")
print(f"Last lookup: {usage.last_lookup_at}")
print(f"Top-up credits: {usage.topup_credits}")
print(f"Cancel at period end: {usage.cancel_at_period_end}")
print(f"Cancel at: {usage.cancel_at}")Retrieve your lookup history with optional filtering and pagination. Useful for auditing, debugging, and building analytics.
# Last 10 lookups
logs = client.logs(limit=10)
for entry in logs.logs:
status = "found" if entry.success else "not found"
print(f"{entry.created_at} | {entry.plate} ({entry.state}) | {status} | {entry.duration_ms}ms")
print(f"Showing {logs.count} of {logs.total} total")# Filter by plate
logs = client.logs(plate="ABC123")
# Only failed lookups
logs = client.logs(success=False)
# Time range (ISO 8601, UTC)
logs = client.logs(
since="2026-07-01T00:00:00",
until="2026-07-31T23:59:59",
)
# Pagination
page1 = client.logs(limit=50, offset=0)
page2 = client.logs(limit=50, offset=50)Each LogEntry contains: plate, state, success (1 or 0), error, duration_ms, make, model, year, client_ip, request_id, created_at.
Check API availability. No authentication required, no quota consumed.
health = client.health()
print(health.status) # "ok"Rate limit info is included with every lookup response.
result = client.lookup("ABC123", "VIC")
print(result.rate_limit.limit) # monthly lookup allowance
print(result.rate_limit.remaining) # lookups left this period
print(result.rate_limit.plan) # current plan slug
# Warn before quota runs out
if result.rate_limit.remaining is not None and result.rate_limit.remaining < 50:
print(f"Warning: only {result.rate_limit.remaining} lookups remaining")from plateapi import (
PlateAPI,
PlateAPIError,
AuthenticationError,
RateLimitError,
QuotaExceededError,
ServerError,
)
client = PlateAPI("pk_live_your_api_key")
try:
result = client.lookup("ABC123", "VIC")
except AuthenticationError:
print("Invalid API key")
except QuotaExceededError:
print("Monthly quota exceeded -- upgrade at plateapi.com.au")
except RateLimitError as e:
print(f"Rate limited, retry after {e.retry_after}s")
except ServerError as e:
print(f"Server error ({e.status_code}), retry after {e.retry_after}s")
except PlateAPIError as e:
print(f"API error: {e} (status {e.status_code})")The SDK automatically retries on:
- Connection errors
- Timeouts
- 429 rate limit responses (waits for Retry-After header)
- 5xx server errors
Default: 3 retries with exponential backoff and jitter. Configure with:
client = PlateAPI(
"pk_live_your_api_key",
max_retries=5,
timeout=60,
)The SDK uses a requests.Session internally. Use a context manager to close it when done.
with PlateAPI("pk_live_your_api_key") as client:
result = client.lookup("ABC123", "VIC")
print(result.vehicle.make)Or close manually:
client = PlateAPI("pk_live_your_api_key")
try:
result = client.lookup("ABC123", "VIC")
finally:
client.close()Use plate TEST123 with any state for testing. Returns a fixed response instantly, no quota consumed.
result = client.lookup("TEST123", "VIC")
assert result.sandbox is True
assert result.success is True
print(result.vehicle.make) # "TOYOTA"The sandbox also supports detailed=True.
client = PlateAPI(
"pk_live_your_api_key",
base_url="https://api.plateapi.com.au", # default
timeout=30, # seconds, default 30
max_retries=3, # default 3
)