Go SDK
github.com/netloc8/netloc8-go — Zero-dependency, context-native IP geolocation and account management client for Go.
Installation
go get github.com/netloc8/netloc8-goRequires Go 1.22+. Zero external dependencies — only the Go standard library.
Quick Start
package main
import (
"context"
"fmt"
"log"
"github.com/netloc8/netloc8-go"
)
func main() {
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"
}Geolocation Methods
| Method | Returns | Description |
|---|---|---|
LookupIP( ctx, ip ) | *Geo, error | Full geolocation for a specific IPv4 or IPv6 address |
LookupMe( ctx ) | *Geo, error | Full geolocation for the caller's own IP |
Timezone( ctx, ip ) | string, error | IANA timezone string only (lightweight) |
MyTimezone( ctx ) | string, error | IANA timezone for the caller's own IP |
Validate( ctx, ip ) | bool, error | Check if a string is a valid IP address (via API) |
// Lightweight timezone lookup (no full geo response)
tz, err := client.Timezone( ctx, "8.8.8.8" )
// tz == "America/Los_Angeles"
// Validate before looking up
valid, err := client.Validate( ctx, userInput )
if valid {
geo, err := client.LookupIP( ctx, userInput )
}Account Management
Manage API keys, usage, audit logs, and allowed origins. Requires a secret key (sk_).
| Method | Description |
|---|---|
GetProfile( ctx ) | Fetch the authenticated user's profile (ID, email, name) |
ListKeys( ctx ) | List all API keys (metadata only — raw values never returned) |
CreateKey( ctx, name, ...opts ) | Create a new key — raw value returned once |
DeleteKey( ctx, keyID ) | Permanently revoke an API key |
RenewKey( ctx, keyID ) | Extend a key's expiration |
GetUsage( ctx ) | Usage stats 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 |
client := netloc8.NewClient( "sk_your_secret_key" )
// Create a new API key
created, err := client.CreateKey( ctx, "CI Pipeline",
netloc8.WithKeyType( "secret" ),
)
fmt.Println( created.RawKey ) // store this — only shown once
// Check usage
usage, err := client.GetUsage( ctx )
fmt.Printf( "%d / %d requests (%s)\n", usage.Total, usage.Cap, usage.Period )
// Query audit log with filters
log, err := client.GetAuditLog( ctx,
netloc8.WithLimit( 25 ),
netloc8.WithAction( "key.created" ),
)
for _, entry := range log.Entries {
fmt.Printf( "%s: %s by %s\n", entry.CreatedAt, entry.Action, entry.ActorLabel )
}Functional options
CreateKey accepts WithKeyType( "secret" | "publishable" ). GetAuditLog accepts WithLimit, WithOffset, and WithAction.
LookupMe — Discover Exit IP
LookupMe calls GET /v1/ip/me to geolocate the IP the API sees. With a proxy transport, this discovers the exit IP of the proxy:
import (
"net/http"
"net/url"
"github.com/netloc8/netloc8-go"
)
proxyURL, _ := url.Parse( "socks5://proxy.example.com:1080" )
transport := &http.Transport{ Proxy: http.ProxyURL( proxyURL ) }
client := netloc8.NewClient( "sk_your_key",
netloc8.WithHTTPClient( &http.Client{ Transport: transport } ),
)
geo, err := client.LookupMe( ctx )
if err != nil {
log.Fatal( err )
}
fmt.Println( geo.IP() ) // the proxy's exit IP
fmt.Println( geo.CountryCode() ) // country of the exit nodeNil-Safe Accessors
All accessor methods on *Geo are nil-safe — they never panic, even on partial or empty responses. Returns the zero value when the underlying field is absent.
| Accessor | Returns | Example |
|---|---|---|
geo.IP() | string | "8.8.8.8" |
geo.CountryCode() | string | "US" |
geo.CountryName() | string | "United States" |
geo.RegionCode() | string | "CA" |
geo.RegionName() | string | "California" |
geo.CityName() | string | "Mountain View" |
geo.TZ() | string | "America/Los_Angeles" |
geo.Lat() | float64 | 37.386 |
geo.Lng() | float64 | -122.084 |
geo.HasCoordinates() | bool | true — distinguishes absent from 0,0 |
geo.ASN() | string | "AS15169" |
geo.Org() | string | "Google LLC" |
You can also access the full struct directly: geo.Location.Country.Unions, geo.Location.District, geo.Meta.Precision, etc.
Helper Functions
| Function | Description |
|---|---|
netloc8.IsEU( geo ) | Check if geolocation is in the EU (checks unions for "EU") |
netloc8.IsPublicIP( ip ) | Check whether an IP is publicly routable (IPv4 and IPv6) |
netloc8.GetClientIP( r ) | Extract the real client IP from HTTP request headers |
netloc8.NormalizeIP( ip ) | Strip IPv4-mapped prefix, brackets, whitespace, lowercase |
netloc8.Subnet( ip ) | Derive the /24 CIDR prefix from an IPv4 address |
netloc8.IsIPv4( ip ) / IsIPv6( ip ) | IP version detection |
netloc8.ParseIP( ip ) | Validate and normalize an IP string |
// EU compliance check
if netloc8.IsEU( geo ) {
// show cookie consent banner, apply GDPR rules, etc.
}
// Extract client IP behind proxies
ip := netloc8.GetClientIP( r ) // checks X-Forwarded-For, CF-Connecting-IP, etc.
// Derive subnet for IP rotation
subnet := netloc8.Subnet( geo.IP() ) // "203.0.113.0/24"GetClientIP header priority
GetClientIP checks headers in order: X-Forwarded-For (first public IP), CF-Connecting-IP, True-Client-IP, X-Real-IP, X-Client-IP, Fastly-Client-IP, Fly-Client-IP.
Error Handling
API errors are returned as *netloc8.APIError with machine-readable codes. Use errors.As for typed error handling:
import "errors"
geo, err := client.LookupIP( ctx, "not-an-ip" )
if err != nil {
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 ) // "550e8400-..."
}
}Convenience predicates (work with wrapped errors via errors.As):
netloc8.IsNotFound( err ) // 404 — IP not in database
netloc8.IsRateLimited( err ) // 429 — rate limit exceeded
netloc8.IsForbidden( err ) // 403 — invalid key or origin mismatchConfiguration Options
| Option | Default | Description |
|---|---|---|
WithBaseURL( url ) | https://api.netloc8.com | Override the API base URL (for proxies or self-hosted) |
WithHTTPClient( hc ) | http.DefaultClient | Custom http.Client for proxy transports, custom TLS, etc. |
WithTimeout( d ) | 10s | Request timeout (safe to use with WithHTTPClient in any order) |
WithOrigin( origin ) | — | Origin header for publishable key (pk_) authentication |
WithUserAgent( ua ) | — | Custom User-Agent string (SDK version is appended) |
client := netloc8.NewClient( "sk_your_key",
netloc8.WithTimeout( 5 * time.Second ),
netloc8.WithBaseURL( "https://geo-proxy.internal" ),
netloc8.WithHTTPClient( &http.Client{
Transport: &http.Transport{
Proxy: http.ProxyURL( proxyURL ),
},
}),
)Concurrency safe
Client is safe for concurrent use across goroutines. Create one client and reuse it for the lifetime of your application.