GeoIP Lookup
Predictable, cacheable geographic IP lookup with explicit parameters
Explicit Parameters Design
ResolveDB uses explicit parameters for all GeoIP queries. The server never infers location from the client's IP address, providing predictability, cache efficiency, and privacy.
| Benefit | Description |
|---|---|
| Predictability | Same query returns same result from anywhere—CI, staging, production |
| Cache efficiency | No ECS fragmentation. One cached response serves all users globally |
| Privacy | Server never learns your IP. You control what data is shared |
| Compatibility | Works through DoH/DoT, VPNs, corporate proxies, Tor |
Multi-Region DNS Cluster
ResolveDB operates authoritative nameservers across multiple geographic regions:
User (NYC) User (LA) User (Berlin)
| | |
v v v
+---------+ +---------+ +---------+
| Public | | Public | | Public |
| DNS | | DNS | | DNS |
+----+----+ +----+----+ +----+----+
| | |
| Anycast / GeoDNS routing |
v v v
+---------+ +---------+ +---------+
| ns01 | | ns02 | | ns03 |
| NYC | | SFO | | AMS |
+---------+ +---------+ +---------+Multi-Region Deployment
ResolveDB runs authoritative nameservers across multiple strategic regions to minimize latency:
| Server | Location | Coverage |
|---|---|---|
| ns01.resolvedb.net | US East | Americas |
| ns02.resolvedb.net | US West | Pacific, Asia |
| ns03.resolvedb.net | Europe | EMEA |
DNS clients automatically route to the nearest available server via the resolver infrastructure.
GeoIP Lookup
GeoIP lookups require an explicit IP address parameter. The server does NOT infer from the client's source IP.
Query Format
# IPv4: use ip- prefix with dots as hyphens
dig get.ip-8-8-8-8.geoip.v1.resolvedb.net TXT +short
# IPv6: use ip- prefix, colons as hyphens, :: becomes --
dig get.ip-2001-4860-4860--8888.geoip.v1.resolvedb.net TXT +short
# HTTP API (explicit IP required)
curl "https://api.resolvedb.io/geoip?ip=8.8.8.8"Response Format
v=rdb1;s=ok;t=data;e=plain;ip=8.8.8.8;cc=US;cn=United States;rg=California;ct=Mountain View;lat=37.386;lon=-122.084;tz=America/Los_Angeles;isp=Google LLCResponse Fields
| Field | Description | Example |
|---|---|---|
ip | IP address looked up | "8.8.8.8" |
cc | Country code (ISO 3166-1) | "US" |
cn | Country name | "United States" |
rg | Region/state | "California" |
ct | City | "Mountain View" |
lat | Latitude | 37.386 |
lon | Longitude | -122.084 |
tz | Timezone | "America/Los_Angeles" |
isp | ISP name | "Google LLC" |
Why Explicit Parameters?
| Traditional GeoDNS (Broken) | ResolveDB (Explicit) |
|---|---|
| Server infers from source IP | Client provides IP in query |
| Different results from different networks | Same query = same result everywhere |
| Breaks through VPNs/proxies | Works through any network path |
| Thousands of ECS-scoped cache entries | One cached response serves all users |
| Can't test in CI (wrong location) | Testable and debuggable from anywhere |
| Server logs reveal your location | You control what data is shared |
Location-Based Queries
For weather and other location-based data, provide explicit location parameters:
City-Based Queries
dig get.city-newyork.weather.public.v1.resolvedb.net TXT +short
dig get.city-london.weather.public.v1.resolvedb.net TXT +short
dig get.city-tokyo.weather.public.v1.resolvedb.net TXT +shortCoordinate-Based Queries
# Format: lat-<lat>.lon-<lon> where decimals use 'd' separator
# NYC: 40.7128, -74.0060
dig get.lat-40d7128.lon--74d0060.weather.public.v1.resolvedb.net TXT +short
# ^^ double hyphen for negative longitudeOr use Base64-encoded JSON:
// Encode coordinates as Base64 JSON
const coords = { lat: 40.7128, lon: -74.0060 };
const encoded = btoa(JSON.stringify(coords));
const query = `get.b64-${encoded}.weather.public.v1.resolvedb.net`;Use Cases
Region-Specific Configuration
Fetch configuration for a specific region by including the region in the query:
# Fetch US-specific config
dig get.region-us.config.myapp.v1.resolvedb.net TXT +short
# Response: {"currency":"USD","language":"en-US","dateFormat":"MM/DD/YYYY"}
# Fetch EU-specific config
dig get.region-eu.config.myapp.v1.resolvedb.net TXT +short
# Response: {"currency":"EUR","language":"en-GB","dateFormat":"DD/MM/YYYY"}Edge Server Selection
Get the nearest edge server by specifying a region:
# Get edge server for US East
dig get.region-us-east.edge.cdn.v1.resolvedb.net TXT +short
# Response: {"server":"edge-nyc.example.com","region":"us-east-1"}
# Get edge server for Europe
dig get.region-eu-west.edge.cdn.v1.resolvedb.net TXT +short
# Response: {"server":"edge-ams.example.com","region":"eu-west-1"}Regional Feature Flags
Fetch feature flags for specific regions:
# EU features (GDPR compliance)
dig get.region-eu.features.myapp.v1.resolvedb.net TXT +short
# Response: {"analytics":false,"thirdPartyCookies":false,"dataExport":true}
# US features
dig get.region-us.features.myapp.v1.resolvedb.net TXT +short
# Response: {"analytics":true,"thirdPartyCookies":true,"dataExport":true}Key Principle: The client chooses which region's data to fetch. The server never infers region from the client's IP.
Private IP Handling
When looking up private/local IP ranges, the GeoIP service returns a response indicating a private network:
# Looking up a private IP address
dig get.ip-192-168-1-100.geoip.v1.resolvedb.net TXT +short
# Response: v=rdb1;s=ok;t=geo;e=plain;ip=192.168.1.100;cc=--;cn=Private Network;isp=PrivatePrivate IP detection includes:
- IPv4: 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16
- IPv6: fc00::/7 (unique local), fe80::/10 (link-local)
- Loopback: 127.0.0.0/8, ::1
Caching
- GeoIP responses use TTL of 300 seconds (5 minutes) by default
- Since queries require explicit IP parameters, responses are fully cacheable
- Same query always returns the same result (cache-friendly)
- No ECS scope fragmentation since location is explicit in query
Privacy Best Practices
For applications handling sensitive data, use multiple layers of protection:
| Layer | How to Use |
|---|---|
| Transport encryption | Query via DoH (https://dns.google/resolve?name=...) to encrypt in transit |
| Client-side encryption | Encrypt data with AES-256-GCM before storing; server never sees plaintext |
| Token privacy | Use h-<hash> prefix instead of exposing tokens in DNS query logs |
| Authenticated namespaces | Use organization namespaces (myorg.resolvedb.net) with JWT auth |
// Client-side encryption example
const key = await crypto.subtle.generateKey(
{ name: 'AES-GCM', length: 256 }, true, ['encrypt', 'decrypt']
);
const iv = crypto.getRandomValues(new Uint8Array(12));
const encrypted = await crypto.subtle.encrypt({ name: 'AES-GCM', iv }, key, sensitiveData);
// Store encrypted - server never sees plaintext
await resolvedb.put('secrets.api-key.myapp.v1', base64(iv + encrypted));Best Practices
- Explicit parameters - Always provide IP or location, never rely on implicit detection
- Cache responses - Default 5-minute TTL works for most use cases
- Handle failures - Design for cases where GeoIP data is unavailable
- Test anywhere - Same query from CI, staging, or prod returns same result
- Encrypt sensitive data - Use client-side encryption for secrets
Complete Example
// GeoIP lookup with explicit IP address
async function lookupGeoIP(ipAddress) {
// Encode IP for DNS query (dots -> hyphens)
const encodedIp = ipAddress.replace(/\./g, '-');
const query = `get.ip-${encodedIp}.geoip.v1.resolvedb.net`;
const response = await dnsQuery(query);
return parseGeoResponse(response);
}
// Location-aware configuration with explicit region
async function getRegionalConfig(region) {
const query = `get.region-${region}.config.myapp.v1.resolvedb.net`;
const response = await dnsQuery(query);
return parseUqrpResponse(response);
}
// Parse UQRP response format
function parseUqrpResponse(response) {
const dataMatch = response.match(/d=(.+)$/);
if (dataMatch) {
return JSON.parse(dataMatch[1]);
}
throw new Error('Invalid UQRP response');
}
// Usage: Client explicitly provides the IP they want to look up
const geoInfo = await lookupGeoIP('8.8.8.8');
console.log(`IP ${geoInfo.ip} is in ${geoInfo.ct}, ${geoInfo.cn}`);
// Output: IP 8.8.8.8 is in Mountain View, United States
// Client explicitly chooses which region's config to fetch
const config = await getRegionalConfig('us');
console.log(`Currency: ${config.currency}`);
// Output: Currency: USDNext Steps
- Quickstart Guide - Get started with ResolveDB
- UQRP Protocol - Learn the query format