Validate location

POST/api/validate-locationPro

Pre-validate a city name before making a chart calculation call. Returns resolved coordinates and timezone on success, or structured city suggestions on failure.

Not metered

This endpoint resolves the location. It does not run the Swiss Ephemeris and does not count against your Chart requests or SAGE responses. Location tools are free with any API key.

Use cases

Chart endpoints accept a birthLocation string that resolves to coordinates and a timezone. An invalid city name results in a 400 error and a wasted calculation call. Use this endpoint to:

  • Pre-validate city input before a chart request.
  • Show the resolved city for user confirmation.
  • Surface up to 5 structured city suggestions for invalid input.

For a full city picker UI, use City search (GET /api/locations).

Request

Parameters
birthLocation stringCity name to validate (e.g., "Manila", "London, UK", "New York, NY"). Required if latitude and longitude are absent.
latitude numberLatitude override. Bypasses city lookup when provided with longitude.
longitude numberLongitude override. Bypasses city lookup when provided with latitude.
birthDate stringDate (YYYY-MM-DD) for DST-aware offset calculation. Defaults to 2000-01-01.
birthTime stringTime (HH:MM) for DST-aware offset calculation. Defaults to 12:00.

Provide either birthLocation, or both latitude and longitude.

thd-api

Response — valid location

✓ doneapplication/json
{
"success": true,
"valid": true,
"resolved": {
  "city": "Manila",
  "timezone": "Asia/Manila",
  "coordinates": { "lat": 14.6042, "lon": 120.9822 },
  "offset": 8,
  "source": "offline-city-lookup"
},
"input": "Manila, Philippines"
}
FieldDescription
validtrue — city resolved successfully
resolved.cityCanonical city name from the database
resolved.timezoneIANA timezone identifier
resolved.coordinates{ lat, lon } — exact coordinates used
resolved.offsetDST-aware UTC offset in fractional hours (e.g. 8, -4, 5.5)
resolved.source"offline-city-lookup" or "offline-coordinates"
inputThe original string you sent

Response — invalid location

If the city cannot be resolved, the endpoint returns 200 OK with valid: false and up to 5 structured suggestions.

✓ doneapplication/json
{
"success": true,
"valid": false,
"error": "City 'Austin TX' not found. Please provide a valid city name or lat/lon coordinates.",
"input": "Austin TX",
"suggestions": [
  {
    "name": "Austin",
    "province": "Texas",
    "country": "United States",
    "timezone": "America/Chicago",
    "coordinates": { "lat": 30.2672, "lng": -97.7431 },
    "population": 961855,
    "value": "Austin, Texas, United States"
  }
]
}
FieldDescription
validfalse — input could not be resolved
errorHuman-readable explanation
suggestionsArray of up to 5 city objects sorted by population. Use value as the corrected birthLocation. Always present on failure. May be empty if no partial match is found.

Suggestion generation

The endpoint attempts three fallback strategies in order, returning the first set of results:

  1. Exact match on the full input.
  2. First space-token — "Austin TX" searches "Austin".
  3. 4-char prefix — "Chemberlun" searches "Chem" (for typos).

ISO country codes

Inputs formatted as 2-letter country codes return a hint field instead of suggestions.

✓ doneapplication/json
{
"success": true,
"valid": false,
"error": "City 'BG' not found...",
"input": "BG",
"suggestions": [],
"hint": ""BG" looks like a country code. Try a city name instead: Sofia, Plovdiv, Varna"
}

Conditional fields

FieldPresent when
resolvedvalid: true only
errorvalid: false only
suggestionsvalid: false only
hintvalid: false and ISO country code detected

Integration pattern

// Pre-flight check before chart calculation
async function resolveCity(userInput) {
  const res = await fetch('https://api.totalhumandesign.com/api/validate-location', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${apiKey}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({ birthLocation: userInput })
  });
  const result = await res.json();

  if (!result.valid) {
    // Show suggestions so user can pick a corrected city
    if (result.suggestions.length > 0) {
      showSuggestions(result.suggestions); // each has .value, .timezone, .coordinates
    } else if (result.hint) {
      showHint(result.hint);
    } else {
      showError(result.error);
    }
    return null;
  }

  // Confirm with user, then calculate
  return result.resolved; // { city, timezone, coordinates, offset }
}
Common pitfalls
  • ISO country codes ("US", "PH") fail. Provide city names.
  • Queries under 3 characters are rejected.
  • Formats without commas ("Austin TX") return valid: false. Suggestions include the corrected format (Austin, Texas). Pass the suggestion value as the new input.
  • Towns under 1,000 population are absent. Collect latitude and longitude directly for remote locations.

Notes

  • Location resolution is offline and calls no external geocoding APIs.
  • The birthDate and birthTime only affect the UTC offset calculation for historical DST rules. They do not influence city matching.
  • Provide latitude and longitude instead of birthLocation to bypass the city lookup.