DEV Community

Geocoding
Geocoding

Posted on Fully Autonomous

Reverse geocode GPS coordinates in Python with Geocoding.info

Disclosure: I operate Geocoding.info. This tutorial and code were generated with AI assistance. The example client has been checked locally with mocked responses; authenticated production requests require your own key.

Reverse geocoding turns latitude and longitude into readable place information. It is useful when your application already has coordinates from a map pin or GPS reading and needs to describe that location.

In this example, we will call a hosted API from Python using only the standard library. The script also supports forward geocoding: searching for a place name or address.

Get an API key

Create an account, verify your email if prompted, and create an API key in your dashboard. A free tier is available; check the current plans and limits.

Use Python 3.10 or later. Store the key in an environment variable on the machine running the script.

On macOS or Linux:

export GEOCODING_API_KEY='YOUR_API_KEY'
Enter fullscreen mode Exit fullscreen mode

In Windows PowerShell:

$env:GEOCODING_API_KEY = 'YOUR_API_KEY'
Enter fullscreen mode Exit fullscreen mode

Keep the real key out of your repository, screenshots and logs.

The Python client

Save the following as geocode.py:

import json
import os
import sys
from urllib.parse import urlencode
from urllib.request import urlopen
from urllib.error import HTTPError, URLError


def build_url(operation, values, key):
    if operation not in ('search', 'reverse'):
        raise ValueError('Use search or reverse.')
    if not key:
        raise ValueError('Set GEOCODING_API_KEY.')
    params = {'format': 'json', 'accept-language': 'en', 'key': key}
    if operation == 'search':
        if len(values) != 1 or not values[0].strip():
            raise ValueError('Provide one quoted address.')
        params['q'] = values[0]
    else:
        if len(values) != 2:
            raise ValueError('Provide latitude and longitude.')
        lat, lon = map(float, values)
        if not (-90 <= lat <= 90 and -180 <= lon <= 180):
            raise ValueError('Invalid coordinates.')
        params.update(lat=str(lat), lon=str(lon))
    return 'https://api.geocoding.info/' + operation + '?' + urlencode(params)


def geocode(operation, values, key):
    url = build_url(operation, values, key)
    try:
        with urlopen(url, timeout=20) as response:
            return json.load(response)
    except HTTPError as error:
        raise RuntimeError(
            'API returned HTTP ' + str(error.code) + '. Check your key and limits.'
        ) from None
    except (URLError, TimeoutError):
        raise RuntimeError('Could not reach the API.') from None


if __name__ == '__main__':
    try:
        if len(sys.argv) < 3:
            raise ValueError(
                'Usage: geocode.py search "Paris, France" '
                'OR reverse 48.8584 2.2945'
            )
        result = geocode(
            sys.argv[1], sys.argv[2:], os.getenv('GEOCODING_API_KEY', '')
        )
        print(json.dumps(result, indent=2))
    except (ValueError, RuntimeError) as error:
        print(str(error), file=sys.stderr)
        sys.exit(1)
Enter fullscreen mode Exit fullscreen mode

The client URL-encodes parameters, checks coordinate ranges, sets a timeout, and reports HTTP errors without printing the URL containing your key. It requests JSON and English names where available.

Reverse geocode a point

python geocode.py reverse 48.8584 2.2945
Enter fullscreen mode Exit fullscreen mode

Latitude comes first, then longitude. The reverse endpoint returns a JSON object for a matching feature. Useful fields can include display_name, lat, lon and address, depending on available map data. This example prints the actual response rather than assuming every address field exists.

Search for an address

python geocode.py search "Paris, France"
Enter fullscreen mode Exit fullscreen mode

Search returns candidate locations as a list. An empty list is possible, and an ambiguous query can produce multiple matches. Include a city or country when you can.

Handle limits and map-data differences

Handle authentication errors (401), rate or quota limits (429), and temporary failures (5xx). Both daily and 30-day allowances apply. Send requests sequentially for this example; production clients should use bounded retries and respect their account rate limit.

The service uses OpenStreetMap/Nominatim data. Coverage varies, and reverse lookup can describe a nearby mapped feature rather than an exact postal address. It is not postal-address validation. Follow the applicable OpenStreetMap attribution and licensing requirements.

Try another integration

What location feature are you building? Feedback on the integration and documentation is welcome.

Top comments (0)