PythonGeolocationTutorial

Python IP Geolocation with requests — Country Detection and Currency Localisation

ApogeoAPI5 min read

Basic IP lookup with requests

import requests

API_KEY = "your_api_key"

def get_geo(ip: str = "me") -> dict:
    """Geolocate an IP address (or 'me' for the caller's IP)."""
    url = f"https://api.apogeoapi.com/v1/ip/{ip}"
    resp = requests.get(url, headers={"X-API-Key": API_KEY}, timeout=3)
    resp.raise_for_status()
    return resp.json()

# Get your own IP's geo data
geo = get_geo()
print(geo["country_code"])  # "DE"
print(geo["city"])           # "Berlin"
print(geo["timezone"])       # "Europe/Berlin"
print(geo["currency"])       # "EUR"

Subnet caching — stretch 1,000 free requests to ~250,000

Group IPs by their first three octets (the /24 subnet). An entire office or apartment building shares one subnet, so ~250 residents share one API call:

from functools import lru_cache
import time, requests

API_KEY = "your_api_key"
_cache: dict[str, tuple[dict, float]] = {}
TTL = 4 * 3600  # 4 hours

def _subnet(ip: str) -> str:
    """Return /24 prefix, e.g. '1.2.3.4' → '1.2.3'."""
    parts = ip.split(".")
    return ".".join(parts[:3]) if len(parts) == 4 else ip

def get_geo_cached(ip: str) -> dict:
    key = _subnet(ip)
    cached = _cache.get(key)
    if cached and time.time() - cached[1] < TTL:
        return cached[0]
    url = f"https://api.apogeoapi.com/v1/ip/{ip}"
    resp = requests.get(url, headers={"X-API-Key": API_KEY}, timeout=3)
    resp.raise_for_status()
    data = resp.json()
    _cache[key] = (data, time.time())
    return data

Async version with httpx

import httpx

async def get_geo_async(ip: str = "me") -> dict:
    async with httpx.AsyncClient() as client:
        resp = await client.get(
            f"https://api.apogeoapi.com/v1/ip/{ip}",
            headers={"X-API-Key": API_KEY},
            timeout=3,
        )
        resp.raise_for_status()
        return resp.json()

# FastAPI example
from fastapi import FastAPI, Request
app = FastAPI()

@app.get("/price")
async def localized_price(request: Request):
    client_ip = request.client.host
    geo = await get_geo_async(client_ip)
    currency = geo.get("currency", "USD")
    usd_price = 29.00
    # Fetch exchange rate separately
    rate_resp = await httpx.AsyncClient().get(
        f"https://api.apogeoapi.com/v1/exchange-rates/USD",
        headers={"X-API-Key": API_KEY},
    )
    rates = rate_resp.json().get("rates", {})
    local_price = round(usd_price * rates.get(currency, 1.0), 2)
    return {"currency": currency, "price": local_price}

Combining IP geo with exchange rates

import requests

API_KEY = "your_api_key"

def get_localized_price(usd_price: float, visitor_ip: str = "me") -> dict:
    geo = get_geo_cached(visitor_ip)
    currency = geo.get("currency", "USD")
    if currency == "USD":
        return {"currency": "USD", "price": usd_price, "country": geo.get("country_code")}
    # Get exchange rates (cache this separately in production)
    resp = requests.get(
        "https://api.apogeoapi.com/v1/exchange-rates/USD",
        headers={"X-API-Key": API_KEY},
        timeout=3,
    )
    rates = resp.json().get("rates", {})
    rate = rates.get(currency, 1.0)
    return {
        "currency": currency,
        "price": round(usd_price * rate, 2),
        "country": geo.get("country_code"),
        "country_name": geo.get("country_name"),
    }

# Usage:
print(get_localized_price(29.00, "8.8.8.8"))
# {'currency': 'USD', 'price': 29.0, 'country': 'US', 'country_name': 'United States'}

Error handling and fallback

DEFAULT_GEO = {"country_code": "US", "country_name": "United States", "currency": "USD"}

def safe_get_geo(ip: str) -> dict:
    try:
        return get_geo_cached(ip)
    except (requests.Timeout, requests.HTTPError, ValueError):
        return DEFAULT_GEO

Environment variable setup

# .env
# APOGEOAPI_KEY=your_api_key

import os
from dotenv import load_dotenv

load_dotenv()
API_KEY = os.getenv("APOGEOAPI_KEY", "")

Try ApogeoAPI free

1,000 requests/month forever. 14-day full-access trial. No credit card.

Get your free API key