IP lookup in a Cloudflare Worker
A small Worker that looks up the caller's IP with the ipatlas API, caches the answer at the edge and keeps your key in a secret.
Do you need this?
Workers already expose coarse location data for the visitor in request.cf. A lookup API is useful when you want the same data source and fields on every platform, or when you need to look up an address that is not the current visitor (from a form, a webhook or a log line). This example uses the visitor's address from CF-Connecting-IP; change ip to look up any other validated address.
src/index.js
export default {
async fetch(request, env) {
const ip = request.headers.get("CF-Connecting-IP");
if (!ip) return json({ error: "no client address" }, 400);
let r;
try {
r = await fetch(`https://api.ipatlas.dev/v1/${encodeURIComponent(ip)}`, {
headers: env.IPATLAS_KEY ? { "X-API-Key": env.IPATLAS_KEY } : {},
cf: { cacheTtl: 3600 }, // cache successful answers at the edge for an hour
signal: AbortSignal.timeout(2000),
});
} catch {
return json({ error: "lookup unavailable" }, 503);
}
if (r.status === 429) { // over the per-second or the quota limit
return json({ error: "lookup busy" }, 503, { "Retry-After": r.headers.get("Retry-After") ?? "60" });
}
if (!r.ok) return json({ error: "lookup failed" }, 502);
const geo = await r.json();
return json({ country: geo.country_code ?? null, city: geo.city ?? null, timezone: geo.timezone ?? null });
},
};
const json = (body, status = 200, headers = {}) =>
new Response(JSON.stringify(body), { status, headers: { "content-type": "application/json", ...headers } });
wrangler.toml
name = "ip-lookup"
main = "src/index.js"
compatibility_date = "2025-01-01"
Create a free key on the home page, then store it as a secret and deploy:
npx wrangler secret put IPATLAS_KEY
npx wrangler deploy
Notes
- Caching:
cf.cacheTtlkeeps successful responses for an hour in the Cloudflare data centre that served the request, so repeat visitors served from that data centre do not reach the API. Each data centre has its own cache, so a very spread-out audience still produces some misses. - Use a key for anything real. Without
IPATLAS_KEYthe call is keyless, and keyless limits are counted per calling address, which a Worker does not control. With a key, your 50,000 a month are counted per key. - Return only what you need. The example forwards three fields, not the whole record.
Limits: without a key, 1,000 requests per day per IP address (10 req/s); with a free key, 50,000 requests per month (50 req/s). Over a limit the API returns 429. IP geolocation is approximate: country is usually right, city often is not.
Attribution
The data is DB-IP Lite, licensed CC BY 4.0. If you show or use it in a public product, link to DB-IP, for example in your footer:
<a href="https://db-ip.com">IP Geolocation by DB-IP</a>
See the API docs for fields, limits and error codes.