1. Set up Python and a secret API key
Use Python 3.9 or newer. The script imports only the standard library, so there is nothing to install and no virtual environment is required. In the dashboard, create a secret key, not a browser key.
Python does not read .env files on its own. Export the key in your terminal; this prompt
keeps it out of the command line and shell history:
read -rs OPENGEOCODING_API_KEY && export OPENGEOCODING_API_KEY Paste the complete key and press Enter. On a deployed server, set the same environment variable through your hosting platform's secret settings. Never commit the key, put it in browser JavaScript or send it to a client. See API key authentication.
2. Save and run the forward-geocoding script
Save this as geocode.py. It sends addressString as JSON to POST /v1/search, with the key in an x-api-key header rather than in the
URL. The request has a 15-second timeout and refuses redirects so credentials cannot be forwarded
to a different host.
import json
import os
import re
import sys
from urllib.error import HTTPError
from urllib.request import HTTPRedirectHandler, Request, build_opener
API_URL = os.environ.get('OPENGEOCODING_API_URL', 'https://api.opengeocoding.com') + '/v1/search'
api_key = os.environ.get('OPENGEOCODING_API_KEY', '').strip()
address = ' '.join(sys.argv[1:]).strip()
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, *args, **kwargs):
return None
def fail(message):
print(message, file=sys.stderr)
raise SystemExit(1)
if not re.fullmatch(r'og_[A-Za-z0-9_-]{32}', api_key):
fail('Set OPENGEOCODING_API_KEY to a complete secret key.')
if not address or len(address) > 500:
fail('Pass an address of 1-500 characters: python3 geocode.py "address"')
request = Request(
API_URL,
data=json.dumps({'addressString': address}).encode(),
headers={'x-api-key': api_key, 'Content-Type': 'application/json'},
method='POST',
)
try:
with build_opener(NoRedirect).open(request, timeout=15) as response:
result = json.load(response)
except HTTPError as error:
print(f'HTTP {error.code}. See https://opengeocoding.com/docs/errors', file=sys.stderr)
retry_after = error.headers.get('Retry-After')
if error.code == 429 and retry_after:
print(f'Wait {retry_after} seconds before retrying.', file=sys.stderr)
raise SystemExit(1)
except (OSError, ValueError):
fail('Network, timeout or invalid response. No automatic retry was made.')
point = result.get('geoCoordinates')
if point is None:
fail('No coordinates returned. Review the address before trying again.')
print(
json.dumps(
{
'displayName': result.get('displayName'),
'lat': point.get('lat', 0),
'lon': point.get('lon', 0),
'goodMatch': result.get('goodMatch', False),
},
indent=2,
ensure_ascii=False,
)
)
Run it from the directory containing the file, with the address in quotes:
python3 geocode.py "10 Rue de Rivoli, 75004 Paris" If the key or input is missing or malformed, the script stops before calling the API. The sample prints results to your terminal, not to a third-party logging service.
3. Read the coordinates and check the match
The API returns a structured address. This is an illustrative response from the OpenAPI specification, not a recorded live lookup:
{
"addressDetails": {
"city": "Paris",
"country": "France",
"countryCode": "fr",
"houseNumber": "10",
"postcode": "75004",
"road": "Rue de Rivoli"
},
"displayName": "Example address",
"geoCoordinates": {
"lat": 48.855,
"lon": 2.36
},
"goodMatch": true
} The script extracts geoCoordinates.lat and geoCoordinates.lon and
prints them as lat and lon, alongside displayName and goodMatch.
- Review partial matches.
goodMatch: falsemeans the result is not a confident full match. Ask for confirmation rather than treating it as a validated delivery address. A true value is not proof that an address is deliverable. - Handle missing coordinates. A successful reply may not contain a location. The script reports that case instead of inventing a point. A successful search still spends a credit when it finds nothing.
- Keep zero distinct from missing. Default scalar fields can be omitted: an
omitted
goodMatchmeans false, and an omitted latitude or longitude withingeoCoordinatesmeans zero. The script supplies those defaults without turning an absent coordinate object into a location.
Coverage and address detail vary by country and source. Review results for your own workload. You can store the results without a contractual deletion deadline, subject to the underlying data licences, including attribution and applicable share-alike requirements.
4. Handle HTTP errors and timeouts
A non-2xx reply raises HTTPError, which the script reports without printing
response bodies, request addresses or keys. Failed calls exit with status 1.
- 400 · Invalid request
- Check that addressString is not blank and is at most 500 characters. Use the documented field names.
- 401 · Authentication failed
- Check the complete key, its expiry and whether it was revoked. Stop retrying: repeated authentication failures can temporarily lock your IP.
- 403 · Key restrictions
- Use a secret key for Python. Browser keys require an allowed Origin header and are not the right key for this script.
- 429 · Rate limit or no credits
- If Retry-After is present, wait that many seconds. Without it, check your credit balance: retrying does not restore credits.
- 503 · Service unavailable
- Retry temporary service failures later, with bounded backoff. Do not retry authentication or input errors unchanged.
No automatic retries are made. If the response is lost or your timeout expires after the server completed the lookup, the call may already have spent a credit; sending it again can spend another. Errors returned by the API do not consume credits. Read the error reference and credit and rate-limit rules before adding retries or concurrency.
The Free plan allows up to 2 requests per second, shared across the account, not per key or Python process. This one-address script does not schedule a batch. For a CSV or a larger queue, use the bulk-geocoding guide, whose script paces requests, rather than looping over addresses or starting an unbounded thread pool.
Use it in a Python application
Keep this request on your server, validate the address at your application boundary and return only the result your client needs. Do not log API keys or address inputs. To reuse it in a web app or job, move the request into a function that raises on failure instead of exiting. For an address form with suggestions while typing, use autocomplete followed by retrieval instead of repeatedly running forward geocoding. For coordinates you already have, use reverse geocoding.