When working with location-based data in Python, you often need to convert city names into geographic coordinates - latitude and longitude.
The Real Use case
This article explores several Python solutions for geocoding city names, inspired by the need to convert the City into coordinates.
I was testing the SerpAPI for extracting Google maps results. I had a list of cities but the API expected geo coordinates like latitude and longitude:
Example of data needed - "ll": "@51.5030319,-0.3008878,11.31z",
:
from serpapi import GoogleSearch
params = {
"engine": "google_maps",
"q": "pizza"
"ll": "@51.5030319,-0.3008878,11.31z",
"type": "search",
...
1. Geopy
Installation
Install using pip with:
pip install geopy
Code
The most recommended solution is geopy
, a Python client for several popular geocoding web services:
from geopy.geocoders import Nominatim
geolocator = Nominatim(user_agent="linux")
location = geolocator.geocode("Chicago, IL")
print((location.latitude, location.longitude))
Output: (41.8781136, -87.6297982)
Errors
There are rate limit errors like:
ConfigurationError: Using Nominatim with default or sample
user_agent"geopy/2.4.1" is strongly discouraged, as it violates Nominatim's ToS
GeocoderInsufficientPrivileges: Non-successful status code 403
I found two solution for this error:
- use user agent like -
Nominatim(user_agent="linux")
- use different service - Photon
from geopy.geocoders import Photon
geolocator = Photon(user_agent="geoapiExercises")
location = geolocator.geocode("Chicago, IL")
print((location.latitude, location.longitude))
Output: (41.8781136, -87.6297982)
Notes
-
Pros:
- Supports multiple geocoding services (Nominatim, Photon, Google Maps, Bing, etc.)
- Simple, clean API
- Handles ambiguous queries well
-
Cons:
- May have usage limits
2. US Census Geocoder
For US-specific geocoding, the US Census Bureau provides a free API:
BASE_URL = 'https://geocoding.geo.census.gov/geocoder/'
return_type = 'locations'
search_type = 'address'
params = {
'street': '425 Stadium Dr',
'city': 'Tuscaloosa',
'state': 'AL',
'zip': 35401,
'benchmark': 'Public_AR_Current',
'format': 'json'
}
response = requests.get(f'{BASE_URL}{return_type}/{search_type}', params=params)
response.status_code
Output:
{'x': -87.549700416257, 'y': 33.21105403378}
Errors:
{"errors":["Street address cannot be empty and cannot exceed 100 characters","Specify House number and Street name along with City and State and/or ZIP Code"],"status":"400"}
if you face error like this it means that your request is incomplete. You can check the resources at the end of this post.
Notes
-
Pros:
- Free to use
- No API key required
- Official US government data
-
Cons:
- US only
- Less precise than commercial services
- Require additional input
Choosing the Right Approach
- For most projects: Use
geopy
with Nominatim (free) or Google Maps (more precise but may require API key) - For US-only projects: Consider the Census geocoder for simple needs
Complete Example with Error Handling
from geopy.geocoders import Nominatim
from geopy.exc import GeocoderTimedOut, GeocoderServiceError
def get_coordinates(city, state, country="US"):
geolocator = Nominatim(user_agent="city_locator")
try:
location = geolocator.geocode(f"{city}, {state}, {country}")
if location:
return (location.latitude, location.longitude)
return (None, None)
except (GeocoderTimedOut, GeocoderServiceError) as e:
print(f"Geocoding error: {e}")
return (None, None)
# Usage
lat, lon = get_coordinates("Chicago", "IL")
print(f"Latitude: {lat}, Longitude: {lon}")
Notes
Remember that free geocoding services often have rate limits, so for production applications consider:
- Implementing caching
- Using a paid service if you need high volume
- Respecting the terms of service for any API you use