Skip to content

IP geolocation API

One request. You have an IP address, or you want the one making the request. This returns a country, a region, a city and a coordinate.

Every request needs an API key; see Authentication. A successful lookup is one normal API call. There is no separate plan to turn on.

live result
the code that produced it
import { Unmap } from "@unmap/sdk";

const unmap = new Unmap({ key: "um_live_..." });

const here = await unmap.geoip.locate();

if (here.found) console.log(here.city, here.country_code);
// Show here.attribution.text, linked to here.attribution.url.
curl "https://api.unmap.dev/geoip?ip=8.8.8.8&key=um_live_..."
const here = await unmap.geoip.locate("8.8.8.8");

Lookup

GET /geoip
ParameterRequiredDescription
ipnoAn IPv4 or IPv6 address. Omit it to look up the address that made the request.
{
  "ip": "8.8.8.8",
  "found": true,
  "country_code": "US",
  "country": "United States",
  "region": "California",
  "city": "Mountain View",
  "coordinates": [-122.085, 37.422],
  "accuracy": "city",
  "release": "2026-11",
  "attribution": { "text": "IP Geolocation by DB-IP", "url": "https://db-ip.com" }
}

coordinates is [longitude, latitude], the same order as the rest of the API. ip is the canonical form of the address that was looked up: dotted IPv4, or IPv6 in its shortest form. An IPv4-mapped IPv6 address comes back as IPv4. release is the month of the database that answered. region and city are null when the database does not have them.

Your own address

const here = await unmap.geoip.locate();

With no ip, the answer is for the machine making the request. From a browser that is the visitor. From a server it is the server. A request that goes through a proxy is located at the proxy, so a server that wants its visitor's location must pass the visitor's address as ip.

Addresses with no answer

An address the database does not list, such as unassigned IPv6 space, is 200 with found: false and no location fields.

{ "ip": "1000::1", "found": false, "release": "2026-11", "attribution": { "text": "IP Geolocation by DB-IP", "url": "https://db-ip.com" } }

Private, loopback, link-local and other reserved ranges are not looked up. They are 400 invalid_ip, because there is nothing to find. NAT64 (64:ff9b::/96), 6to4 (2002::/16) and IPv4-mapped addresses are read through to the IPv4 address inside them first, so ::ffff:10.0.0.1 is rejected the same way 10.0.0.1 is.

Accuracy

Treat the answer as the area an internet provider serves, not a place. It is often only the right country, or a nearby city, and mobile networks, VPNs and corporate networks can place a person hundreds of kilometres from where they are. Use it to pick a language, a currency or a starting map view. Do not use it to identify a person or a street address.

Errors

StatusCodeWhen
400invalid_ipip is not an IPv4 or IPv6 address, or it is private or reserved. Also when you omitted it and the caller's address could not be read, or is private. Pass ip.
400bad_requestYou omitted ip and there is no caller address to default to.
503service_unavailableThe dataset is missing, unreadable or invalid. Retry later.
{ "error": "ip must be an IPv4 or IPv6 address", "code": "invalid_ip" }

Only a 200 is billed. Every 400 and 503 is free.

Privacy

The address you look up is not logged, stored or counted; the request itself counts as one call, like any other. Usage rows carry the endpoint and nothing else, and the address is not part of any cache key. Responses carry Cache-Control: private, max-age=300: your browser may reuse one for five minutes, and no shared cache stores it.

Attribution

The data is the IP to City Lite database from DB-IP, under CC BY 4.0. Every 200 carries attribution. Show attribution.text, linked to attribution.url, wherever your product shows or uses the result. The attribution page has the full credit.

Coverage and updates

The lookup is worldwide. Every other Unmap API is Canadian-first; this one is not. DB-IP publishes a new file each month. It is checked weekly, built into small immutable objects, and promoted by one manifest write, so a bad release is rolled back by pointing at the last one. If a refresh fails, the Worker keeps serving the previous release for up to an hour before it answers 503.