geoip2-golang

Unofficial Go reader for MaxMind GeoIP2 and GeoLite2 databases, exposing typed City, Country, ASN, ISP, and Enterprise IP lookups over a fast memory-mapped mmdb decoder.

SDK
Go
vv1.13.0
2,336stars
ISC

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
74/100Good
Development Activity80
Maintenance56
Community60
Maturity60
Momentum40

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
80/100Excellent
Architecture80
Code Quality85
Innovation90
Learning Curve65

geoip2-golang is a Go client for reading MaxMind’s GeoIP2 and GeoLite2 binary database files (.mmdb format). It wraps the lower-level maxminddb-golang reader with a typed, database-aware API: callers open a database once and then call the method matching its type (City, Country, ASN, ISP, Domain, ConnectionType, AnonymousIP, AnonymousPlus, or Enterprise) with a netip.Addr, getting back a fully typed struct with localized place names, coordinates, ASN/ISP metadata, or anonymizer signals depending on which MaxMind product is loaded.

Version 2 is a deliberate rewrite around Go’s modern netip.Addr type and generated (rather than reflection-based) decoders, cutting allocations by 56% and memory use by 34% versus v1 while keeping the same per-database-type method calls. Every result struct exposes a HasData() check so callers can distinguish “no data for this IP” from a decode error, and the library validates the loaded database’s declared type before running a lookup, returning a descriptive InvalidMethodError instead of silently returning zero values.

What You Get

  • A single Reader type opened via Open() (file path) or OpenBytes() (in-memory), memory-mapping the .mmdb file for fast repeated lookups
  • One typed method per MaxMind database (City, Country, ASN, ISP, Domain, ConnectionType, AnonymousIP, AnonymousPlus, Enterprise) that refuses to run against the wrong database type
  • Fully typed result structs with localized Names (en, de, es, fr, ja, pt-BR, ru, zh-CN), HasData()/HasCoordinates() predicates, and populated Network/IPAddress fields on every result
  • Generated (non-reflection) decoders for the model structs, checked in and regenerated via go generate against the maxminddb-golang code generator
  • A documented MIGRATION.md covering every v1-to-v2 breaking change, plus GoDoc examples for every supported database type

Common Use Cases

  • Enriching web server access logs or analytics events with visitor city/country before they’re written downstream
  • Blocking or flagging requests from anonymizing VPNs, proxies, or Tor exit nodes using the AnonymousIP/AnonymousPlus databases
  • Attributing traffic to an ISP or autonomous system for abuse detection, fraud scoring, or network-quality routing decisions
  • Geo-based content localization or feature gating in a Go backend without an external geolocation API call per request

Under The Hood

Architecture The library is a thin, single-package (geoip2) typed facade over maxminddb-golang/v2’s memory-mapped reader. reader.go defines a Reader struct holding the underlying mmdbReader plus a databaseType bitmask (built with iota-based flags) computed once at Open/OpenBytes time from the mmdb file’s declared Metadata.DatabaseType string. Each public lookup method (City, Country, ASN, Enterprise, ISP, Domain, ConnectionType, AnonymousIP, AnonymousPlus) first checks its own bit against that mask — returning a typed InvalidMethodError if the database doesn’t support it — then delegates to mmdbReader.Lookup(ip).Decode(&result) and back-fills Network/IPAddress on the decoded struct from the lookup result. There is no internal layering or DI: the model types in models.go are pure data with a handful of convenience predicates (HasData, HasCoordinates), and a custom Date type implements mmdbdata.Unmarshaler/json.Marshaler for MaxMind’s ISO-8601 date fields. Swapping the core decode strategy (reflection vs. generated) required no changes to this public surface, which is the intended seam.

Tech Stack A Go 1.25 module (github.com/oschwald/geoip2-golang/v2) with a single runtime dependency, github.com/oschwald/maxminddb-golang/v2 (same author), which supplies the actual mmdb file format decoder and memory-mapping. Tests depend on stretchr/testify. The module declares maxminddb-golang/v2/maxminddb-gen as a Go 1.24-style tool dependency, invoked via a //go:generate directive in models.go to regenerate models_maxminddb.go’s hand-off-free decoders; CI (golangci-lint with default: all linters, tuned via .golangci.yml) and a GitHub Actions Go workflow plus CodeQL scanning gate every change, including a check that generated files are current.

Code Quality reader_test.go carries 14 test functions plus a table-driven generated_test.go pair asserting the generated decoders match the reflection path bit-for-bit; reflection_benchmark_test.go benchmarks the two decode strategies against each other, and example_test.go supplies compiler-checked GoDoc examples for every database type. Errors are explicit and typed (InvalidMethodError, UnknownDatabaseTypeError) rather than sentinel strings or panics, both implementing error with a descriptive message naming the offending method and database type. Naming and struct tags (json + maxminddb on every field) are consistent throughout, and the golangci-lint config opts into nearly every available linter rather than a minimal subset.

API Design Getting a result takes three calls: Open, netip.ParseAddr, and the matching typed method — no configuration object or builder required, though functional Option values are supported for advanced maxminddb tuning. Every returned record exposes HasData() so callers don’t need to hand-check zero values, and coordinate access goes through HasCoordinates() rather than nil-checking two pointer fields directly. Failure modes are self-describing (calling ISP() on a City database names both the method and the database type in the error) instead of returning an ambiguous empty struct. A package-level GoDoc comment doubles as a runnable quick-start, and MIGRATION.md documents every v1→v2 signature and field change for upgraders.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search