Building an IP Address Query Tool: From API Calls to Geolocation Implementation#

When developing web applications, you often need to get users’ IP addresses and geolocation information—for analytics, content delivery, fraud detection, and more. I recently built an IP query tool for a side project and documented the key implementation decisions.

How IP Geolocation Actually Works#

First, let’s clarify something: IP addresses don’t contain geographic information. IP geolocation works by querying IP address attribution databases built from:

  1. WHOIS data: Registration info when IPs are assigned to ISPs or organizations
  2. BGP routing tables: Inferring location from routing paths
  3. Crowdsourced data: Reverse calculation from WiFi/cell tower located users

Accuracy is limited—city-level is typical, street-level is unreliable.

Frontend Implementation: API Selection#

The core implementation is straightforward:

interface IpInfo {
  ip: string
  country?: string
  region?: string
  city?: string
  isp?: string
  timezone?: string
  lat?: number
  lon?: number
}

async function queryIpInfo(ip: string): Promise<IpInfo | null> {
  try {
    const response = await fetch(`https://ipapi.co/${ip}/json/`)
    const data = await response.json()

    if (data.ip) {
      return {
        ip: data.ip,
        country: data.country_name,
        region: data.region,
        city: data.city,
        isp: data.org,
        timezone: data.timezone,
        lat: data.latitude,
        lon: data.longitude,
      }
    }
    return null
  } catch (e) {
    console.error('IP query failed:', e)
    return null
  }
}

Why ipapi.co?#

Several IP geolocation APIs exist. Key selection criteria:

API Free Tier HTTPS Speed Data Quality
ipapi.co 1000/day Fast High
ip-api.com 45/min ❌ (paid) Fast Medium
ipinfo.io 50000/month Fast Medium
ipgeolocation.io 30000/month Medium High

ipapi.co provides free HTTPS support—essential for frontend calls. ip-api.com’s free tier lacks HTTPS, causing mixed content blocks in browsers.

Fetching Your Own IP#

To get the client’s current IP before querying others:

const fetchMyIp = async () => {
  try {
    const response = await fetch('https://ipapi.co/json/')
    const data = await response.json()
    setMyIp(data.ip)
  } catch (e) {
    console.error('Failed to fetch IP:', e)
  }
}

No IP parameter needed—the server returns the client IP from the request.

Handling IPv4 and IPv6#

IPv6 adoption is growing. Basic validation:

// IPv4 format
const ipv4Regex = /^(\d{1,3}\.){3}\d{1,3}$/

// IPv6 format (simplified)
const ipv6Regex = /^([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$/

function validateIp(ip: string): boolean {
  return ipv4Regex.test(ip) || ipv6Regex.test(ip)
}

Full IPv6 regex is complex due to shorthand forms (:: for consecutive zeros). Use a library like ip-regex in production.

Error Handling & User Experience#

Robust error handling is essential:

const handleQuery = async () => {
  if (!ip.trim()) {
    setError('Please enter an IP address')
    return
  }

  setLoading(true)
  setError('')

  try {
    const result = await queryIpInfo(ip)
    if (result) {
      setResult(result)
    } else {
      setError('Query failed. Please check the IP address format.')
    }
  } catch (e) {
    setError('Network error. Please try again.')
  } finally {
    setLoading(false)
  }
}

UX improvements:

  • Enter key support: onKeyDown={(e) => e.key === 'Enter' && handleQuery()}
  • Loading feedback: button shows “Querying…” and disables
  • Specific error messages: distinguish format vs network errors

Performance & Rate Limiting#

Free APIs have rate limits. Consider these strategies:

1. Frontend Caching#

Cache results in localStorage:

const cacheKey = `ip-info-${ip}`
const cached = localStorage.getItem(cacheKey)
if (cached) {
  setResult(JSON.parse(cached))
  return
}

// After API call
localStorage.setItem(cacheKey, JSON.stringify(result))

2. Debouncing#

For real-time queries, debounce input:

import { debounce } from 'lodash'

const debouncedQuery = useMemo(
  () => debounce((value: string) => {
    if (validateIp(value)) {
      handleQuery(value)
    }
  }, 500),
  []
)

3. Backend Proxy (Advanced)#

For high volume, use backend proxy + cache:

Client → Your Backend → IP API
           ↑
        Redis Cache

This reduces API calls and hides your API key.

Privacy & Compliance#

IP addresses are personal data under GDPR. Best practices:

  1. Notify users: Clearly state IP collection
  2. Minimize data: Only collect what’s needed
  3. Protect data: Don’t store raw IPs
// Masking example
const maskIp = (ip: string) => {
  return ip.replace(/(\d+\.\d+\.)\d+\.\d+/, '$1***.***')
}
// 192.168.1.100 → 192.168.***.***

Final Result#

Built an online tool based on these principles: IP Address Query

Features:

  • Auto-detect your IP
  • Query any IPv4/IPv6 address
  • Display country, region, city, ISP, timezone, coordinates
  • One-click detail lookup for your IP

Implementation isn’t complex, but getting the details right requires thoughtful API selection, error handling, performance optimization, and privacy compliance.


Related tools: IP Subnet Calculator | Port Checker