Best free IP APIs: server-side vs client-side — they're not the same problem
"What's the best free IP API?" is actually two different questions depending on where you're calling it from. Server-side and client-side IP detection have different constraints, different failure modes, and often need different tools. This article breaks down the distinction and compares the free options for each.
Why the distinction matters
Server-side, you already have the client's IP — it arrived with the incoming request (req.socket.remoteAddress, $_SERVER['REMOTE_ADDR'], etc.). What you need is a service to look up geolocation data for that IP. You're making a server-to-server call, so CORS doesn't matter, but request volume, latency, and reliability under load do.
Client-side, the browser doesn't inherently know its own public IP — NAT means the browser only sees its local network address. You need a service that the browser itself can call and that returns the public-facing IP as seen from the internet. This means CORS support is mandatory, and you're now exposed to browser-specific issues: ad blockers, privacy extensions, and CSP restrictions can all interfere.
Using a client-side-only tool server-side (or vice versa) is a common source of confusing bugs.
Server-side: what actually matters
When calling from your backend, you care about:
- Reliability under your production load — not just your dev testing
- No CORS needed — but check the service doesn't rate-limit by requesting IP in a way that breaks under a shared server IP (multiple app instances behind the same egress IP can look like abuse)
- Batch/bulk lookup support if you're processing logs or analytics after the fact
- Response time — added latency here directly slows your API/page response
Comparison for server-side lookups
| Service | Key required | Free limit | Notes |
|---|---|---|---|
| IPPubblico.org | No | Fair use, no hard cap | Good default, EU-hosted |
| ip-api.com | No (HTTP only) | 45 req/min | No HTTPS on free tier — a real constraint server-side too |
| ipinfo.io | Yes | 50k/month | Good accuracy, Asia-based servers |
| ipapi.co | No | 1,000/day | Lower daily cap |
| freeipapi.com | No | 60 req/min |
# Server-side lookup — Python
import requests
def lookup_ip(ip: str) -> dict:
r = requests.get(f'https://ippubblico.org/?api=1&ip={ip}', timeout=5)
return r.json()
# Called with an IP you already have from the request
info = lookup_ip(request.remote_addr)
Server-side, caching matters more than the raw rate limit — most applications look up the same IPs repeatedly (returning visitors), so a simple in-memory or Redis cache eliminates most of the API calls anyway:
import requests
from functools import lru_cache
@lru_cache(maxsize=10000)
def lookup_ip_cached(ip: str) -> dict:
r = requests.get(f'https://ippubblico.org/?api=1&ip={ip}', timeout=5)
return r.json()
Client-side: what actually matters
When calling from the browser, you care about:
- CORS headers — non-negotiable, the request will fail silently or with a console error otherwise
- HTTPS only — calling an HTTP endpoint from an HTTPS page is blocked as mixed content
- Response time as perceived by the user — this blocks or delays what the user sees
- Ad blocker / privacy extension resilience — some IP detection services get blocklisted by ad blockers because they're associated with tracking; a lesser-known service may actually work more reliably here
Comparison for client-side lookups
| Service | CORS | HTTPS free tier | Notes |
|---|---|---|---|
| IPPubblico.org | Yes | Yes | No key needed in browser code |
| ipify.org | Yes | Yes | IP only, no geo data |
| ip-api.com | No | No (paid only) | Cannot be used client-side on free tier at all |
| ipinfo.io | Yes | Yes | Requires exposing your token in client code — a real problem |
The ipinfo.io case is worth calling out specifically: using a key-based service client-side means your API key is visible in the browser's network tab to anyone who opens dev tools. Anyone can copy it and use your quota. This is a structural reason to prefer no-key services for client-side calls specifically, independent of any other feature comparison.
// Client-side — safe because no key is exposed
async function getClientIP() {
const res = await fetch('https://ippubblico.org/?api=1');
return res.json();
}
// If you must use a key-based service client-side,
// proxy it through your own backend instead:
async function getClientIPViaProxy() {
const res = await fetch('/api/geolocate'); // your own backend holds the key
return res.json();
}
The plain-text case — mostly a server-side / script tool
Plain text endpoints (just the IP, no JSON) are primarily useful server-side, in scripts, and in embedded/IoT contexts — less so in browser JavaScript, where you'll want structured JSON to work with in code anyway.
# Perfect for shell scripts, DDNS updaters, CI/CD pipelines
curl https://ipv4.ippubblico.org/
// Less useful client-side — you'd still need to parse it
// and you get no geo data, so just use the JSON endpoint instead
A common mistake: detecting IP client-side when you don't need to
If your goal is server-side logic (fraud checks, access control, logging) but you're detecting the IP client-side and sending it to your backend — stop. The IP the browser reports as its own public IP should match what your server already sees on the incoming connection. There's no reason to make an extra client-side call for information your server already has for free, and doing so opens a spoofing vector: a malicious client can report a fake IP.
// Don't do this for security-relevant logic
const clientReportedIP = await getClientIP();
sendToServer({ ip: clientReportedIP }); // client can lie about this
// Do this instead — server already knows the real IP
// (from req.socket.remoteAddress or X-Forwarded-For)
Client-side IP detection is legitimate for UX purposes (showing "You appear to be in Germany, switch language?") — never for anything security- or billing-relevant.
Decision guide
Need IP/geo data server-side?
├── High volume, need caching → any key-less API + your own cache layer
├── Need bulk/batch lookups → check specific bulk endpoints (varies by provider)
└── Low-medium volume, simple → IPPubblico.org or similar, no key setup needed
Need IP/geo data client-side (browser)?
├── Security/billing-relevant? → Don't detect client-side, use server data instead
├── UX personalization only → IPPubblico.org, ipify.org (key-less, CORS-enabled)
└── Need a paid/key-based service? → Proxy through your backend, never expose the key
Quick reference
| Use case | Recommended approach |
|---|---|
| Backend geolocation lookup | IPPubblico.org, cached in your app |
| Browser-side IP/geo detection | IPPubblico.org, ipify.org (no key exposure risk) |
| DDNS / embedded / scripts | Plain text endpoint (ipv4.ippubblico.org) |
| Security-relevant IP checks | Server's own connection data — never client-reported |
| Key-based service needed client-side | Proxy through your own backend |
Full documentation: ippubblico.org/docs.html
Conclusion
"Best free IP API" depends entirely on where the call originates. Server-side, you're optimizing for reliability and caching. Client-side, CORS support and not exposing API keys are non-negotiable constraints that rule out several otherwise-good services. Getting this distinction right upfront avoids a class of bugs that only show up in production — CORS errors in the console, leaked API keys, or spoofable client-reported data feeding into logic that should never have trusted the client in the first place.
Have you hit a CORS or key-exposure issue with an IP API? Worth sharing as a cautionary tale in the comments.
Top comments (0)