Python SDK
Geocoding with blocking and asyncio clients. Install, authenticate, and make your first call.
The Python SDK includes typed request dictionaries, dataclass results, and optional Google/HERE fallbacks. Both clients use HTTPX.
Installation
Requires Python 3.11+. The package is not yet published; install from the repository root:
python -m venv .venv
source .venv/bin/activate
python -m pip install ./sdk/python
The distribution is affordable-geo-apis. Import it as affordable_geo_apis.
Quick start
Choose a blocking or async client:
- Blocking
- Async
import os
from affordable_geo_apis import GeocodingClient
with GeocodingClient(
base_url=os.environ['GEOCODING_BASE_URL'],
api_key=os.environ['GEOCODING_API_KEY'],
) as client:
response = client.geocode({
'street': '11 Wall Street',
'city': 'New York',
'state': 'NY',
'zip': '10005',
})
print(response.to_dict())
import asyncio
import os
from affordable_geo_apis import AsyncGeocodingClient
async def main():
async with AsyncGeocodingClient(
base_url=os.environ['GEOCODING_BASE_URL'],
api_key=os.environ['GEOCODING_API_KEY'],
) as client:
response = await client.geocode({
'street': '11 Wall Street',
'city': 'New York',
'state': 'NY',
'zip': '10005',
})
print(response.to_dict())
asyncio.run(main())
The context manager closes the client. A successful no-match request returns an empty results list.
Authentication
Set your deployment URL and API key:
export GEOCODING_BASE_URL='http://localhost:8000'
export GEOCODING_API_KEY='YOUR_API_KEY'
Pass them as base_url and api_key. The SDK sends the apikey header. Use your assigned HTTPS URL in production, without the endpoint path.
Available methods
| Method | Use |
|---|---|
client.geocode(request, timeout=None) | Find coordinates for an address |
client.search(request, timeout=None) | Find addresses and places from one line of free text |
client.reverse_geocode(request, timeout=None) | Find address candidates for coordinates |
client.autocomplete(request, timeout=None) | Suggest addresses for partial text and a required country |
client.resolve_autocomplete(suggestion, timeout=None) | Resolve the selected suggestion into one address |
client.close() | Close the blocking client |
await client.aclose() | Close the async client |
Geocode, search, and reverse geocode return GeocodingResponse. Autocomplete returns AutocompleteResponse; resolution returns GeocodingResult. Await all five operations with the async client. See the client reference for all types.
Common operations
These snippets run inside an open blocking client. Await calls when using AsyncGeocodingClient.
Free-text search
response = client.search({
'q': '11 Wall Street, New York, NY',
'country': 'US',
'limit': 5,
})
print(response.results)
Search accepts one line of text, such as an address, place name, city, or ZIP code, and returns matches of any type. q is required (1 to 256 characters). country restricts results, near ({'lat': ..., 'lon': ...}) prefers nearby results, and limit is 1 to 40 (default 10). Use geocode when the address is already split into fields. See search.
Address autocomplete
response = client.autocomplete({'q': '11 Wall', 'country': 'US', 'limit': 5})
if response.suggestions:
address = client.resolve_autocomplete(response.suggestions[0])
print(address.label, address.lat, address.lon)
Keep the suggestion object unchanged until selection. Resolution calls only its provider and does not run fallback selection or use fallback budgets/circuits. See autocomplete and blocking/async examples.
Reverse geocoding
response = client.reverse_geocode({
'lat': 40.7074,
'lon': -74.0113,
})
print(response.results)
Reading results
for result in response.results:
print(result.label, result.lat, result.lon)
Use response.to_dict() for nested JSON serialization. Result country values are normalized to ISO alpha-2 codes.
Configuration
Provider fallbacks
from affordable_geo_apis import FallbackBudget, FallbackOptions, GeocodingClient
with GeocodingClient(
base_url=os.environ['GEOCODING_BASE_URL'],
api_key=os.environ['GEOCODING_API_KEY'],
fallback_budget=FallbackBudget(max_requests=1000),
fallbacks=[
FallbackOptions(provider='google', api_key=os.environ['GOOGLE_MAPS_API_KEY']),
FallbackOptions(provider='here', api_key=os.environ['HERE_API_KEY']),
],
) as client:
response = client.geocode({
'street': '10 Downing Street', 'city': 'London', 'country': 'GB',
})
GB routes directly to backups. For primary countries, backups run on no results, low confidence, or eligible outages. They are off by default. Both clients accept the same options; see fallback policy.
Error handling
from affordable_geo_apis import GeocodingError
try:
response = client.geocode({'street': '11 Wall Street', 'city': 'New York'})
except GeocodingError as error:
print(error.code, error.status, error.message)
print(error.to_dict())
Primary HTTP 4xx and malformed responses stop the chain. Terminal errors include the attempt history.
Timeouts and cancellation
Constructor timeout_ms defaults to 10000 milliseconds per provider. Per-call timeout sets a total deadline in seconds:
response = client.geocode(
{'street': '11 Wall Street', 'city': 'New York'},
timeout=15,
)
Total deadline expiry raises TimeoutError. A provider timeout raises GeocodingError(code='timeout') and can trigger fallback. Cancel async calls with task.cancel() or an enclosing asyncio.timeout(...).
Retries
The SDK does not retry automatically. Bound retries in your application and use backoff. Each repeated call can consume primary and backup usage.
Raw provider response
if response.provider != 'primary':
print(response.provider_response)
The dictionary contains the full selected Google/HERE body. Primary responses have provider_response=None, omitted by to_dict().
Reuse clients for pooling, budgets, and circuits. Use the async client on one event loop; the blocking client can serve concurrent threads. Close clients at shutdown or use context managers.
For partial address entry, see address autocomplete. Both blocking and async clients support it.