How to Locate Visitors from Your Backend by Passing the Client IP

API or service
IP Geolocation API
Task
Incoming request → client IP → approximate location → cache
Examples
URLAI
Difficulty
Intermediate
Time
10 min

Your backend handles a visitor's request and needs the visitor's approximate location: the country for prices, language, or legal texts, or the city and region for local content. The Geoapify IP Geolocation API returns the location of an IP address. Called from the backend, it would see your server's IP address, so pass the visitor's address in the ip parameter.

Read the client IP address from the incoming request, skip addresses that are not public, send the rest to the API, and cache the result. The API key stays on the server.

Task flow: incoming request → client IP address → IP Geolocation API with ip → approximate location → cached result for later requests.

Get the client IP address

Where the visitor's IP address comes from depends on what sits between the browser and your application:

Setup Where the client IP address is Notes
The application receives connections directly The connection's remote address, for example req.socket.remoteAddress in Node.js Nothing else can change it.
A reverse proxy or load balancer you run, such as nginx or a cloud load balancer The X-Forwarded-For header, or the standard Forwarded header (RFC 7239) The connection's remote address is the proxy, not the visitor.
A CDN in front of the site The header your CDN documents for the client address, or X-Forwarded-For Check your CDN's documentation for the header name.

X-Forwarded-For holds a comma-separated list. Each proxy appends the address it received the request from, so the list reads client, proxy1, proxy2. A visitor can send their own X-Forwarded-For header with any value, which ends up at the start of the list. Read the address added by your own outermost proxy: count trusted proxies from the right, or use your framework's trusted-proxy setting, such as trust proxy in Express or ProxyFix in Flask. Never trust the header when your application can be reached without the proxy.

Before you call the API:

  • Normalize the address. IPv4 clients can appear as IPv4-mapped IPv6 addresses, such as ::ffff:129.6.15.28. Send the IPv4 part, 129.6.15.28. The API accepts both IPv4 and IPv6 addresses.
  • Skip addresses that are not public. Loopback addresses (127.0.0.1, ::1) and private networks (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, fc00::/7) don't identify a place on the internet. Use your default instead of calling the API. In local development, the client address is usually one of these, so test with a fixed public address.
  • Validate the value. Send only a parsed IP address, never the raw header. An invalid ip value returns HTTP 400.

Request the location for the client IP

Send the client IP address in ip. The key comes from the server's configuration, for example an environment variable, and never reaches the browser:

https://api.geoapify.com/v1/ipinfo?ip=129.6.15.28&apiKey=YOUR_API_KEY

This example looks up 129.6.15.28, a public NIST time server that NIST lists in Gaithersburg, Maryland.

Reduced response: The names objects and some country fields are left out.

{
  "ip": "129.6.15.28",
  "city": { "name": "Gaithersburg" },
  "state": { "name": "Maryland" },
  "postal": { "code": "20899" },
  "country": {
    "name": "United States",
    "iso_code": "US",
    "phone_code": "1",
    "capital": "Washington D.C.",
    "flag": "🇺🇸",
    "languages": [
      { "iso_code": "en", "name": "English", "name_native": "English" }
    ]
  },
  "continent": { "code": "NA", "name": "North America" },
  "location": { "latitude": 39.1402, "longitude": -77.2185 }
}

Check ip in the response: it is the address that was looked up. If it is your server's address, the ip parameter didn't reach the API.

Use the fields your feature needs, for example country.iso_code for prices or legal texts, and city.name or state.name for local content. Any field can be missing when there is no data for it, so keep a default for each one. The location is approximate, usually at city level: it is not the visitor's exact position and not a street address. See the API reference for every field.

Status Meaning What the backend does
200 The lookup succeeded. Use the fields, with defaults for missing ones.
400 The ip value isn't a valid IP address. Use the default; check how the address is read and normalized.
401 The API key is missing or invalid. Use the default and alert the team.
429 Too many requests. Use the default; retry later, not within the visitor's request.
500 Internal server error. Use the default.

Cache, time out, and protect the data

Cache by IP address. Each request costs one credit; see IP Geolocation pricing. You can cache and store IP Geolocation results. One visitor sends many requests, so look up each IP address once and reuse the result:

  • Key the cache by the normalized IP address and keep entries for a limited time, for example a day. Addresses get reassigned, so don't keep results forever.
  • Cache only successful lookups. After a failure, use the default and try again on a later request.
  • When several requests from a new address arrive at once, send one lookup and let the others wait for it.

Don't let the lookup slow down the page. Set a short timeout, for example one or two seconds, and fall back to your default when it expires. When the location only personalizes the page, you can also run the lookup in the background and use it from the next request on.

Treat the IP address as personal data. In many jurisdictions, an IP address can identify a person. Keep only what the feature needs: store the derived country or city instead of the raw address when that is enough, keep cache entries short, and describe the lookup in your privacy notice. Follow your own legal requirements; this guide is not legal advice.

Let users override it. Offer a country or language switch and keep the visitor's choice, for example in a cookie or profile setting. Use the IP result only when there is no choice yet.

Backend IP lookup with an AI coding agent

Copy this prompt into Codex, Claude Code, Cursor, or another coding agent to add a visitor location lookup to your backend.

Add a visitor location lookup to my backend with the Geoapify IP Geolocation API: read the client IP address from the incoming request, look it up, and make the approximate location available to the rest of the request handling.

Before you start
- Inspect the existing project first. Reuse its current framework, HTTP client, cache, configuration, logging, and conventions. Make the smallest necessary changes.
- Ask only what the project doesn't answer: which proxies, load balancers, or CDN sit in front of the application, which location fields the feature needs (for example country.iso_code, city.name), and the default when no location is available.
- Read the API key from GEOAPIFY_API_KEY. Never send it to the browser, print it, or log it.

Client IP address
- Read the client address from the connection when there is no proxy. Behind proxies, use the framework's trusted-proxy setting or count trusted proxies from the right of X-Forwarded-For; never take the left-most value of a header the client can set.
- Normalize IPv4-mapped IPv6 addresses (::ffff:a.b.c.d) to IPv4. Skip loopback and private addresses (127.0.0.0/8, ::1, 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, fc00::/7) and use the default instead.

API flow
1. Send GET https://api.geoapify.com/v1/ipinfo?ip=<client IP>&apiKey=... Always pass ip: without it, the API looks up the server.
2. Read only the fields the feature needs. Every field can be missing; fall back to the default.
3. Check that the response ip equals the address you sent.

Requirements
- Cache successful results by normalized IP address for a limited time (for example 24 hours); don't cache failures; deduplicate concurrent lookups for the same address.
- Use a short timeout (1–2 seconds) and fall back to the default on timeout, 400, 401, 429, or 500. Never block the response on a retry.
- Keep a user's explicit country or language choice above the IP result.
- Store the derived location rather than the raw IP address when that is enough, and add tests with a mocked response, a private address, and a spoofed X-Forwarded-For header.

Review the generated code against this guide. Check that it trusts only your own proxies, skips private addresses, always passes ip, caches successful lookups, times out quickly, and keeps the API key on the server.