City Search
GET/api/locationsPro
Search cities by name. Returns matching cities with timezone, coordinates, and population — use this to build city autocomplete in your own frontend or chatbot.
Not metered
This endpoint only searches the city database. It does not run Swiss Ephemeris and does not count against your Chart requests or SAGE responses. Location tools are free with any key.
Why use this?
All chart endpoints require a valid birthLocation. If you're building your own UI, you need a way to let users pick a real city. This endpoint gives you that — it's the same offline city database the THD frontend uses, with 167,000+ cities across 246 countries.
Typical flow:
- User types into a city input field
- You call
/api/locations?query=<input>on each keystroke (debounced) - Display the returned list as a dropdown
- On selection, pass the chosen
valueasbirthLocationin your chart request
Request
Method: GET
| Param | Type | Required | Default | Description |
|---|---|---|---|---|
query | string | Yes | — | Partial city name, min 3 characters |
limit | number | No | 8 | Max results returned (max 20) |
Response
Returns a JSON array sorted by population descending (most prominent city wins ties).
Response200
[
{
"name": "Manila",
"province": "National Capital Region",
"country": "Philippines",
"timezone": "Asia/Manila",
"coordinates": { "lat": 14.6042, "lng": 120.9822 },
"population": 1600000,
"value": "Manila, National Capital Region, Philippines"
},
{
"name": "Manila",
"province": "Arkansas",
"country": "United States",
"timezone": "America/Chicago",
"coordinates": { "lat": 35.8801, "lng": -90.167 },
"population": 3736,
"value": "Manila, Arkansas, United States"
}
]| Field | Description |
|---|---|
name | City name |
province | State or province (null if not applicable) |
country | Full country name |
timezone | IANA timezone identifier |
coordinates | { lat, lng } — pass these as latitude/longitude in chart requests to bypass city lookup |
population | Population used for ranking (higher = ranked first) |
value | Display string for your UI — safe to use as birthLocation in chart calls |
Error — query too short:
{ "success": false, "error": "query param is required and must be at least 3 characters." }
Dataset
- Source: GeoNames
cities1000(CC-BY 4.0) - Coverage: 167,617 cities, population ≥ 1,000, 246 countries
- US cities: 17,321 — covers towns down to ~1,000 population
- Search: prefix-first then contains, diacritics-insensitive (
"Zurich"matches"Zürich") - Tiebreaker: population descending —
"London"returns UK (8.9M) before Canada (422K) - Comma hint:
"London, Canada"filters by province/country
Population floor
Towns with fewer than 1,000 residents are not in the dataset. For very remote birthplaces, collect coordinates directly from the user and pass latitude + longitude to the chart endpoint — this bypasses the city lookup entirely.
Integration example
// Debounced autocomplete
let debounce;
cityInput.addEventListener('input', (e) => {
clearTimeout(debounce);
if (e.target.value.length < 3) return;
debounce = setTimeout(async () => {
const res = await fetch(
`/api/locations?query=${encodeURIComponent(e.target.value)}&limit=8`,
{ headers: { Authorization: `Bearer ${apiKey}` } }
);
const cities = await res.json();
// Render dropdown
dropdown.innerHTML = cities.map(city =>
`<option value="${city.value}" data-lat="${city.coordinates.lat}" data-lng="${city.coordinates.lng}">
${city.value}
</option>`
).join('');
}, 250);
});
