On this page
Developer Guide: IP Lookup Implementation
A collection of code examples and API references for fetching public IP addresses, geolocation data, and network information in your own applications.
Quick API Comparison
| API | Endpoint | Data | Rate Limit |
|---|---|---|---|
| ipify | api.ipify.org?format=json | IP only | Generous free tier |
| ipinfo.io | ipinfo.io/json | IP, location, ISP, ASN, org | 50k req/month free |
| Cloudflare DNS | cloudflare-dns.com/dns-query | DNS records only | No auth required |
1. Fetch Public IP in JavaScript
The simplest way to get a user's public IP address from the browser is using a free API like ipify. This requires no API key and works over HTTPS.
// Fetch public IP only
const res = await fetch('https://api.ipify.org?format=json');
const data = await res.json();
console.log(data.ip); // "203.0.113.1" Limitations: ipify only returns the IP address. For geolocation data (city, region, ISP), you need a second API call or a provider like ipinfo.io.
CORS: Both ipify and ipinfo.io support CORS, so they work directly from browser fetch() without a proxy.
2. Get Geolocation from IP
For location data, ISP, and ASN information, ipinfo.io is a popular free option:
const res = await fetch('https://ipinfo.io/json');
const data = await res.json();
console.log({
ip: data.ip, // "203.0.113.1"
city: data.city, // "Mountain View"
region: data.region, // "California"
country: data.country,// "US"
loc: data.loc, // "37.4056,-122.0775"
org: data.org, // "AS15169 Google LLC"
timezone: data.timezone, // "America/Los_Angeles"
postal: data.postal, // "94043"
}); To look up a specific IP address instead of the current user's:
const res = await fetch('https://ipinfo.io/8.8.8.8/json');
const data = await res.json();
console.log(data); Accuracy note: IP geolocation is based on ISP registration data, not GPS. It is typically accurate to the city level in urban areas but can be hundreds of miles off in rural regions.
3. IPv4 vs IPv6 Detection
Most IP APIs return whichever address version the request came from. If you need to detect whether a user is on IPv4 or IPv6, you can check the format of the returned IP:
function detectIpVersion(ip) {
if (ip.includes(':')) return 'IPv6';
if (/^\d+\.\d+\.\d+\.\d+$/.test(ip)) return 'IPv4';
return 'Unknown';
}
const res = await fetch('https://api.ipify.org?format=json');
const { ip } = await res.json();
console.log(detectIpVersion(ip)); // "IPv4" For robust detection that supports both protocols, consider using a dual-stack endpoint or making requests to both IPv4 and IPv6-specific endpoints with a fallback.
4. Client-Side vs Server-Side IP Fetching
When building an IP lookup tool, you can fetch the IP on the client side (in the browser) or on the server side (during SSR or API request). Each approach has trade-offs:
Client-side (browser fetch):
- Pro: Always gets the user's real IP. Simple to implement. No server load.
- Pro: Can show loading states and update dynamically.
- Con: Requires JavaScript. Not available in server-rendered HTML.
- Con: Subject to CORS restrictions (most public APIs support CORS).
Server-side (API route / SSR fetch):
- Pro: IP data is available during page render (SEO-friendly).
- Pro: Can hide API keys and control rate limiting.
- Con: Gets the server's IP, not the user's, unless you forward the
X-Forwarded-Forheader. - Con: More complex to set up with proper error handling.
Best practice: Use a hybrid approach — show a server-rendered skeleton or cached data, then hydrate with the real client-side IP using JavaScript. This is the pattern used by IPLookup itself (server-rendered shell with client:idle React islands).
5. AstroJS IP Lookup Implementation
This website (IPLookup) is built with Astro and uses the following architecture:
A React component (LookupTool) is embedded in the Astro page using client:idle. On hydration, it calls fetchIpData() which tries ipinfo.io first, falls back to ipify if that fails, and returns structured data including location, ISP, ASN, and connection type.
// src/lib/ip-api.ts (simplified)
export async function fetchIpData() {
try {
const res = await fetch('https://ipinfo.io/json');
const data = await res.json();
return {
ip: data.ip,
city: data.city,
region: data.region,
country: data.country,
loc: data.loc,
org: data.org,
timezone: data.timezone,
};
} catch {
// Fallback: IP-only
const res = await fetch('https://api.ipify.org?format=json');
const { ip } = await res.json();
return { ip };
}
} Page integration (Astro):
---
// src/pages/lookup/index.astro
import Layout from '../../layouts/Layout.astro';
import { LookupTool } from '../../components/LookupTool';
---
<Layout title="IP Lookup Tool">
<LookupTool client:idle />
</Layout> Key considerations when implementing IP lookup in Astro:
- Island architecture: Use
client:idlefor above-fold components that need fast interactivity,client:visiblefor below-fold content like maps. - Lazy loading: Heavy dependencies like Leaflet (map) should be lazy-loaded with
React.lazy()+Suspenseto reduce initial bundle size. - Error handling: Always implement fallback APIs and timeout handling (8s timeout in IPLookup using AbortController).
- No server storage: Fetch APIs from the client side to avoid storing user IPs on your server — this simplifies privacy compliance.
6. Full Example: Minimal IP Lookup Page
A minimal standalone HTML page that fetches and displays IP data:
<!DOCTYPE html>
<html>
<body>
<h1 id="ip">Loading…</h1>
<p id="loc"></p>
<script>
Promise.all([
fetch('https://api.ipify.org?format=json'),
fetch('https://ipinfo.io/json'),
])
.then(async ([ipRes, geoRes]) => {
const { ip } = await ipRes.json();
const geo = await geoRes.json();
document.getElementById('ip').textContent = ip;
document.getElementById('loc').textContent =
`${geo.city}, ${geo.region}, ${geo.country}`;
})
.catch(() => {
document.getElementById('ip').textContent = 'Failed to load';
});
</script>
</body>
</html> 7. Privacy-First IP Lookup
If you are building an IP lookup tool, consider these privacy best practices:
- Fetch from the browser, not your server. This way, user IPs never touch your infrastructure.
- No logging. Do not log IP addresses or store them in databases.
- No cookies. Do not use tracking cookies or analytics that capture IPs.
- Use HTTPS. Ensure all API calls use HTTPS to prevent eavesdropping.
- Explain clearly. Tell users what data is fetched and that it is not stored.
This site (IPLookup) follows all of these practices — see our privacy policy for details.
8. CLI & curl Examples
For quick debugging or scripting, use curl to fetch IP data directly from the terminal:
# Get your public IP only
curl https://api.ipify.org?format=json
# Get full geolocation data (IP, city, region, country, ISP)
curl https://ipinfo.io/json
# Look up a specific IP address
curl https://ipinfo.io/8.8.8.8/json
# Query DNS A records for a domain
curl -H "Accept: application/dns-json" \
"https://cloudflare-dns.com/dns-query?name=example.com&type=A"
# Pretty-print with jq
curl -s https://ipinfo.io/json | jq . Tip: Pipe the output through jq for formatted, colorized JSON. Install jq with brew install jq (macOS) or apt install jq (Linux).
9. React & Vue Component Examples
Below are minimal components for React and Vue that you can drop into your own projects to display the current user's IP and location.
React (TypeScript):
import { useEffect, useState } from 'react';
interface IpData {
ip: string;
city?: string;
region?: string;
country?: string;
org?: string;
}
export function IpLookup() {
const [data, setData] = useState<IpData | null>(null);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 8000);
fetch('https://ipinfo.io/json', { signal: controller.signal })
.then<IpData>((r) => r.json())
.then(setData)
.catch(() => {
fetch('https://api.ipify.org?format=json')
.then<{ ip: string }>((r) => r.json())
.then((d) => setData({ ip: d.ip }))
.catch(() => setError('Failed to load'));
})
.finally(() => clearTimeout(timeout));
return () => controller.abort();
}, []);
if (error) return <p>{error}</p>;
if (!data) return <p>Loading…</p>;
return (
<div>
<p><strong>IP:</strong> {data.ip}</p>
{data.city && <p><strong>Location:</strong> {data.city}, {data.region} {data.country}</p>}
{data.org && <p><strong>ISP:</strong> {data.org}</p>}
</div>
);
} Vue 3 (Composition API):
<template>
<div>
<p v-if="error">{{ error }}</p>
<p v-else-if="!data">Loading…</p>
<div v-else>
<p><strong>IP:</strong> {{ data.ip }}</p>
<p v-if="data.city">
<strong>Location:</strong> {{ data.city }}, {{ data.region }} {{ data.country }}
</p>
<p v-if="data.org"><strong>ISP:</strong> {{ data.org }}</p>
</div>
</div>
</template>
<script setup>
import { ref, onMounted, onUnmounted } from 'vue';
const data = ref(null);
const error = ref(null);
onMounted(() => {
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 8000);
fetch('https://ipinfo.io/json', { signal: controller.signal })
.then((r) => r.json())
.then((d) => { data.value = d; })
.catch(() => {
fetch('https://api.ipify.org?format=json')
.then((r) => r.json())
.then((d) => { data.value = { ip: d.ip }; })
.catch(() => { error.value = 'Failed to load'; });
})
.finally(() => clearTimeout(timeout));
onUnmounted(() => controller.abort());
});
</script> Both examples include timeout handling (8s) and a fallback to ipify if ipinfo.io fails.
10. DNS Lookup API
IPLookup's DNS Lookup tool uses Cloudflare's DNS-over-HTTPS (DoH) API, which requires no API key and supports all major record types.
// Base endpoint with Accept header
const BASE = 'https://cloudflare-dns.com/dns-query';
// Query A records for a domain
const res = await fetch(`${BASE}?name=example.com&type=A`, {
headers: { Accept: 'application/dns-json' },
});
const data = await res.json();
// Supported record types
const TYPES = ['A', 'AAAA', 'MX', 'CNAME', 'NS', 'TXT', 'SOA', 'SRV']; See our interactive DNS Lookup Tool for a live implementation of this API.
11. Node.js Server-Side IP Lookup
When building a server-rendered app (e.g., Express, Next.js, or Astro SSR), you can fetch the IP on the server side. However, you must forward the client's real IP via X-Forwarded-For if your app is behind a proxy.
import express from 'express';
const app = express();
app.get('/api/ip-lookup', async (req, res) => {
const clientIp = req.headers['x-forwarded-for']
?.split(',')[0]
?.trim()
|| req.ip;
try {
const response = await fetch(`https://ipinfo.io/${clientIp}/json`);
const data = await response.json();
res.json(data);
} catch {
res.status(502).json({ error: 'IP lookup failed' });
}
});
app.listen(3000); Next.js API Route:
// pages/api/ip.ts (Next.js)
import type { NextApiRequest, NextApiResponse } from 'next';
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
const forwarded = req.headers['x-forwarded-for'];
const ip = typeof forwarded === 'string'
? forwarded.split(',')[0].trim()
: req.socket.remoteAddress;
try {
const response = await fetch(`https://ipinfo.io/${ip}/json`);
const data = await response.json();
res.status(200).json(data);
} catch {
res.status(502).json({ error: 'Failed to fetch IP data' });
}
} Important: When deploying to platforms like Vercel, Netlify, or Cloudflare, the client IP is usually available via a platform-specific header (x-forwarded-for, cf-connecting-ip, etc.). Always trust the leftmost IP in x-forwarded-for, as it is the original client.
Related Resources
- DNS Record Lookup Tool — Query DNS records for any domain
- Networking Glossary — Definitions of IP and networking terms
- ipinfo.io API Docs — Official API documentation
- Cloudflare DNS-over-HTTPS — DNS resolution API