Note: This repository is a read-only mirror. Please do not submit issues or pull requests here; contributions and development are managed internally.
The easiest way to add IP geolocation to Go applications. Look up any IP address to get country, city, region, timezone, coordinates, ASN, and EU membership — with city-level precision.
Built for developers who need geolocation without wiring up raw API calls: geo-aware proxy routing, exit-IP verification, EU compliance gating, timezone-accurate scheduling, and regional content delivery.
- Zero dependencies — only the Go standard library. No transitive dependency tree to audit
- Proxy-aware — plug in any
*http.Clientwith a proxy transport to discover exit IPs through tunnels - Context-native — every API call takes
context.Contextfor cancellation, timeouts, and tracing - Nil-safe accessors —
geo.CountryCode(),geo.CityName(),geo.Lat(),geo.TZ()never panic on partial responses - Typed errors —
*APIErrorwith machine-readable codes,errors.Assupport, and convenience predicates - Privacy compliance ready —
IsEU()helper for GDPR, country and region fields for CCPA and other regulations
| Use Case | How |
|---|---|
| Detect visitor country | geo.CountryCode() → "US" |
| Show cookie consent to EU users | netloc8.IsEU( geo ) |
| Discover proxy exit IP | client.LookupMe( ctx ) with proxy transport |
| Derive subnet for rotation | netloc8.Subnet( geo.IP() ) → "203.0.113.0/24" |
| Display local timezone | geo.TZ() → "America/Los_Angeles" |
| Classify IP as public/private | netloc8.IsPublicIP( ip ) |
| Extract client IP from headers | netloc8.GetClientIP( r ) |
go get github.com/netloc8/netloc8-goDocumentation · API Reference · Dashboard
import (
"context"
"fmt"
"github.com/netloc8/netloc8-go"
)
client := netloc8.NewClient( "sk_your_secret_key" )
geo, err := client.LookupIP( context.Background(), "8.8.8.8" )
if err != nil {
log.Fatal( err )
}
fmt.Println( geo.CountryCode() ) // "US"
fmt.Println( geo.CityName() ) // "Mountain View"
fmt.Println( geo.TZ() ) // "America/Los_Angeles"
fmt.Println( geo.ASN() ) // "AS15169"
fmt.Println( geo.Org() ) // "Google LLC"All lookup methods return a *Geo struct matching the API's GeolocationResult:
geo, _ := client.LookupIP( ctx, "8.8.8.8" )
geo.IP() // "8.8.8.8"
geo.Query.IPVersion // 4
geo.CountryCode() // "US"
geo.CountryName() // "United States"
geo.Location.Country.Flag // "🇺🇸"
geo.Location.Country.Unions // ["EU"] or []
geo.RegionCode() // "CA"
geo.RegionName() // "California"
geo.CityName() // "Mountain View"
geo.Location.PostalCode // "94043"
geo.Lat() // 37.386
geo.Lng() // -122.084
geo.TZ() // "America/Los_Angeles"
geo.Location.UTCOffset // "-07:00"
geo.Location.GeoConfidence // 1.0
geo.ASN() // "AS15169"
geo.Org() // "Google LLC"
geo.Network.Domain // "google.com"
geo.Meta.Precision // "city"All accessor methods (CountryCode(), CityName(), Lat(), TZ(), etc.) are nil-safe
— they return zero values (empty strings or 0) on nil or partial responses, so you
never need to nil-check nested structs. Use HasCoordinates() to distinguish
absent coordinates from a real 0,0 location.
All methods accept context.Context for cancellation and timeouts.
| Method | Description |
|---|---|
LookupIP(ctx, ip) |
Full geolocation for a specific IP address |
LookupMe(ctx) |
Full geolocation for the caller's own IP |
Timezone(ctx, ip) |
IANA timezone string for a specific IP |
MyTimezone(ctx) |
IANA timezone string for the caller's own IP |
Validate(ctx, ip) |
Check whether a string is a valid IP address (via API) |
Requires a secret key (sk_).
| Method | Description |
|---|---|
GetProfile(ctx) |
Fetch the authenticated user's profile |
ListKeys(ctx) |
List all API keys (metadata only) |
CreateKey(ctx, name, ...opts) |
Create a new API key — returns the raw key once |
DeleteKey(ctx, keyID) |
Permanently delete an API key |
RenewKey(ctx, keyID) |
Extend a key's expiration |
GetUsage(ctx) |
API usage statistics for the current billing period |
GetAuditLog(ctx, ...opts) |
Paginated audit log with optional filters |
ListSites(ctx) |
List allowed origins for publishable keys |
CreateSite(ctx, origin) |
Add an allowed origin |
DeleteSite(ctx, siteID) |
Remove an allowed origin |
| Option | Description |
|---|---|
WithTimeout(d) |
Request timeout (default: 10s) |
WithHTTPClient(c) |
Custom *http.Client for proxy transports, custom TLS, connection pooling |
WithBaseURL(url) |
Override the API base URL (for testing or self-hosted deployments) |
WithUserAgent(ua) |
Prepend a custom User-Agent string |
WithOrigin(url) |
Set Origin header — only needed for publishable keys (pk_) |
Standalone functions that work locally — no API client or network access needed:
| Function | Description |
|---|---|
NormalizeIP(ip) |
Strip IPv4-mapped prefix, brackets, whitespace, lowercase |
IsPublicIP(ip) |
Check if IP is publicly routable (rejects RFC1918, CGNAT, loopback, link-local, ULA) |
IsIPv4(ip) / IsIPv6(ip) |
IP version detection |
ParseIP(ip) |
Validate and normalize an IP string |
Subnet(ip) |
Derive /24 CIDR prefix from IPv4 |
IsEU(geo) |
Check EU membership via country.unions |
GetClientIP(r) |
Extract the real client IP from HTTP request headers |
All fetch methods return errors — they never panic. API errors are returned as
*APIError with structured error codes:
geo, err := client.LookupIP( ctx, "not-an-ip" )
var apiErr *netloc8.APIError
if errors.As( err, &apiErr ) {
fmt.Println( apiErr.Code ) // "INVALID_IP"
fmt.Println( apiErr.Message ) // "Invalid IP address format"
fmt.Println( apiErr.Status ) // 400
fmt.Println( apiErr.RequestID ) // "req-abc-123"
}Convenience predicates:
netloc8.IsNotFound( err ) // 404 — IP not in database
netloc8.IsRateLimited( err ) // 429 — rate limit exceeded
netloc8.IsForbidden( err ) // 403 — invalid key or origin mismatchDiscover proxy exit IPs by passing a custom transport. The request tunnels
through the proxy — LookupMe returns the proxy's exit IP, not yours:
proxyURL, _ := url.Parse( "http://user:pass@proxy:8080" )
client := netloc8.NewClient( "pk_key",
netloc8.WithOrigin( "https://your-app.com" ),
netloc8.WithHTTPClient( &http.Client{
Transport: &http.Transport{ Proxy: http.ProxyURL( proxyURL ) },
}),
)
geo, _ := client.LookupMe( ctx )
fmt.Println( geo.IP() ) // proxy's exit IP
fmt.Println( geo.CountryCode() ) // proxy's exit country
fmt.Println( netloc8.Subnet( geo.IP() ) ) // "203.0.113.0/24"Extract the real client IP from reverse proxy headers in HTTP handlers:
func handler( w http.ResponseWriter, r *http.Request ) {
ip := netloc8.GetClientIP( r )
// Checks in order: X-Forwarded-For, CF-Connecting-IP,
// True-Client-IP, X-Real-IP, X-Client-IP,
// Fastly-Client-IP, Fly-Client-IP
}| Key type | Prefix | Usage |
|---|---|---|
| Secret | sk_ |
Server-side — full access, no origin restriction |
| Publishable | pk_ |
Origin-restricted — requires WithOrigin |