This is a Go reader for the MaxMind DB format. Although this can be used to read GeoLite2 and GeoIP2 databases, geoip2 provides a higher-level API for doing so.
This is not an official MaxMind API.
go get github.com/oschwald/maxminddb-golang/v2
Version 2 includes significant improvements:
netip.Addr instead of net.IP for better performanceCursorUnmarshaler for
reflection-free custom decodingNetworks() and NetworksWithin()Reader.Verify() and access
metadata helpers such as Metadata.BuildTime()See MIGRATION.md for guidance on updating existing v1 code.
package main
import (
"fmt"
"log"
"net/netip"
"github.com/oschwald/maxminddb-golang/v2"
)
func main() {
db, err := maxminddb.Open("GeoLite2-City.mmdb")
if err != nil {
log.Fatal(err)
}
defer db.Close()
ip, err := netip.ParseAddr("81.2.69.142")
if err != nil {
log.Fatal(err)
}
var record struct {
Country struct {
ISOCode string `maxminddb:"iso_code"`
Names map[string]string `maxminddb:"names"`
} `maxminddb:"country"`
City struct {
Names map[string]string `maxminddb:"names"`
} `maxminddb:"city"`
}
err = db.Lookup(ip).Decode(&record)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Country: %s (%s)\n", record.Country.Names["en"], record.Country.ISOCode)
fmt.Printf("City: %s\n", record.City.Names["en"])
}
db, err := maxminddb.Open("GeoLite2-City.mmdb")
if err != nil {
log.Fatal(err)
}
defer db.Close()
var record any
ip := netip.MustParseAddr("1.2.3.4")
err = db.Lookup(ip).Decode(&record)
type City struct {
Country struct {
ISOCode string `maxminddb:"iso_code"`
Names struct {
English string `maxminddb:"en"`
German string `maxminddb:"de"`
} `maxminddb:"names"`
} `maxminddb:"country"`
}
var city City
err = db.Lookup(ip).Decode(&city)
For application-owned structs, maxminddb-gen can generate an
UnmarshalMaxMindDBCursor method that avoids reflection. The generator is
versioned with this module and remains optional; types with neither generated
nor handwritten custom unmarshaling methods continue to use reflection.
Add the tool to the consuming module's go.mod and add a generation directive
in the package that owns the target types:
tool github.com/oschwald/maxminddb-golang/v2/maxminddb-gen
//go:generate go tool maxminddb-gen $GOFILE
This discovers the exported structs declared in the directive's source file.
For models.go, it writes models_maxminddb.go; recognized build suffixes and
source build constraints are preserved. Constrained inputs must match the
generation environment; multiple inputs share the intersection of their
constraints. Use -output to override the default. Run go generate ./... and
check the generated file into source control. See
maxminddb-gen/README.md for supported types,
diagnostics, and reproducible CI usage.
For new handwritten decoders, implement mmdbdata.CursorUnmarshaler. Cursor
reads return an opaque successor positioned after the decoded value, allowing
nested custom decoding to continue without rescanning it.
The older UnmarshalMaxMindDB(*mmdbdata.Decoder) error callback is deprecated.
It remains supported throughout v2 but is planned for removal in v3; see
GitHub #224. When a
type implements both callbacks, UnmarshalMaxMindDBCursor takes precedence.
type Label string
func (label *Label) UnmarshalMaxMindDBCursor(
cursor mmdbdata.Cursor,
) (mmdbdata.Cursor, error) {
value, next, err := cursor.ReadString()
if err != nil {
return mmdbdata.Cursor{}, mmdbdata.NormalizeUnmarshalError[Label](err)
}
*label = Label(value)
return next, nil
}
// Iterate over all networks in the database
for result := range db.Networks() {
var record struct {
Country struct {
ISOCode string `maxminddb:"iso_code"`
} `maxminddb:"country"`
}
err := result.Decode(&record)
if err != nil {
log.Fatal(err)
}
fmt.Printf("%s: %s\n", result.Prefix(), record.Country.ISOCode)
}
// Iterate over networks within a specific prefix
prefix := netip.MustParsePrefix("192.168.0.0/16")
for result := range db.NetworksWithin(prefix) {
// Process networks within 192.168.0.0/16
}
var countryCode string
err = db.Lookup(ip).DecodePath(&countryCode, "country", "iso_code")
var cityName string
err = db.Lookup(ip).DecodePath(&cityName, "city", "names", "en")
This library supports all MaxMind DB (.mmdb) format databases, including:
MaxMind Official Databases:
Third-Party Databases:
The library is format-agnostic and will work with any valid .mmdb file regardless of the data provider.
Close invalidates outstanding results, Reader-backed cursors,
and their derived traversal handles; it must not run concurrently with their
use and should run only after readers are done.anymaxminddb-gen, or implement CursorUnmarshaler for custom decodingResult.Offset() as a cache key for database
recordsDownload from MaxMind's GeoLite page.
Contributions welcome! Please fork the repository and open a pull request with your changes.
This is free software, licensed under the ISC License.
Go
99.1%
This is a Go reader for the MaxMind DB format. Although this can be used to read GeoLite2 and GeoIP2 databases, geoip2 provides a higher-level API for doing so.
This is not an official MaxMind API.
go get github.com/oschwald/maxminddb-golang/v2
Version 2 includes significant improvements:
netip.Addr instead of net.IP for better performanceCursorUnmarshaler for
reflection-free custom decodingNetworks() and NetworksWithin()Reader.Verify() and access
metadata helpers such as Metadata.BuildTime()See MIGRATION.md for guidance on updating existing v1 code.
package main
import (
"fmt"
"log"
"net/netip"
"github.com/oschwald/maxminddb-golang/v2"
)
func main() {
db, err := maxminddb.Open("GeoLite2-City.mmdb")
if err != nil {
log.Fatal(err)
}
defer db.Close()
ip, err := netip.ParseAddr("81.2.69.142")
if err != nil {
log.Fatal(err)
}
var record struct {
Country struct {
ISOCode string `maxminddb:"iso_code"`
Names map[string]string `maxminddb:"names"`
} `maxminddb:"country"`
City struct {
Names map[string]string `maxminddb:"names"`
} `maxminddb:"city"`
}
err = db.Lookup(ip).Decode(&record)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Country: %s (%s)\n", record.Country.Names["en"], record.Country.ISOCode)
fmt.Printf("City: %s\n", record.City.Names["en"])
}
db, err := maxminddb.Open("GeoLite2-City.mmdb")
if err != nil {
log.Fatal(err)
}
defer db.Close()
var record any
ip := netip.MustParseAddr("1.2.3.4")
err = db.Lookup(ip).Decode(&record)
type City struct {
Country struct {
ISOCode string `maxminddb:"iso_code"`
Names struct {
English string `maxminddb:"en"`
German string `maxminddb:"de"`
} `maxminddb:"names"`
} `maxminddb:"country"`
}
var city City
err = db.Lookup(ip).Decode(&city)
For application-owned structs, maxminddb-gen can generate an
UnmarshalMaxMindDBCursor method that avoids reflection. The generator is
versioned with this module and remains optional; types with neither generated
nor handwritten custom unmarshaling methods continue to use reflection.
Add the tool to the consuming module's go.mod and add a generation directive
in the package that owns the target types:
tool github.com/oschwald/maxminddb-golang/v2/maxminddb-gen
//go:generate go tool maxminddb-gen $GOFILE
This discovers the exported structs declared in the directive's source file.
For models.go, it writes models_maxminddb.go; recognized build suffixes and
source build constraints are preserved. Constrained inputs must match the
generation environment; multiple inputs share the intersection of their
constraints. Use -output to override the default. Run go generate ./... and
check the generated file into source control. See
maxminddb-gen/README.md for supported types,
diagnostics, and reproducible CI usage.
For new handwritten decoders, implement mmdbdata.CursorUnmarshaler. Cursor
reads return an opaque successor positioned after the decoded value, allowing
nested custom decoding to continue without rescanning it.
The older UnmarshalMaxMindDB(*mmdbdata.Decoder) error callback is deprecated.
It remains supported throughout v2 but is planned for removal in v3; see
GitHub #224. When a
type implements both callbacks, UnmarshalMaxMindDBCursor takes precedence.
type Label string
func (label *Label) UnmarshalMaxMindDBCursor(
cursor mmdbdata.Cursor,
) (mmdbdata.Cursor, error) {
value, next, err := cursor.ReadString()
if err != nil {
return mmdbdata.Cursor{}, mmdbdata.NormalizeUnmarshalError[Label](err)
}
*label = Label(value)
return next, nil
}
// Iterate over all networks in the database
for result := range db.Networks() {
var record struct {
Country struct {
ISOCode string `maxminddb:"iso_code"`
} `maxminddb:"country"`
}
err := result.Decode(&record)
if err != nil {
log.Fatal(err)
}
fmt.Printf("%s: %s\n", result.Prefix(), record.Country.ISOCode)
}
// Iterate over networks within a specific prefix
prefix := netip.MustParsePrefix("192.168.0.0/16")
for result := range db.NetworksWithin(prefix) {
// Process networks within 192.168.0.0/16
}
var countryCode string
err = db.Lookup(ip).DecodePath(&countryCode, "country", "iso_code")
var cityName string
err = db.Lookup(ip).DecodePath(&cityName, "city", "names", "en")
This library supports all MaxMind DB (.mmdb) format databases, including:
MaxMind Official Databases:
Third-Party Databases:
The library is format-agnostic and will work with any valid .mmdb file regardless of the data provider.
Close invalidates outstanding results, Reader-backed cursors,
and their derived traversal handles; it must not run concurrently with their
use and should run only after readers are done.anymaxminddb-gen, or implement CursorUnmarshaler for custom decodingResult.Offset() as a cache key for database
recordsDownload from MaxMind's GeoLite page.
Contributions welcome! Please fork the repository and open a pull request with your changes.
This is free software, licensed under the ISC License.
Go
99.1%