country.is is a lovely, minimal free API: give it an IP, get back the country code. If you ever need more than the code — the country's name, flag, currency or calling code — countries.dev does the same lookup, also free and keyless, and returns the whole record.
The same lookup, no key
curl https://countries.dev/ip # the caller's own IP
curl https://countries.dev/ip/8.8.8.8 # any IP{
"ip": "8.8.8.8",
"countryCode": "US",
"country": { "name": "United States of America", "flag": "🇺🇸", "currencies": [{ "code": "USD" }] }
}Like country.is, there's no API key and no sign-up, and CORS is open so you can call it straight from the browser.
You get the country, not just the code
country.is gives you "US". Most apps then do a second lookup to turn that into a name, flag or currency. countries.dev attaches the full country record, so one request is enough:
const { country } = await fetch('https://countries.dev/ip').then((r) => r.json());
`${country.flag} ${country.name}`; // 🇺🇸 United States of AmericaJust want the bare code, country.is-style? It's right there as countryCode.
Batch lookups
country.is is one IP per request. countries.dev takes up to 100:
curl -X POST https://countries.dev/ip -d '["8.8.8.8","1.1.1.1"]'Switching over
country.is returns { ip, country } where country is the code; countries.dev returns { ip, countryCode, country } where country is the full object. Read countryCode instead of country and you're done.
See the IP geolocation API page for a live demo, or the docs. Only have coordinates instead of an IP? Use reverse geocoding.
Written by
Dov Azencot
At
Fri Jun 26 2026