Skip to content

Latest commit

 

History

6 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 

Repository files navigation

PlateAPI Python SDK

Python SDK for PlateAPI -- Australian vehicle registration plate lookup.

Install

pip install plateapi

Requires Python 3.8+.

Quick start

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)

Plate lookup

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.

Detailed lookup

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)

Multiple matches

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})")

Vehicle database

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="")

Check usage

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}")

Request logs

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")

Filtering

# 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)

Log entry fields

Each LogEntry contains: plate, state, success (1 or 0), error, duration_ms, make, model, year, client_ip, request_id, created_at.

Health check

Check API availability. No authentication required, no quota consumed.

health = client.health()
print(health.status)  # "ok"

Rate limits

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")

Error handling

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})")

Retry behaviour

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,
)

Context manager

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()

Sandbox

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.

Configuration

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
)

Links

About

Python SDK for PlateAPI -- Australian vehicle registration plate lookup API

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages