How to Localize by IP with an IP Geolocation API
Set the right currency, language hint and timezone on the first request — before the user touches a setting — all from one lookup.
You can greet a visitor in their local currency, timezone and regional defaults on the very first request — no setting, no prompt — from a single IP lookup. The response already carries currency, timezone and country; this guide turns them into sensible defaults the user can always override.
The examples here are Python, standard library only — but the same logic maps directly onto our official SDKs for TypeScript, Go, Ruby, Java and 7 more. Field paths and the worked outputs below are all verified against the live API.
1Get a key and call the API securely
Create a free account here — 2,000 lookups/day, no card. Send the key as an Authorization: Bearer header, loaded from an environment variable, so it stays out of URLs and logs:
import os, urllib.request, json
API_KEY=*** # export it; never hardcode
def lookup(ip):
req = urllib.request.Request(
f"https://api.ipwho.org/ip/{ip}",
headers={"Authorization": f"Bearer {API_KEY}", "User-Agent": "MyApp/1.0"},
)
with urllib.request.urlopen(req, timeout=10) as resp:
return json.loads(resp.read())["data"]2Three fields set your regional defaults:
The response carries everything you need for localization: currency.code / currency.symbol, timezone.time_zone, and geoLocation.countryCode / is_in_eu. Read them once on arrival:
# Pull the localization fields — all in the standard response.
data = lookup("91.0.0.1")
geo = data.get("geoLocation") or {}
cur = data.get("currency") or {}
tz = data.get("timezone") or {}
defaults = {
"country": geo.get("countryCode"), # "DE"
"currency": cur.get("code"), # "EUR"
"symbol": cur.get("symbol"), # "€"
"timezone": tz.get("time_zone"), # "Europe/Berlin"
"in_eu": geo.get("is_in_eu"), # True
}
print(defaults)3Format prices in the local currency — as a default, not a lock:
Use the currency to pre-select the storefront's display currency. This is a default: always keep a manual currency switcher, because a visitor's IP country isn't always where they want to be billed.
# Format a price in the visitor's local currency. These are DISPLAY
# defaults — always let the user switch currency/locale manually.
def local_price(amount, data):
cur = data.get("currency") or {}
symbol = cur.get("symbol") or "$"
code = cur.get("code") or "USD"
return f"{symbol}{amount:,.2f} {code}"
print(local_price(49, lookup("91.0.0.1"))) # €49.00 EUR
print(local_price(49, lookup("76.102.11.203"))) # $49.00 USD4The full script — copy, paste, run:
Everything together. Export your key, pass an IP. Standard library only. Tested against the live API (2026-10-26).
#!/usr/bin/env python3
"""Localize defaults from an IP using the IPWho IP geolocation API."""
import os, sys, urllib.request, json
API_KEY=***
def lookup(ip):
req = urllib.request.Request(
f"https://api.ipwho.org/ip/{ip}",
headers={"Authorization": f"Bearer {API_KEY}", "User-Agent": "IPWho-L10n/1.0"},
)
with urllib.request.urlopen(req, timeout=10) as resp:
return json.loads(resp.read())["data"]
def localize(data):
geo = data.get("geoLocation") or {}
cur = data.get("currency") or {}
tz = data.get("timezone") or {}
return {
"country": geo.get("countryCode"),
"currency": cur.get("code") or "USD",
"symbol": cur.get("symbol") or "$",
"timezone": tz.get("time_zone"),
"in_eu": bool(geo.get("is_in_eu")),
}
if __name__ == "__main__":
ip = sys.argv[1] if len(sys.argv) > 1 else "91.0.0.1"
print(f"{ip}: {localize(lookup(ip))}")$ python3 localize.py 91.0.0.1 # Deutsche Telekom, Germany
91.0.0.1: {'country': 'DE', 'currency': 'EUR', 'symbol': '€', 'timezone': 'Europe/Berlin', 'in_eu': True}
$ python3 localize.py 76.102.11.203 # Comcast, USA
76.102.11.203: {'country': 'US', 'currency': 'USD', 'symbol': '$', 'timezone': 'America/Los_Angeles', 'in_eu': False}
$ python3 localize.py 133.0.0.1 # Japan
133.0.0.1: {'country': 'JP', 'currency': 'JPY', 'symbol': '¥', 'timezone': 'Asia/Tokyo', 'in_eu': False}