All requests require X-Api-Key: <your-key> header — except the health check.
· Rate limit: 250 req/s sustained, 500 burst.
· Default precision: P4 (11 m cell).
Measured capacity — load-tested 2 Jul 2026
Observed throughput on this server (8 vCPU / 22 GB), driven with real coordinates across all four countries.
These are measured figures, not estimates. Real-world capacity depends mostly on the cache hit ratio.
Endpoint / mode
Throughput
Notes
/reverse — cache hit
~2,500 req/s
p50 6 ms
/reverse — fresh geocode
~450 req/s
p50 37 ms, p99 115 ms
Edge rate limit (all endpoints)
250 req/s
burst 500; excess → HTTP 503
/reverse/bulk — cached
~30,000 pts/s
max 5,000 points/request
/reverse/bulk — fresh
~13 pts/s
max 500 uncached/request
/reverse/jobs — enqueue
~3,000 pts/s
max 100,000 points/job
/reverse/jobs — processing
~16 pts/s
background drain (4 workers)
Endpoints
Method
Path
What it does
GET
/api/v1/health
Check API, database, and Nominatim status. No auth required.
POST
/api/v1/reverse
Reverse geocode one coordinate. Returns cached result instantly; calls Nominatim on a miss and caches the result for 180 days.
POST
/api/v1/reverse/bulk
Geocode up to 5,000 coordinates in one synchronous call. Best when most points are already cached. Returns 413 if >500 are uncached — submit an async job instead.
POST
/api/v1/reverse/jobs
Submit a large batch (up to 100,000 coords) as an async job. Returns a job_id immediately; workers geocode in background at ~20/s. Cached points are resolved at submission time.
GET
/api/v1/reverse/jobs/{id}
Poll job progress. Compare completed_points to total_points. Poll every 3–5 seconds.
GET
/api/v1/reverse/jobs/{id}/results
Download results with limit + offset pagination (max 1,000 per page). Can be called while the job is still running.
When to use what
Situation
Recommended endpoint
Live vehicle ping
POST /api/v1/reverse
Small batch (<5,000), mostly routes already seen
POST /api/v1/reverse/bulk
Any batch with many fresh / unseen coordinates
POST /api/v1/reverse/jobs then poll
Historical import (10k–100k coordinates)
POST /api/v1/reverse/jobs → poll → paginate results
{"points": [...], "client_reference": "batch-001"} reference is optional
Response fields & report locations
Print report_location on reports. Its default format is detailed:
road, neighbourhood, suburb, and city, skipping missing or repeated names.
For example: Sentema Road, Masanafu, Bukuluugi, Kampala.
detail_level describes the available address detail, not GPS accuracy.
Existing address, display_name, status, IDs, and cache keys retain their values.
New normalized_address always includes the same fields, with unavailable values set to
null. Its locality and locality_type identify the best available local area.
raw_address preserves the provider components.
Single and bulk requests accept "display_mode": "area" | "detailed" | "full".
For async jobs, choose the format when downloading:
/api/v1/reverse/jobs/{id}/results?display_mode=detailed.
Every result also includes lat, lon, precision, zoom,
source, display_mode, and schema_version (1.1).
These additions apply immediately to existing cache entries.
Interactive tester
JSON array. Optional "id" field per point for your own tracking.
Up to 100,000 points. The returned job_id auto-fills the status poller below.