Documentation / Practical guides
From data to useful.
Three common patterns for working with geographic data in a real application.
01 / Location picker
Load countries, states, and cities
Build a cascading picker by fetching states after a country is chosen, then cities after a state is chosen. Lists return only essential fields; fetch a detail endpoint when you need more.
const API_BASE = "https://cdn.jsdelivr.net/npm/geo-data-api@latest/dist/api/v1/";
async function getJson(path) {
const response = await fetch(new URL(path, API_BASE));
if (!response.ok) throw new Error("Request failed: " + response.status);
return response.json();
}
async function loadPickerData() {
const countries = await getJson("countries.json");
const states = await getJson("states/country/us.json");
const cities = await getJson("cities/state/us-ca.json");
return { countries: countries.data, states: states.data, cities: cities.data };
}
loadPickerData().then(console.log).catch(console.error);
Replace us and us-ca with the
selected country and state codes. If a state has no ISO 3166-2
code, its city-list filename uses its numeric ID.
02 / City search
Search within a country
Load the country’s city-search shard once, then filter it
locally. Normalize the query in the same way as
name_normalized so a search for “Sao Paulo”
matches “São Paulo”.
const API_BASE = "https://cdn.jsdelivr.net/npm/geo-data-api@latest/dist/api/v1/";
async function getJson(path) {
const response = await fetch(new URL(path, API_BASE));
if (!response.ok) throw new Error("Request failed: " + response.status);
return response.json();
}
function normalizeName(value) {
return value.toLowerCase().normalize("NFD").replace(/\p{M}/gu, "");
}
let brazilShard;
async function searchBrazilianCities(query) {
const normalized = normalizeName(query.trim());
if (!normalized) return [];
if (!brazilShard) {
brazilShard = getJson("search/cities/country/br.json").catch(error => {
brazilShard = null;
throw error;
});
}
const result = await brazilShard;
return result.data.filter(city => city.name_normalized.includes(normalized));
}
searchBrazilianCities("Sao Paulo").then(console.log).catch(console.error);
This folds combining marks; it is not full transliteration. A country with no city records may have no shard. Use the shard index to discover available files, and cache the response for repeated searches.
03 / City details
Find a city by ID
City details are stored in batches. Find the ID range in the
index, request its filename relative to the API
base, and look up the ID in the batch data.
const API_BASE = "https://cdn.jsdelivr.net/npm/geo-data-api@latest/dist/api/v1/";
async function getJson(path) {
const response = await fetch(new URL(path, API_BASE));
if (!response.ok) throw new Error("Request failed: " + response.status);
return response.json();
}
async function getCity(cityId) {
const index = await getJson("cities/batches.json");
const batch = index.data.find(item =>
cityId >= item.start_id && cityId <= item.end_id
);
if (!batch) return null;
const result = await getJson(batch.filename);
return result.data[cityId] || null;
}
getCity(57995).then(console.log).catch(console.error);
IDs can have gaps even within a batch range. A missing batch
or ID returns null; cache the index and batch
files when performing repeated lookups.
04 / Shipping
A few deployment tips
- Pin a dataset or package version when reproducibility matters.
- Fetch smaller country or state files when possible, and cache responses in your app.
- Serve the files with your host’s CORS and cache settings appropriate for your clients.
- The combined search endpoint is a sample. Use the dedicated indexes for complete results.
Ready to experiment? Open the playground to try these patterns with the current dataset.