City Search

GET/api/locationsPro

Search cities by name to retrieve timezones, coordinates, and populations for building city autocomplete inputs.

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.

Overview

Chart endpoints require a valid birthLocation. This endpoint queries the offline city database used by the THD frontend, containing 167,000+ cities across 246 countries.

Typical flow:

  1. User types into a city input field
  2. Call /api/locations?query=<input> on each keystroke (debounced)
  3. Display the returned list as a dropdown
  4. Pass the chosen value as birthLocation in the 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).

✓ doneapplication/json
[
{
  "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 } object. Pass these as latitude/longitude in chart requests to bypass city lookup.
populationPopulation used for ranking (higher ranks first).
valueDisplay string for UIs. Pass this as birthLocation in chart requests.

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 and longitude to the chart endpoint to bypass 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);
});