DEV Community

Vix
Vix

Posted on

Best free IP APIs: server-side vs client-side — they're not the same problem

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)
Enter fullscreen mode Exit fullscreen mode

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()
Enter fullscreen mode Exit fullscreen mode

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();
}
Enter fullscreen mode Exit fullscreen mode

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/
Enter fullscreen mode Exit fullscreen mode
// Less useful client-side — you'd still need to parse it
// and you get no geo data, so just use the JSON endpoint instead
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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)