Guide

How to Route Users to the Nearest Region by IP

Pick the closest backend region from one lookup — using the latitude/longitude in the response — for lower latency.

You can send a user to your closest backend region — for lower latency — from a single IP lookup. The response includes latitude and longitude; this guide computes the nearest region with a simple great-circle distance, with a country fallback for IPs that return no coordinates.

Python

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 from an environment variable, keeping it out of URLs and logs:

Python
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"]

2Compute the nearest region from latitude/longitude:

The response carries geoLocation.latitude and geoLocation.longitude. Define your regions once, then pick the closest with a great-circle (haversine) distance. Always keep a default for IPs that return no coordinates:

Python
import math

REGIONS = {
    "eu-west (Frankfurt)":  (50.11, 8.68),
    "us-east (Virginia)":   (38.0, -78.5),
    "us-west (Oregon)":     (45.8, -119.7),
    "ap-northeast (Tokyo)": (35.69, 139.69),
}

def haversine(a, b):
    R = 6371  # km
    la1, lo1, la2, lo2 = map(math.radians, [a[0], a[1], b[0], b[1]])
    h = math.sin((la2-la1)/2)**2 + math.cos(la1)*math.cos(la2)*math.sin((lo2-lo1)/2)**2
    return 2 * R * math.asin(math.sqrt(h))

def nearest_region(data):
    geo = data.get("geoLocation") or {}
    lat, lon = geo.get("latitude"), geo.get("longitude")
    if lat is None or lon is None:
        return {"region": "default", "reason": "no coordinates"}
    pt = (lat, lon)
    region = min(REGIONS, key=lambda r: haversine(pt, REGIONS[r]))
    return {"country": geo.get("countryCode"), "region": region,
            "km": round(haversine(pt, REGIONS[region]))}

print(nearest_region(lookup("91.0.0.1")))

3Route the request — the full script:

Everything together. Swap the REGIONS map for your real backend coordinates. Note that coordinates are approximate (city-level or coarser) — this is ideal for region selection, not for anything needing pinpoint accuracy:

Python
#!/usr/bin/env python3
"""Route to the nearest backend region by IP (IPWho IP geolocation API)."""
import os, sys, math, urllib.request, json

API_KEY=*** REGIONS = {
    "eu-west (Frankfurt)":  (50.11, 8.68),
    "us-east (Virginia)":   (38.0, -78.5),
    "us-west (Oregon)":     (45.8, -119.7),
    "ap-northeast (Tokyo)": (35.69, 139.69),
}

def lookup(ip):
    req = urllib.request.Request(
        f"https://api.ipwho.org/ip/{ip}",
        headers={"Authorization": f"Bearer {API_KEY}", "User-Agent": "IPWho-Routing/1.0"},
    )
    with urllib.request.urlopen(req, timeout=10) as resp:
        return json.loads(resp.read())["data"]

def haversine(a, b):
    R = 6371
    la1, lo1, la2, lo2 = map(math.radians, [a[0], a[1], b[0], b[1]])
    h = math.sin((la2-la1)/2)**2 + math.cos(la1)*math.cos(la2)*math.sin((lo2-lo1)/2)**2
    return 2 * R * math.asin(math.sqrt(h))

def nearest_region(data):
    geo = data.get("geoLocation") or {}
    lat, lon = geo.get("latitude"), geo.get("longitude")
    if lat is None or lon is None:
        return {"region": "default", "reason": "no coordinates"}
    pt = (lat, lon)
    region = min(REGIONS, key=lambda r: haversine(pt, REGIONS[r]))
    return {"country": geo.get("countryCode"), "region": region, "km": round(haversine(pt, REGIONS[region]))}

if __name__ == "__main__":
    ip = sys.argv[1] if len(sys.argv) > 1 else "91.0.0.1"
    print(f"{ip}: {nearest_region(lookup(ip))}")
Output
$ python3 route.py 91.0.0.1        # Germany
91.0.0.1: {'country': 'DE', 'region': 'eu-west (Frankfurt)', 'km': 183}

$ python3 route.py 76.102.11.203   # California
76.102.11.203: {'country': 'US', 'region': 'us-west (Oregon)', 'km': 958}

$ python3 route.py 133.0.0.1       # Japan
133.0.0.1: {'country': 'JP', 'region': 'ap-northeast (Tokyo)', 'km': 0}

Start with a free API key

Free tier: 2,000 lookups/day, no card. Paid plans from $2.50/month. Full field reference here, or see the regional-routing use case here.