Go client for PlateAPI -- Australian vehicle registration plate lookup.
go get github.com/PlateAPI/plateapi-goRequires Go 1.21+. Zero external dependencies -- stdlib only.
package main
import (
"context"
"fmt"
plateapi "github.com/PlateAPI/plateapi-go"
)
func main() {
client := plateapi.NewClient("pk_live_your_api_key")
result, err := client.Lookup(context.Background(), "ABC123", "VIC", false)
if err != nil {
panic(err)
}
if result.Success && result.Vehicle != nil {
fmt.Printf("%s %s\n", *result.Vehicle.Make, *result.Vehicle.Model)
}
}result, err := client.Lookup(ctx, "ABC123", "VIC", false)
fmt.Println(result.Success) // true
fmt.Println(*result.Vehicle.Make) // "TOYOTA"
fmt.Println(*result.Vehicle.Model) // "HILUX"
fmt.Println(*result.Vehicle.LowestYear) // 2015
fmt.Println(*result.Vehicle.HighestYear) // 2023
fmt.Println(*result.Vehicle.YearRange) // "2015 - 2023"
fmt.Println(*result.Vehicle.Body) // "UTILITY"
fmt.Println(*result.Vehicle.Engine) // "2.8L"
fmt.Println(*result.Vehicle.Description) // "TOYOTA HILUX UTILITY 2.8L"
fmt.Println(*result.DurationMs) // 2451.3
fmt.Println(*result.Source) // "plateapi"
fmt.Println(*result.RequestID) // "req_7f3a9c1b4e..."Valid states: NSW, VIC, QLD, SA, WA, TAS, NT, ACT.
result, err := client.Lookup(ctx, "ABC123", "NSW", true)
if result.Success {
fmt.Println(*result.Vehicle.DetailedDescription)
fmt.Println(*result.Vehicle.Series)
}result, _ := client.Lookup(ctx, "ABC123", "VIC", false)
for _, alt := range result.Alternatives {
fmt.Printf("Also matched: %s %s (%s)\n",
*alt.Make, *alt.Model, *alt.YearRange)
}Browse the full vehicle database (32,000+ vehicles, 213 makes). Each call narrows the cascade through all 7 levels. Paid plans only, no quota consumed.
// Step 1: All makes
makes, _ := client.Vehicles(ctx, nil)
// makes.Type == "make", makes.Data == ["ABARTH", "AC", ...]
// Step 2: Models for a make
models, _ := client.Vehicles(ctx, &plateapi.VehiclesOptions{
Make: plateapi.String("TOYOTA"),
})
// Step 3: Years
years, _ := client.Vehicles(ctx, &plateapi.VehiclesOptions{
Make: plateapi.String("TOYOTA"),
Model: plateapi.String("HILUX"),
})
// Step 4: Series
series, _ := client.Vehicles(ctx, &plateapi.VehiclesOptions{
Make: plateapi.String("TOYOTA"),
Model: plateapi.String("HILUX"),
Year: plateapi.Int(2020),
})
// Step 5: Engines
engines, _ := client.Vehicles(ctx, &plateapi.VehiclesOptions{
Make: plateapi.String("TOYOTA"),
Model: plateapi.String("HILUX"),
Year: plateapi.Int(2020),
Series: plateapi.String("SR5"),
})
// Step 6: Variants
variants, _ := client.Vehicles(ctx, &plateapi.VehiclesOptions{
Make: plateapi.String("TOYOTA"),
Model: plateapi.String("HILUX"),
Year: plateapi.Int(2020),
Series: plateapi.String("SR5"),
Engine: plateapi.String("2.8L"),
})
// Step 7: Full vehicle details
vehicles, _ := client.Vehicles(ctx, &plateapi.VehiclesOptions{
Make: plateapi.String("TOYOTA"),
Model: plateapi.String("HILUX"),
Year: plateapi.Int(2020),
Series: plateapi.String("SR5"),
Engine: plateapi.String("2.8L"),
Variant: plateapi.String("4x4 Double Cab"),
})For vehicles without a series code, pass an empty string:
result, _ := client.Vehicles(ctx, &plateapi.VehiclesOptions{
Make: plateapi.String("TOYOTA"),
Model: plateapi.String("HILUX"),
Year: plateapi.Int(2020),
Series: plateapi.String(""),
})usage, _ := client.Usage(ctx)
fmt.Printf("%d/%d lookups used\n", usage.UsedThisMonth, usage.MonthlyLimit)
fmt.Printf("%d remaining\n", usage.Remaining)
fmt.Printf("%.1f%% used\n", *usage.PercentUsed)
fmt.Printf("Plan: %s\n", *usage.Plan)
fmt.Printf("Rate limit: %d/min\n", usage.RateLimitPerMin)
fmt.Printf("Period: %s to %s\n", *usage.PeriodStart, *usage.PeriodEnd)
fmt.Printf("Days remaining: %d\n", *usage.DaysRemaining)
fmt.Printf("Top-up credits: %d\n", usage.TopupCredits)// Last 10 lookups
logs, _ := client.Logs(ctx, &plateapi.LogsOptions{Limit: 10})
for _, entry := range logs.Logs {
fmt.Printf("%s | %s (%s) | %v | %.0fms\n",
*entry.CreatedAt, *entry.Plate, *entry.State,
entry.Success != 0, *entry.DurationMs)
}
fmt.Printf("Showing %d of %d total\n", logs.Count, logs.Total)// Filter by plate
plateLogs, _ := client.Logs(ctx, &plateapi.LogsOptions{Plate: plateapi.String("ABC123")})
// Only failed lookups
failed, _ := client.Logs(ctx, &plateapi.LogsOptions{Success: plateapi.Bool(false)})
// Time range
july, _ := client.Logs(ctx, &plateapi.LogsOptions{
Since: plateapi.String("2026-07-01T00:00:00"),
Until: plateapi.String("2026-07-31T23:59:59"),
})
// Pagination
page1, _ := client.Logs(ctx, &plateapi.LogsOptions{Limit: 50, Offset: 0})
page2, _ := client.Logs(ctx, &plateapi.LogsOptions{Limit: 50, Offset: 50})No authentication required, no quota consumed.
health, _ := client.Health(ctx)
fmt.Println(health.Status) // "ok"result, _ := client.Lookup(ctx, "ABC123", "VIC", false)
if result.RateLimit.Remaining != nil {
fmt.Printf("Lookups remaining: %d\n", *result.RateLimit.Remaining)
}import "errors"
result, err := client.Lookup(ctx, "ABC123", "VIC", false)
if err != nil {
var authErr *plateapi.AuthenticationError
var quotaErr *plateapi.QuotaExceededError
var rateErr *plateapi.RateLimitError
var serverErr *plateapi.ServerError
var apiErr *plateapi.APIError
switch {
case errors.As(err, &authErr):
fmt.Println("Invalid API key")
case errors.As(err, "aErr):
fmt.Println("Monthly quota exceeded")
case errors.As(err, &rateErr):
fmt.Print("Rate limited")
if rateErr.RetryAfter != nil {
fmt.Printf(", retry after %.0fs", *rateErr.RetryAfter)
}
fmt.Println()
case errors.As(err, &serverErr):
fmt.Printf("Server error (%d)\n", serverErr.StatusCode)
case errors.As(err, &apiErr):
fmt.Printf("API error: %s (status %d)\n", apiErr.Message, apiErr.StatusCode)
default:
fmt.Printf("Error: %v\n", err)
}
}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.NewClient("pk_live_your_api_key",
plateapi.WithMaxRetries(5),
plateapi.WithTimeout(60 * time.Second),
)All methods accept context.Context for cancellation and timeouts:
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
result, err := client.Lookup(ctx, "ABC123", "VIC", false)client := plateapi.NewClient("pk_live_your_api_key",
plateapi.WithHTTPClient(&http.Client{
Transport: &http.Transport{
MaxIdleConns: 10,
},
}),
)Use plate TEST123 with any state for testing. Returns a fixed response instantly, no quota consumed.
result, _ := client.Lookup(ctx, "TEST123", "VIC", false)
// result.Sandbox == true
// result.Success == true
// result.Vehicle.Make == "TOYOTA"The SDK provides pointer helpers for building options:
plateapi.String("TOYOTA") // *string
plateapi.Int(2020) // *int
plateapi.Bool(true) // *bool