How to Geo-Target Ads & Filter Invalid Traffic by IP
Segment by country, region and city for creative — and filter datacentre invalid traffic — from one lookup.
You can pick the right regional creative and filter out non-human datacentre traffic from a single IP lookup — before you spend a cent serving an impression. The response gives you country, region and city for segmentation, and a connection type that flags hosting/datacentre origins where invalid traffic concentrates.
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:
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"]2Segment by geography and flag invalid traffic:
Use geoLocation.country / region / city to choose regional creative, and treat a Corporate connection type as a strong invalid-traffic (IVT) signal — ad impressions from cloud and hosting ranges are very often non-human. One caveat: Corporate also includes office and campus networks with real people, so pair it with the VPN/Tor flags and your own frequency rules rather than hard-excluding on network type alone:
def ad_segment(data):
geo = data.get("geoLocation") or {}
conn = data.get("connection") or {}
# Datacentre/hosting origins are where most invalid (non-human) traffic sits
invalid = conn.get("connection_type") == "Corporate"
return {
"country": geo.get("countryCode"),
"region": geo.get("region"),
"city": geo.get("city"),
"invalid_traffic": invalid,
"serve_ads": not invalid,
}
print(ad_segment(lookup("91.0.0.1")))3Serve or filter — the full script:
Drop the datacentre impressions before you pay for them, and route everyone else to the right regional creative. Note that region and city can be null on some IPs — fall back to country-level targeting when they are:
#!/usr/bin/env python3
"""Geo-segment + invalid-traffic filter from an IP (IPWho IP geolocation API)."""
import os, sys, urllib.request, json
API_KEY=*** lookup(ip):
req = urllib.request.Request(
f"https://api.ipwho.org/ip/{ip}",
headers={"Authorization": f"Bearer {API_KEY}", "User-Agent": "IPWho-Ads/1.0"},
)
with urllib.request.urlopen(req, timeout=10) as resp:
return json.loads(resp.read())["data"]
def ad_segment(data):
geo = data.get("geoLocation") or {}
conn = data.get("connection") or {}
invalid = conn.get("connection_type") == "Corporate"
return {
"country": geo.get("countryCode"),
"region": geo.get("region"),
"city": geo.get("city"),
"invalid_traffic": invalid,
"serve_ads": not invalid,
}
if __name__ == "__main__":
ip = sys.argv[1] if len(sys.argv) > 1 else "91.0.0.1"
print(f"{ip}: {ad_segment(lookup(ip))}")$ python3 ads.py 91.0.0.1 # Germany, residential
91.0.0.1: {'country': 'DE', 'region': 'North Rhine-Westphalia', 'city': 'Düsseldorf', 'invalid_traffic': False, 'serve_ads': True}
$ python3 ads.py 76.102.11.203 # California, residential
76.102.11.203: {'country': 'US', 'region': 'California', 'city': 'Los Altos', 'invalid_traffic': False, 'serve_ads': True}
$ python3 ads.py 52.95.110.1 # AWS datacentre -> filtered
52.95.110.1: {'country': 'US', 'region': None, 'city': None, 'invalid_traffic': True, 'serve_ads': False}