Enrich Events with Geo IP Data in Python

Returns a copy of each event dictionary, enriched with a geo-location dict from a mock IP-to-geo lookup table, with a fallback for unknown IPs.

Easy Python 3.9+ Aug 9, 2026 Data pipelines & processing 14 views 0 copies

Python code

32 lines
Python 3.9+
import ipaddress


GEO_IP_DB = {
    "192.168.1.10": {"country": "US", "city": "New York", "lat": 40.7128, "lon": -74.0060},
    "10.0.0.5": {"country": "DE", "city": "Berlin", "lat": 52.5200, "lon": 13.4050},
    "172.16.0.8": {"country": "JP", "city": "Tokyo", "lat": 35.6762, "lon": 139.6503},
}

EVENTS = [
    {"id": 1, "ip": "192.168.1.10", "action": "login"},
    {"id": 2, "ip": "10.0.0.5", "action": "download"},
    {"id": 3, "ip": "8.8.8.8", "action": "ping"},
    {"id": 4, "ip": "172.16.0.8", "action": "upload"},
]


def enrich_event(event: dict) -> dict:
    """Return a copy of the event, enriched with geo data if known."""
    enriched = dict(event)
    ip = event.get("ip", "")
    geo = GEO_IP_DB.get(ip)
    if geo is not None:
        enriched["geo"] = geo
    else:
        enriched["geo"] = {"country": "UNKNOWN", "city": "UNKNOWN", "lat": None, "lon": None}
    return enriched


if __name__ == "__main__":
    for ev in EVENTS:
        print(enrich_event(ev))

Output

stdout
{'id': 1, 'ip': '192.168.1.10', 'action': 'login', 'geo': {'country': 'US', 'city': 'New York', 'lat': 40.7128, 'lon': -74.006}}
{'id': 2, 'ip': '10.0.0.5', 'action': 'download', 'geo': {'country': 'DE', 'city': 'Berlin', 'lat': 52.52, 'lon': 13.405}}
{'id': 3, 'ip': '8.8.8.8', 'action': 'ping', 'geo': {'country': 'UNKNOWN', 'city': 'UNKNOWN', 'lat': None, 'lon': None}}
{'id': 4, 'ip': '172.16.0.8', 'action': 'upload', 'geo': {'country': 'JP', 'city': 'Tokyo', 'lat': 35.6762, 'lon': 139.6503}}

How it works

The function creates a shallow copy of the original event dict with dict(event) so the input isn't mutated. It then uses GEO_IP_DB.get(ip) to look up geo data; if found, it adds a geo key, otherwise it supplies a fallback dict with UNKNOWN placeholders. This pattern keeps enrichment side-effect-free and predictable, which is essential in data pipelines where events are processed in batches and original records may be needed later. The ipaddress import is present but unused in this example; in production you'd validate or normalize IPs before lookup.

Common mistakes

  • Mutating the original event dict instead of returning a copy, causing side effects downstream.
  • Assuming every IP exists in the lookup table and not handling missing keys with a fallback.
  • Using `geo` as a direct reference to the DB dict, so later changes leak into the event data.
  • Ignoring IP format variations like IPv6 or strings with port numbers before lookup.

Variations

  1. Use a real geo IP library like `geoip2` with a MaxMind database for production lookups.
  2. Lazy-load the geo DB from a file or environment variable to avoid hardcoding in code.

Real-world use cases

  • Adding geographic metadata to security log events for regional threat analysis.
  • Enriching clickstream events with user location before storing in a data warehouse.
  • Populating dashboard events with city and country info for real-time traffic maps.

Sponsored

Run this sample

Open the browser IDE to tweak the example and see results without installing anything.

Open editor

More from Data pipelines & processing

Related tutorials and quizzes for this topic.