DEV Community

ApogeoAPI
ApogeoAPI

Posted on • Originally published at apogeoapi.com

Countries, States, and Cities API — Complete Guide 2026

Geographic hierarchy — country → state → city — is one of the most common data needs in web apps. Shipping forms, user profiles, store locators, tax calculations. Here's everything you need to know about using a countries/states/cities API effectively.

What Data Does a Countries API Return?

A good countries API returns more than just country names. The core fields you should expect:

  • iso2, iso3 — Standard country codes
  • name — Official country name
  • capital — Capital city
  • region, subregion — Geographic region
  • flag_url — Flag image URL
  • currency, currency_symbol — Local currency
  • phone_code — International dial code
  • timezones — Array of IANA timezone strings

What About States and Cities?

The hierarchy goes: 250+ countries → 5,000+ states/provinces → 150,000+ cities. Not all APIs support all three levels. RestCountries has no cities. GeoDB Cities covers all three but with complex query params. ApogeoAPI covers all three with a simple REST structure.

Building a Cascading Country → State → City Selector

'use client';
import { useState, useEffect } from 'react';
const API = 'https://api.apogeoapi.com/v1';
const headers = { 'X-API-Key': process.env.NEXT_PUBLIC_APOGEO_KEY! };
export function LocationSelector() {
const [countries, setCountries] = useState([]);
const [states, setStates] = useState([]);
const [cities, setCities] = useState([]);
const [country, setCountry] = useState('');
const [state, setState] = useState('');
useEffect(() => {
fetch(`${API}/countries`, { headers }).then(r => r.json()).then(setCountries);
}, []);
useEffect(() => {
if (!country) return;
setStates([]); setState(''); setCities([]);
fetch(`${API}/countries/${country}/states`, { headers })
.then(r => r.json()).then(setStates);
}, [country]);
useEffect(() => {
if (!country || !state) return;
setCities([]);
fetch(`${API}/countries/${country}/states/${state}/cities`, { headers })
.then(r => r.json()).then(d => setCities(d.data ?? []));
}, [country, state]);
return (

 setCountry(e.target.value)}>
Select country
{countries.map((c: any) => {c.name})}

 setState(e.target.value)} disabled={!states.length}>
Select state
{states.map((s: any) => {s.name})}

Select city
{cities.map((c: any) => {c.name})}

);
}
Enter fullscreen mode Exit fullscreen mode

Which API Should You Use?

Feature RestCountries GeoDB ApogeoAPI
Countries
States
Cities (150K+)
Exchange rates
IP Geolocation
Simple REST Partial
Free tier Always free Very limited Free + 14-day trial

Performance Tips

  • Cache countries and states on the client — they rarely change. Use SWR with a 24h stale time.
  • Paginate cities — 150K cities don't load at once. Use ?page=1&limit=50 and load more on scroll.
  • Pre-select via IP — Use IP geolocation to pre-select the user's country automatically on form load.

Originally published at https://apogeoapi.com/blog/countries-states-cities-api. Try ApogeoAPI free at apogeoapi.com.

Top comments (0)