Total Human DesignDashboard|
Documentation

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:

  1. User types into a city input field
  2. You call /api/locations?query=<input> on each keystroke (debounced)
  3. Display the returned list as a dropdown
  4. On selection, pass the chosen value as birthLocation in your chart request

Request

Method: GET

ParamTypeRequiredDefaultDescription
querystringYesPartial city name, min 3 characters
limitnumberNo8Max results returned (max 20)
thd-api

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"
}
]
FieldDescription
nameCity name
provinceState or province (null if not applicable)
countryFull country name
timezoneIANA timezone identifier
coordinates{ lat, lng } — pass these as latitude/longitude in chart requests to bypass city lookup
populationPopulation used for ranking (higher = ranked first)
valueDisplay 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); });