
40.7128, -74.0060. Your dispatcher, customer, or report needs "New York, NY." Reverse geocoding closes that gap by turning latitude and longitude into a readable address or place description. It powers delivery notifications, fleet event labels, field-service job logs, and location-data enrichment.
The returned address is not determined by coordinates alone. Coordinate order, input precision, provider coverage, nearby mapped features, and query parameters all shape the result.
This guide walks through a provider-neutral Python workflow: API setup, a requests call, response parsing, validation, rate-limit handling, common mistakes, and alternatives.
Key Takeaways
- Reverse geocoding turns latitude and longitude into an address, ZIP code, city, or nearby place.
- Reliable calls need valid decimal coordinates, correct lat/lon order, and secure credentials.
requestsis enough to start; production needs timeouts, retries, caching, and rate-limit controls.- Free tiers suit testing; production choices hinge on coverage, storage rights, privacy, and cost.
How to Reverse Geocode Coordinates in Python Using an API
Step 1: Choose an API and prepare the environment
Compare providers on current documentation: US coverage, address precision, response format, rate limits, authentication, batch support, privacy, and storage terms. Verify pricing and limits yourself, because they change.
Endpoints and coordinate inputs differ by provider:
| Provider | Coordinate input |
|---|---|
latlng=latitude,longitude |
|
| HERE | at=latitude,longitude |
| Mapbox | separate longitude and latitude |
| Nominatim | lat, lon |
| NextBillion.ai | at=latitude,longitude |
Install the client and keep the key out of source code:
pip install requests
export GEOCODE_API_KEY="your_key_here"
Per the Twelve-Factor guidance, credentials for external services belong in configuration, not code. A dedicated secret manager is safer still, since environment variables can leak into logs and system dumps.
Step 2: Validate the coordinates and build the request
Validate before you spend a request. Latitude runs from -90 to 90, longitude from -180 to 180. Keep the lat/lon order explicit in the signature.
import os
import requests
API_URL = "https://api.nextbillion.io/revgeocode" # swap for your provider
def reverse_geocode(lat: float, lon: float, api_key: str, lang: str = "en"):
if not -90 <= lat <= 90:
raise ValueError(f"Latitude out of range: {lat}")
if not -180 <= lon <= 180:
raise ValueError(f"Longitude out of range: {lon}")
params = {"at": f"{lat},{lon}", "key": api_key, "lang": lang}
resp = requests.get(API_URL, params=params, timeout=10)
resp.raise_for_status()
return resp.json()

Pass everything through the params dictionary so requests handles URL encoding. Optional parameters such as country filters, language, and result type go in the same place. Parameter names vary by provider, so check the documentation.
Step 3: Send the request and parse the response
Always set a timeout. Requests has no default timeout, so a stalled connection can hang your worker indefinitely. Call raise_for_status(), then decode JSON.
Response shapes differ:
- Google:
results[].formatted_addressandaddress_components, plus a top-levelstatus - HERE and NextBillion.ai:
items[].address.labelwith adistancefield - Mapbox: GeoJSON
features[].properties.full_address - Nominatim:
display_name, but only withformat=jsonv2; the default is XML
Inspect a raw response from a known point before writing extraction logic:
def parse_address(data: dict) -> dict:
items = data.get("items", [])
if not items:
return {"status": "no_match", "raw": data}
addr = items[0].get("address", {})
return {
"status": "ok",
"label": addr.get("label"),
"street": addr.get("street"),
"city": addr.get("city"),
"state": addr.get("state"),
"postal_code": addr.get("postalCode"),
"distance_m": items[0].get("distance"),
"raw": data,
}
Keep the raw payload for auditing and debugging where your storage terms allow it.
Step 4: Make the function production-ready
A blank string tells you nothing. Distinguish the failure types:
- Invalid input: raise before the request
- 401/403: bad, expired, or restricted credentials
- Empty results: a valid lookup with no match
- 429: rate limit; honor
Retry-Afterif present - 500/502/503/504: transient server trouble, safe to retry
- Malformed JSON: catch
JSONDecodeErrorand log the status code
import time, random
def call_with_retry(lat, lon, key, max_tries=5):
for attempt in range(max_tries):
try:
return reverse_geocode(lat, lon, key)
except requests.HTTPError as e:
code = e.response.status_code
if code in (401, 403):
raise
if code == 429 or code >= 500:
wait = min(30, 2 ** attempt) + random.random()
time.sleep(wait)
continue
raise
except requests.Timeout:
time.sleep(2 ** attempt)
return {"status": "failed"}
For datasets, deduplicate and cache repeated coordinates first. Then use bounded concurrency, or the provider's batch endpoint where one exists. Capped exponential backoff with jitter keeps synchronized retries from spiking the service.
When to Use Reverse Geocoding and What You Need Beforehand
Use it when you have coordinates but need a label for users, reporting, dispatch, or enrichment. Skip it when you only run spatial calculations such as distance or geofence checks. Those work on raw points.
Best-fit use cases and limitations
Good fits:
- Turning delivery or service-stop GPS points into customer-facing addresses
- Labeling fleet events such as harsh braking or long idling
- Grouping records by city or ZIP code
- Enriching a location dataset
The catch: a point may land on a road, parcel, building, rural stretch, or lake. The API returns the nearest mapped feature, not a guaranteed rooftop address. Nominatim's documentation says it returns the closest suitable object, which can sit on a different street.
Python and API requirements
- A currently supported Python version (3.11 to 3.13 receive security fixes; current Requests needs 3.10+)
- The
requestslibrary and network access - An API key, if the provider requires one
- Current provider documentation
Test with known coordinates, such as a landmark you can verify, and read the raw response before coding field extraction.
Input, security, and compliance readiness
- Use decimal degrees in WGS84, and decide how to handle missing or stale GPS values
- Decide whether you need a full address or just city, state, ZIP, or country
- Keep keys out of source code and logs
- Review privacy and retention terms for location data
Storage rights matter. Google generally restricts caching. Mapbox separates Temporary results (no caching) from Permanent ones. Esri requires forStorage=true to store results. Free-request allowances do not grant permission to keep the addresses.

Privacy law applies too. California's CCPA treats precise geolocation as sensitive personal information, depending on whether your business meets the law's criteria.
Key Parameters That Affect Reverse Geocoding Results
The API returns the provider's best mapped match for a point. Your parameters and the underlying map data both influence accuracy, granularity, latency, and cost.
Coordinate order, precision, and quality
Swapped latitude and longitude is the classic bug. GeoJSON uses longitude first, while Google's latlng and HERE's at use latitude first. Keep provider-specific adapters. A swap can put a Chicago truck in the Indian Ocean, or return an empty result.
Other quality threats:
- Out-of-range values
- Aggressive rounding
- GPS noise and stale fixes
Smartphones are typically accurate to 4.9 m under open sky, and buildings, bridges, and trees make it worse. That is a positioning figure, not address accuracy.
On precision, RFC 7946 notes that six decimal places is about 10 cm. Keep enough decimals for your use case, but more digits do not make an address more accurate.
When the response includes a distance field, compare it to your original point. Reject or flag matches that sit too far away for your use.
Provider coverage and result granularity
Map completeness varies by country, rural area, road type, and provider. A city-level result may be the best available answer for some points.
Check match type fields, such as Esri's Addr_type, Google's types and location_type, or HERE's result type, to see what was actually matched. Do not treat administrative output as a street address.
Filters, language, and regional parameters
Useful controls include:
- Google:
language,region,result_type,location_type - Mapbox:
country(ISO alpha-2),language,types - HERE:
lang, plusbearingto prefer the street matching a vehicle's heading - Nominatim:
accept-language,zoom(0-18; 18 is building level)
Restrictive filters can remove valid matches when the GPS fix is noisy or near a border. Test filters on representative data first.
Rate limits, latency, retries, and caching
Quotas vary widely. Google lists 3,000 queries per minute, Mapbox defaults to 1,000 per minute, and public Nominatim allows at most 1 request per second. Public Nominatim also says that package and vehicle tracking applications must run their own service.

Practical controls:
- Set timeouts and bounded retries with backoff
- Build cache keys from normalized coordinates plus parameters, where terms permit caching
- Send records that fail repeatedly to a dead-letter queue for review
Common Mistakes, Troubleshooting, and Alternatives
Frequent mistakes:
- Skipping validation or swapping lat/lon
- Hardcoding API keys
- Logging sensitive location data
- Assuming every response has a street address
- Ignoring attribution or usage terms
A 200 response does not prove a correct address. Check for empty result arrays, match type, returned coordinates, distance or confidence fields where available, and the specific components your app needs.
Google's ZERO_RESULTS status, for example, is a successful lookup with nothing found.
Troubleshooting an empty or incomplete address
Likely causes: invalid coordinates, an unmapped area, a restrictive filter, administrative-only output, or an unexpected schema.
- Check coordinate order and ranges
- Inspect the raw payload and match type
- Relax nonessential filters
- Test another known point
- Store a clear "no match" status instead of inventing an address
Troubleshooting authentication, quota, and server errors
- 401: missing or invalid credentials
- 403: the server understood the request but refuses it; check key restrictions, billing, and account setup
- 429: reduce concurrency and honor
Retry-After - 5xx: retry with exponential backoff
Rotate bad credentials securely. For persistent failures, escalate with request IDs and logs that exclude secrets.
Troubleshooting an address that appears inaccurate
The API may return the nearest road, parcel, locality, or point of interest rather than the physical feature at your coordinates. Check match type, distance, coordinate freshness, and map coverage. Compare against another provider before using the result for dispatch or compliance-sensitive work.

Beyond fixing bad responses, you may need a different reverse-geocoding approach entirely.
Offline or locally hosted reverse geocoding
Choose local reverse geocoding for privacy-sensitive processing, disconnected environments, predictable costs, or very large repeated datasets.
Options include the offline reverse_geocoder library (nearest cities only, not street addresses) and self-hosted Nominatim or Pelias. Trade-offs are freshness, address detail, and maintenance, since you handle imports and updates yourself.
Hosted provider SDK or Python library
Wrappers such as googlemaps and geopy simplify authentication and parsing. They also add provider coupling, can lag behind API changes, and may hide request details.
geopy is a client for third-party services, not an offline database, so provider quotas still apply. Direct requests calls give you full control with a bit more code.
Enterprise location platform for broader operational workflows
If reverse geocoding is one piece of a larger routing, fleet, or field-service stack, consider NextBillion.ai. Its documented /revgeocode endpoint takes at=latitude,longitude and a key, and returns structured address, position, access, and distance fields. Documented continuous-request limits are 2,400 queries per minute or 40 per second.
NextBillion.ai also offers routing, optimization, and tracking APIs, with fixed-fee pricing options and deployment on customer infrastructure. Before committing, verify coverage, pricing model, batch support, storage terms, and deployment options for reverse geocoding specifically.
Conclusion
Reverse geocoding in Python is straightforward once the API, coordinates, parameters, and response schema line up. A dozen lines of requests code will get you an address.
Reliable production results take more:
- Input and response validation
- Secure key management
- Provider-aware parsing
- Rate-limit handling
- Review of approximate or incomplete matches
Pick the API that balances address quality, coverage, performance, privacy, scale, and cost for your workflow. Test it on your own coordinates first.
Frequently Asked Questions
Is there a free API for reverse geocoding?
Yes. Google lists 10,000 free monthly events and Mapbox Temporary lists 100,000 free monthly requests. Public Nominatim is free but restricted. Check limits, attribution, storage, and commercial-use terms before production.
What is the difference between geocoding and reverse geocoding?
Geocoding converts an address or place description into coordinates. Reverse geocoding converts coordinates into an address or location description.
How do I reverse geocode coordinates in Python?
Send a requests.get call to the provider's reverse endpoint with latitude, longitude, and your key, plus a timeout. Then call raise_for_status(), parse the JSON, and handle errors.
How can I reverse geocode many coordinates without exceeding rate limits?
Deduplicate coordinates, cache results where terms allow, and use batch endpoints or bounded concurrency. Add exponential backoff with jitter, and stay within the provider's documented limits.
What should I do if a reverse geocoding API returns no address?
Check coordinate order and ranges, then inspect the raw response, match type, filters, and coverage. If nothing turns up, store the record as unmatched or low-confidence.


