# Install
go get github.com/ipscope-io/ipscope-go# Quickstart: single, batch & prefix lookups
Pass your API key explicitly or set IPSCOPE_KEY in your environment. Every lookup returns the canonical response schema with geo, network, connection, security and security_extensions.
package main
import (
"context"
"fmt"
"log"
"os"
"github.com/ipscope-io/ipscope-go"
)
func main() {
client := ipscope.NewClient(ipscope.Config{
APIKey: os.Getenv("IPSCOPE_KEY"),
})
defer client.Close()
ctx := context.Background()
// 1. Single IPv4 or IPv6 lookup
info, err := client.LookupIP(ctx, "198.51.100.7", nil)
if err != nil {
log.Fatal(err)
}
fmt.Printf("%s (%s) ASN=%d VPN=%v\n",
info.IP, *info.Geo.CountryCode, *info.Network.ASN, info.Security.IsVPN)
// 2. Batch lookup (up to 100 IPs; deduplicated & cached)
batch, err := client.LookupBatch(ctx, []string{"198.51.100.7", "1.1.1.1"}, nil)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Resolved %d records\n", batch.Count)
// 3. Network CIDR lookup
netInfo, err := client.LookupNetwork(ctx, "198.51.100.0/24")
if err != nil {
log.Fatal(err)
}
fmt.Printf("Prefix %s -> AS%d (%s)\n", netInfo.QueriedCIDR, *netInfo.Record.ASN, *netInfo.Record.Organization)
}1. Single IPv4/IPv6 lookup response (GET /v1/ip/{ip})
{
"ip": "198.51.100.7",
"ip_version": 4,
"matched_network": "198.51.100.0/24",
"geo": {
"country_code": "US",
"declared_country_code": "US",
"physical_country_code": "US",
"is_virtual_location": false,
"location_type": "physical",
"confidence": 0.94,
"subdivision": "Virginia",
"city": "Ashburn",
"postal_code": "20147",
"latitude": 39.0438,
"longitude": -77.4874,
"accuracy_radius_km": 10,
"timezone": "America/New_York"
},
"network": {
"asn": 64501,
"organization": "Example Cloud LLC"
},
"connection": {
"connection_type": "hosting",
"is_hosting": true,
"is_residential": false,
"is_mobile": false,
"is_business": false
},
"security": {
"is_vpn": true,
"is_hosting": true,
"is_tor_exit": false,
"is_proxy": false,
"anonymizer_type": "vpn",
"confidence": 0.96,
"reason_codes": [
"vpn_handshake_confirmed",
"datacenter_asn_range"
]
},
"security_extensions": {
"abuse_velocity_score": 18,
"is_known_scanner": false,
"observation_recency_hours": 12,
"anonymizer_reason": "vpn_handshake_confirmed",
"threat_level": "medium",
"abuse_velocity": "low",
"confidence": 0.96
},
"dataset_version": "2026-10-27T00:00:00Z.crawl-1793088000000",
"updated_at": "2026-10-27T00:00:00Z"
}4. Network CIDR prefix lookup response (GET /v1/networks/{prefix}/{length})
{
"queried_cidr": "198.51.100.0/24",
"family": 4,
"dataset_version": "2026-10-27T00:00:00Z.crawl-1793088000000",
"record": {
"prefix": "198.51.100.0/24",
"family": 4,
"asn": 64501,
"organization": "Example Cloud LLC",
"countryCode": "US",
"city": "Ashburn",
"locationType": "physical",
"isVpn": true,
"isHosting": true,
"isTorExit": false,
"isProxy": false,
"anonymizerType": "vpn"
}
}# Architecture & runtime guarantees
- Zero external dependencies (
net/http,encoding/json,context,synconly), resulting in zero supply-chain bloat ingo.mod. - Thread-safe doubly-linked list + hash map LRU cache protected by a fine-grained mutex.
LookupBatchStreamcallback API processes arbitrarily large IP slices in 100-IP chunks without allocating intermediate slice copies.- Context-aware cancellation and
io.LimitReaderresponse-body bounding (2 MiBceiling) on every HTTP call.
# Reading local MMDB files
If you subscribe to a downloadable database, our .mmdb snapshots open directly in standard MaxMind DB readers with sub-microsecond in-memory lookups:
db, err := maxminddb.Open("/var/lib/ipscope/ipscope-complete.mmdb")
if err != nil {
log.Fatal(err)
}
defer db.Close()
var record map[string]any
err = db.Lookup(net.ParseIP("198.51.100.7"), &record)# Plain HTTP fallback
Need to call the REST API without installing a package? Use Go’s standard HTTP client directly:
req, _ := http.NewRequestWithContext(ctx, http.MethodGet,
"https://api.ipscope.io/v1/ip/198.51.100.7", nil)
req.Header.Set("Authorization", "Bearer "+os.Getenv("IPSCOPE_KEY"))
res, err := http.DefaultClient.Do(req)
if err != nil {
return err
}
defer res.Body.Close()
var info map[string]any
err = json.NewDecoder(res.Body).Decode(&info)