Store cities in your database
You can store each city that the API returns. Its ID stays with the city when the sources change, and it still answers after the city leaves the dataset.
- The terms let you store the data that the API returns, with no restrictions.
- The ID of a city does not come from a source record. If a source replaces or merges the record of a city, the city keeps its ID.
- Onde never gives an ID to a different place.
- An ID still answers after its city leaves the dataset. A city that leaves the dataset tells what Get a city returns.
Store the ID and the fields that you show
Store the ID with a copy of the fields that your pages show. Then a page shows the city with no request to the API, and with no cost.
ALTER TABLE users
ADD COLUMN city_id text,
ADD COLUMN city_name text,
ADD COLUMN region_name text,
ADD COLUMN country_code text; The names are in the language of the request. If your app has one language, store the names in this language.
Group and count by city
Two people who select Kyoto get the same ID, also if one types 京都 and the other types Kyoto. Group and count by city_id, and not by the name.
SELECT city_id, min(city_name) AS city_name, count(*) AS users
FROM users
GROUP BY city_id
ORDER BY users DESC; To filter by country, use country_code. It is the ISO 3166-1 alpha-2 code, such as JP.
Show the city in another language
If your app has more than one language, send the ID to Get a city. Get a city returns names, with the name in each language that City API supports. region and country also have names.
{
"id": "city_39sy1dm2dj",
"name": "Kyoto",
"names": {
"en": "Kyoto",
"de": "Kyōto",
"es": "Kioto",
"fr": "Kyōto",
"it": "Kyoto",
"nl": "Kyoto",
"pt": "Quioto",
"ru": "Киото",
"ja": "京都市",
"ko": "교토 시",
"zh": "京都",
"ar": "كيوتو",
"hi": "क्योटो"
}
} Store names, and the app shows the city in each language with no more requests.
Read a city again
A new dataset can correct the name, the region, the coordinates, or the population of a city. Most apps do not need these corrections. If your app does, read each stored city again with Get a city.
Each response gives the dataset version in meta.dataset, such as 2026-11-17. Store the version with each city. Get dataset details gives the current version, with no key and no cost.
curl "https://api.onde.earth/v1/meta" Read only the cities with an older version. Each read costs one unit, and the limit of requests per second applies, so read the cities in a background job.
const CITY_URL = 'https://api.onde.earth/v1/cities/'
// Returns the current city, or null if the city has left the dataset.
async function readCity(id) {
const response = await fetch(CITY_URL + encodeURIComponent(id), {
headers: { Authorization: `Bearer ${process.env.ONDE_KEY}` }
})
const body = await response.json()
if (response.status === 410) {
return null
}
if (!response.ok) {
throw new Error(body.detail)
}
return body.data
} A city that leaves the dataset
Rarely, a city leaves the dataset. For example, the source has two records for one city, and Onde merges them. The ID of the city keeps working, and it gives the same response in all later datasets:
- If the city has a replacement, Get a city returns the replacement with status 200.
data.idis the ID of the replacement, andmeta.retiredIdis the ID that you sent. Replace the stored ID withdata.id. - If the city has no replacement, Get a city returns status 410 with the error
city_retired. Keep the stored fields, and do not read this ID again.
readCity above returns the replacement in the first case and null in the second case.