GeoNames API in Python#
by Michael T. Moen
GeoNames API documentation: https://www.geonames.org/export/web-services.html
GeoNames API license: https://www.geonames.org/export/
The GeoNames API is licensed under the Creative Commons’ CC 4.0 license, allowing users to share and adapt the API’s data for any purpose, as long as appropriate attribution is given.
These recipe examples were tested on March 4, 2024.
NOTE: The GeoNames API limits users to a maximum of 10000 credits per day and 1000 credits per hour. See here for a list of how many credits a request to each endpoint uses.
Setup#
User Registration#
Users must register with GeoNames before accessing the GeoNames API. Sign up can be found here. Import your username below:
# First add the username as a variable called username in a file username.py
from username import username
Import Libraries#
This tutorial uses the following libraries:
import requests
1. Searching with a ZIP code#
This example uses the postalCodeSearchJSON
endpoint to find the coordinates of the the ZIP code 35401.
endpoint = 'postalCodeSearchJSON'
parameters = '&'.join([
'postalcode=35401', # Postal code to search
'countryBias=US', # Moves US results to the top of the results list
f'username={username}' # Must include GeoNames username in all API calls
])
url = f'https://secure.geonames.org/{endpoint}?{parameters}'
response = requests.get(url)
# Status code 200 indicates success
response.status_code
200
top_result = response.json()['postalCodes'][0]
top_result
{'adminCode2': '125',
'adminCode1': 'AL',
'adminName2': 'Tuscaloosa',
'lng': -87.562666,
'countryCode': 'US',
'postalCode': '35401',
'adminName1': 'Alabama',
'ISO3166-2': 'AL',
'placeName': 'Tuscaloosa',
'lat': 33.196891}
latitude = top_result['lat']
longitude = top_result['lng']
latitude, longitude
(33.196891, -87.562666)
2. Searching with queries#
Queries allow users to search for location at several different levels.
Searching for a city#
In this example, we search for a location using the query “Tuscaloosa.”
endpoint = 'searchJSON'
parameters = '&'.join([
'q=Tuscaloosa', # Search query
'countryBias=US', # Moves US results to the top of the results list
'maxRows=10', # Limit results to top 10
f'username={username}' # Must include GeoNames username in all API calls
])
url = f'https://secure.geonames.org/{endpoint}?{parameters}'
response = requests.get(url)
# Status code 200 indicates success
response.status_code
200
# Display top result
response.json()['geonames'][0]
{'adminCode1': 'AL',
'lng': '-87.56917',
'geonameId': 4094455,
'toponymName': 'Tuscaloosa',
'countryId': '6252001',
'fcl': 'P',
'population': 98332,
'countryCode': 'US',
'name': 'Tuscaloosa',
'fclName': 'city, village,...',
'adminCodes1': {'ISO3166_2': 'AL'},
'countryName': 'United States',
'fcodeName': 'seat of a second-order administrative division',
'adminName1': 'Alabama',
'lat': '33.20984',
'fcode': 'PPLA2'}
Seaching for a buidling#
In this example, we search for a location using the query “Bruno Business Library.”
endpoint = 'searchJSON'
parameters = '&'.join([
'q=Bruno Business Library', # Search query
'countryBias=US', # Moves US results to the top of the results list
'maxRows=10', # Limit results to top 10
f'username={username}' # Must include GeoNames username in all API calls
])
url = f'https://secure.geonames.org/{endpoint}?{parameters}'
response = requests.get(url)
# Status code 200 indicates success
response.status_code
200
# Display top result
response.json()['geonames'][0]
{'adminCode1': 'AL',
'lng': '-87.54925',
'geonameId': 11524498,
'toponymName': 'Angelo Bruno Business Library',
'countryId': '6252001',
'fcl': 'S',
'population': 0,
'countryCode': 'US',
'name': 'Angelo Bruno Business Library',
'fclName': 'spot, building, farm',
'adminCodes1': {'ISO3166_2': 'AL'},
'countryName': 'United States',
'fcodeName': 'library',
'adminName1': 'Alabama',
'lat': '33.2111',
'fcode': 'LIBR'}
Searching for an island#
In this example, we use the query “Martha’s Vineyard.”
endpoint = 'searchJSON'
parameters = '&'.join([
"q=Martha's Vineyard", # Search query
'countryBias=US', # Moves US results to the top of the results list
'maxRows=10', # Limit results to top 10
f'username={username}' # Must include GeoNames username in all API calls
])
url = f'https://secure.geonames.org/{endpoint}?{parameters}'
response = requests.get(url)
# Status code 200 indicates success
response.status_code
200
# Display top result
response.json()['geonames'][0]
{'adminCode1': 'MA',
'lng': '-70.61265',
'geonameId': 4943237,
'toponymName': "Martha's Vineyard Airport",
'countryId': '6252001',
'fcl': 'S',
'population': 0,
'countryCode': 'US',
'name': "Martha's Vineyard Airport",
'fclName': 'spot, building, farm',
'adminCodes1': {'ISO3166_2': 'MA'},
'countryName': 'United States',
'fcodeName': 'airport',
'adminName1': 'Massachusetts',
'lat': '41.39016',
'fcode': 'AIRP'}
Note that the result above is the data for Matha’s Vineyard Airport. If we wish to find the data associated with the island, we can look at the fcodeName
of the locations in the response:
for location in response.json()['geonames']:
print(f'{location['toponymName']:<40}{location['fcodeName']}')
Martha's Vineyard Airport airport
Martha's Vineyard Island island
Vineyard Haven populated place
Martha's Vineyard Hospital hospital
Martha's Vineyard Regional High School school
Marthas Vineyard Campground camp(s)
Martha's Vineyard Aero Light
Martha's Vineyard State Forest forest(s)
Martha's Vineyard State Forest forest(s)
Martha's Vineyard Agricultural Society vineyard
3. Reverse Geocoding#
The findNearbyPostalCodesJSON
endpoint can be used to find the ZIP code of a pair of coordinates.
endpoint = 'findNearbyPostalCodesJSON'
parameters = '&'.join([
'lat=38.625189', # Search latitude
'lng=-90.187330', # Search longitude
'maxRows=10', # Limit results to top 10
f'username={username}' # Must include GeoNames username in all API calls
])
url = f'https://secure.geonames.org/{endpoint}?{parameters}'
response = requests.get(url)
# Status code 200 indicates success
response.status_code
200
# Print 10 nearest ZIP codes
print('ZIP | Distance (km)')
for zip in response.json()['postalCodes']:
print(f'{zip['postalCode']} | {zip['distance']}')
ZIP | Distance (km)
63102 | 0
63180 | 0.94603
63188 | 0.94603
63197 | 0.94603
63169 | 0.94603
63155 | 0.94603
63150 | 0.94603
63182 | 0.94603
63101 | 1.1038
62202 | 2.64737