- Notifications
You must be signed in to change notification settings - Fork215
Unofficial MaxMind GeoIP2 Reader for Go
License
oschwald/geoip2-golang
Folders and files
| Name | Name | Last commit message | Last commit date | |
|---|---|---|---|---|
Repository files navigation
This library reads MaxMindGeoLite2 andGeoIP2 databases.
This library is built usingthe Go maxminddb reader. Alldata for the database record is decoded using this library. Version 2.0provides significant performance improvements with 56% fewer allocations and34% less memory usage compared to v1. Version 2.0 also addsNetwork andIPAddress fields to all result structs, and includes aHasData() method toeasily check if data was found. If you only need several fields, you may getsuperior performance by using maxminddb'sLookup directly with a resultstruct that only contains the required fields. (Seeexample_test.goin the maxminddb repository for an example of this.)
go get github.com/oschwald/geoip2-golang/v2Version 2.0 includes several major improvements:
- Performance: 56% fewer allocations and 34% less memory usage
- Modern API: Uses
netip.Addrinstead ofnet.IPfor better performance - Network Information: All result structs now include
NetworkandIPAddressfields - Data Validation: New
HasData()method to easily check if data was found - Structured Names: Replaced
map[string]stringwith typedNamesstructfor better performance - Go 1.24 Support: Uses
omitzeroJSON tags to match MaxMind databasebehavior
SeeMIGRATION.md for step-by-step guidance on upgrading fromv1.
See GoDoc fordocumentation and examples.
package mainimport ("fmt""log""net/netip""github.com/oschwald/geoip2-golang/v2")funcmain() {db,err:=geoip2.Open("GeoIP2-City.mmdb")iferr!=nil {log.Fatal(err)}deferdb.Close()// If you are using strings that may be invalid, use netip.ParseAddr and check for errorsip,err:=netip.ParseAddr("81.2.69.142")iferr!=nil {log.Fatal(err)}record,err:=db.City(ip)iferr!=nil {log.Fatal(err)}if!record.HasData() {fmt.Println("No data found for this IP")return}fmt.Printf("Portuguese (BR) city name: %v\n",record.City.Names.BrazilianPortuguese)iflen(record.Subdivisions)>0 {fmt.Printf("English subdivision name: %v\n",record.Subdivisions[0].Names.English)}fmt.Printf("Russian country name: %v\n",record.Country.Names.Russian)fmt.Printf("ISO country code: %v\n",record.Country.ISOCode)fmt.Printf("Time zone: %v\n",record.Location.TimeZone)ifrecord.Location.HasCoordinates() {fmt.Printf("Coordinates: %v, %v\n",*record.Location.Latitude,*record.Location.Longitude)}// Output:// Portuguese (BR) city name: Londres// English subdivision name: England// Russian country name: Великобритания// ISO country code: GB// Time zone: Europe/London// Coordinates: 51.5142, -0.0931}
- Go 1.24 or later
- MaxMind GeoIP2 or GeoLite2 database files (.mmdb format)
Download free GeoLite2 databases fromMaxMind's website.Registration required.
Purchase GeoIP2 databases fromMaxMind for enhanced accuracyand additional features.
This library supports all MaxMind GeoIP2 and GeoLite2 database types. Below areexamples for each database type:
The City database provides the most comprehensive geolocation data, includingcity, subdivision, country, and precise location information.
package mainimport ("fmt""log""net/netip""github.com/oschwald/geoip2-golang/v2")funcmain() {db,err:=geoip2.Open("GeoIP2-City.mmdb")iferr!=nil {log.Fatal(err)}deferdb.Close()ip,err:=netip.ParseAddr("128.101.101.101")iferr!=nil {log.Fatal(err)}record,err:=db.City(ip)iferr!=nil {log.Fatal(err)}if!record.HasData() {fmt.Println("No data found for this IP")return}fmt.Printf("City: %v\n",record.City.Names.English)fmt.Printf("Subdivision: %v\n",record.Subdivisions[0].Names.English)fmt.Printf("Country: %v (%v)\n",record.Country.Names.English,record.Country.ISOCode)fmt.Printf("Continent: %v (%v)\n",record.Continent.Names.English,record.Continent.Code)fmt.Printf("Postal Code: %v\n",record.Postal.Code)ifrecord.Location.HasCoordinates() {fmt.Printf("Location: %v, %v\n",*record.Location.Latitude,*record.Location.Longitude)}fmt.Printf("Time Zone: %v\n",record.Location.TimeZone)fmt.Printf("Network: %v\n",record.Traits.Network)fmt.Printf("IP Address: %v\n",record.Traits.IPAddress)}
The Country database provides country-level geolocation data.
package mainimport ("fmt""log""net/netip""github.com/oschwald/geoip2-golang/v2")funcmain() {db,err:=geoip2.Open("GeoIP2-Country.mmdb")iferr!=nil {log.Fatal(err)}deferdb.Close()ip,err:=netip.ParseAddr("81.2.69.142")iferr!=nil {log.Fatal(err)}record,err:=db.Country(ip)iferr!=nil {log.Fatal(err)}if!record.HasData() {fmt.Println("No data found for this IP")return}fmt.Printf("Country: %v (%v)\n",record.Country.Names.English,record.Country.ISOCode)fmt.Printf("Continent: %v (%v)\n",record.Continent.Names.English,record.Continent.Code)fmt.Printf("Is in EU: %v\n",record.Country.IsInEuropeanUnion)fmt.Printf("Network: %v\n",record.Traits.Network)fmt.Printf("IP Address: %v\n",record.Traits.IPAddress)ifrecord.RegisteredCountry.Names.English!="" {fmt.Printf("Registered Country: %v (%v)\n",record.RegisteredCountry.Names.English,record.RegisteredCountry.ISOCode)}}
The ASN database provides Autonomous System Number and organizationinformation.
package mainimport ("fmt""log""net/netip""github.com/oschwald/geoip2-golang/v2")funcmain() {db,err:=geoip2.Open("GeoLite2-ASN.mmdb")iferr!=nil {log.Fatal(err)}deferdb.Close()ip,err:=netip.ParseAddr("1.128.0.0")iferr!=nil {log.Fatal(err)}record,err:=db.ASN(ip)iferr!=nil {log.Fatal(err)}if!record.HasData() {fmt.Println("No data found for this IP")return}fmt.Printf("ASN: %v\n",record.AutonomousSystemNumber)fmt.Printf("Organization: %v\n",record.AutonomousSystemOrganization)fmt.Printf("Network: %v\n",record.Network)fmt.Printf("IP Address: %v\n",record.IPAddress)}
The Anonymous IP database identifies various types of anonymous and proxynetworks.
package mainimport ("fmt""log""net/netip""github.com/oschwald/geoip2-golang/v2")funcmain() {db,err:=geoip2.Open("GeoIP2-Anonymous-IP.mmdb")iferr!=nil {log.Fatal(err)}deferdb.Close()ip,err:=netip.ParseAddr("81.2.69.142")iferr!=nil {log.Fatal(err)}record,err:=db.AnonymousIP(ip)iferr!=nil {log.Fatal(err)}if!record.HasData() {fmt.Println("No data found for this IP")return}fmt.Printf("Is Anonymous: %v\n",record.IsAnonymous)fmt.Printf("Is Anonymous VPN: %v\n",record.IsAnonymousVPN)fmt.Printf("Is Hosting Provider: %v\n",record.IsHostingProvider)fmt.Printf("Is Public Proxy: %v\n",record.IsPublicProxy)fmt.Printf("Is Residential Proxy: %v\n",record.IsResidentialProxy)fmt.Printf("Is Tor Exit Node: %v\n",record.IsTorExitNode)fmt.Printf("Network: %v\n",record.Network)fmt.Printf("IP Address: %v\n",record.IPAddress)}
The Enterprise database provides the most comprehensive data, including allCity database fields plus additional enterprise features.
package mainimport ("fmt""log""net/netip""github.com/oschwald/geoip2-golang/v2")funcmain() {db,err:=geoip2.Open("GeoIP2-Enterprise.mmdb")iferr!=nil {log.Fatal(err)}deferdb.Close()ip,err:=netip.ParseAddr("128.101.101.101")iferr!=nil {log.Fatal(err)}record,err:=db.Enterprise(ip)iferr!=nil {log.Fatal(err)}if!record.HasData() {fmt.Println("No data found for this IP")return}// Basic location informationfmt.Printf("City: %v\n",record.City.Names.English)fmt.Printf("Country: %v (%v)\n",record.Country.Names.English,record.Country.ISOCode)ifrecord.Location.HasCoordinates() {fmt.Printf("Location: %v, %v\n",*record.Location.Latitude,*record.Location.Longitude)}// Enterprise-specific fieldsfmt.Printf("ISP: %v\n",record.Traits.ISP)fmt.Printf("Organization: %v\n",record.Traits.Organization)fmt.Printf("ASN: %v (%v)\n",record.Traits.AutonomousSystemNumber,record.Traits.AutonomousSystemOrganization)fmt.Printf("Connection Type: %v\n",record.Traits.ConnectionType)fmt.Printf("Domain: %v\n",record.Traits.Domain)fmt.Printf("User Type: %v\n",record.Traits.UserType)fmt.Printf("Static IP Score: %v\n",record.Traits.StaticIPScore)fmt.Printf("Is Anycast: %v\n",record.Traits.IsAnycast)fmt.Printf("Is Legitimate Proxy: %v\n",record.Traits.IsLegitimateProxy)// Mobile carrier information (if available)ifrecord.Traits.MobileCountryCode!="" {fmt.Printf("Mobile Country Code: %v\n",record.Traits.MobileCountryCode)fmt.Printf("Mobile Network Code: %v\n",record.Traits.MobileNetworkCode)}fmt.Printf("Network: %v\n",record.Traits.Network)fmt.Printf("IP Address: %v\n",record.Traits.IPAddress)}
The ISP database provides ISP, organization, and ASN information.
package mainimport ("fmt""log""net/netip""github.com/oschwald/geoip2-golang/v2")funcmain() {db,err:=geoip2.Open("GeoIP2-ISP.mmdb")iferr!=nil {log.Fatal(err)}deferdb.Close()ip,err:=netip.ParseAddr("1.128.0.0")iferr!=nil {log.Fatal(err)}record,err:=db.ISP(ip)iferr!=nil {log.Fatal(err)}if!record.HasData() {fmt.Println("No data found for this IP")return}fmt.Printf("ISP: %v\n",record.ISP)fmt.Printf("Organization: %v\n",record.Organization)fmt.Printf("ASN: %v (%v)\n",record.AutonomousSystemNumber,record.AutonomousSystemOrganization)// Mobile carrier information (if available)ifrecord.MobileCountryCode!="" {fmt.Printf("Mobile Country Code: %v\n",record.MobileCountryCode)fmt.Printf("Mobile Network Code: %v\n",record.MobileNetworkCode)}fmt.Printf("Network: %v\n",record.Network)fmt.Printf("IP Address: %v\n",record.IPAddress)}
The Domain database provides the second-level domain associated with an IPaddress.
package mainimport ("fmt""log""net/netip""github.com/oschwald/geoip2-golang/v2")funcmain() {db,err:=geoip2.Open("GeoIP2-Domain.mmdb")iferr!=nil {log.Fatal(err)}deferdb.Close()ip,err:=netip.ParseAddr("1.2.0.0")iferr!=nil {log.Fatal(err)}record,err:=db.Domain(ip)iferr!=nil {log.Fatal(err)}if!record.HasData() {fmt.Println("No data found for this IP")return}fmt.Printf("Domain: %v\n",record.Domain)fmt.Printf("Network: %v\n",record.Network)fmt.Printf("IP Address: %v\n",record.IPAddress)}
The Connection Type database identifies the connection type of an IP address.
package mainimport ("fmt""log""net/netip""github.com/oschwald/geoip2-golang/v2")funcmain() {db,err:=geoip2.Open("GeoIP2-Connection-Type.mmdb")iferr!=nil {log.Fatal(err)}deferdb.Close()ip,err:=netip.ParseAddr("1.0.128.0")iferr!=nil {log.Fatal(err)}record,err:=db.ConnectionType(ip)iferr!=nil {log.Fatal(err)}if!record.HasData() {fmt.Println("No data found for this IP")return}fmt.Printf("Connection Type: %v\n",record.ConnectionType)fmt.Printf("Network: %v\n",record.Network)fmt.Printf("IP Address: %v\n",record.IPAddress)}
All database lookups can return errors and should be handled appropriately:
package mainimport ("fmt""log""net/netip""github.com/oschwald/geoip2-golang/v2")funcmain() {db,err:=geoip2.Open("GeoIP2-City.mmdb")iferr!=nil {log.Fatal(err)}deferdb.Close()ip,err:=netip.ParseAddr("10.0.0.1")// Private IPiferr!=nil {log.Fatal(err)}record,err:=db.City(ip)iferr!=nil {log.Fatal(err)}// Always check if data was foundif!record.HasData() {fmt.Println("No data found for this IP address")return}// Check individual fields before using themifrecord.City.Names.English!="" {fmt.Printf("City: %v\n",record.City.Names.English)}else {fmt.Println("City name not available")}// Check array bounds for subdivisionsiflen(record.Subdivisions)>0 {fmt.Printf("Subdivision: %v\n",record.Subdivisions[0].Names.English)}else {fmt.Println("No subdivision data available")}fmt.Printf("Country: %v\n",record.Country.Names.English)}
Always reuse database instances across requests rather than opening/closingrepeatedly:
// Good: Create once, use many timesdb,err:=geoip2.Open("GeoIP2-City.mmdb")iferr!=nil {log.Fatal(err)}deferdb.Close()// Use db for multiple lookups...
For applications needing only specific fields, consider using the lower-levelmaxminddb library with custom result structs to reduce memory allocation.
The Reader is safe for concurrent use by multiple goroutines.
All result structs include JSON tags and support marshaling to JSON:
record,err:=db.City(ip)iferr!=nil {log.Fatal(err)}jsonData,err:=json.Marshal(record)iferr!=nil {log.Fatal(err)}fmt.Println(string(jsonData))
- Import Path: Change from
github.com/oschwald/geoip2-golangtogithub.com/oschwald/geoip2-golang/v2 - IP Type: Use
netip.Addrinstead ofnet.IP - Field Names:
IsoCode→ISOCode - Names Access: Use struct fields instead of map access
- Data Validation: Use
HasData()method to check for data availability
// v1import"github.com/oschwald/geoip2-golang"ip:=net.ParseIP("81.2.69.142")record,err:=db.City(ip)cityName:=record.City.Names["en"]// v2import"github.com/oschwald/geoip2-golang/v2"ip,err:=netip.ParseAddr("81.2.69.142")iferr!=nil {// handle error}record,err:=db.City(ip)if!record.HasData() {// handle no data found}cityName:=record.City.Names.English
Database not found: Ensure the .mmdb file path is correct and readable.
No data returned: Check ifHasData() returns false - the IP may not be inthe database or may be a private/reserved IP.
Performance issues: Ensure you're reusing the database instance rather thanopening it for each lookup.
Make sure you checked out test data submodule:
git submodule initgit submodule updateExecute test suite:
go testContributions welcome! Please fork the repository and open a pull request withyour changes.
This is free software, licensed under the ISC license.
About
Unofficial MaxMind GeoIP2 Reader for Go
Topics
Resources
License
Uh oh!
There was an error while loading.Please reload this page.
Stars
Watchers
Forks
Packages0
Uh oh!
There was an error while loading.Please reload this page.