Total Human DesignDashboard|
Documentation

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.

No calculation units consumed

This endpoint only resolves the location — it does not run Swiss Ephemeris or consume a calculation unit from your quota. Use it to catch bad input before calling a chart endpoint.

Why use this?

All chart endpoints accept a birthLocation string that gets resolved to coordinates and timezone. If the city name is invalid, you waste a calculation call and get a 400 error.

This endpoint lets you:

  • Pre-validate city input before submitting a chart request
  • Show the resolved city to your user so they can confirm the match
  • Surface suggestions when the input is invalid — up to 5 similar cities returned so the user can correct without guessing

If you're building a full city picker UI, see City Search (GET /api/locations) instead.

Request

Parameters

ParameterTypeRequiredDescription
birthLocationstringNoCity name to validate (e.g. "Manila", "London, UK", "New York, NY"). Required if latitude/longitude not provided.
latitudenumberNoOverride latitude. Bypasses city lookup when provided with longitude.
longitudenumberNoOverride longitude. Bypasses city lookup when provided with latitude.
birthDatestringNoDate (YYYY-MM-DD) for DST-aware offset calculation. Defaults to 2000-01-01.
birthTimestringNoTime (HH:MM) for DST-aware offset calculation. Defaults to 12:00.

Either birthLocation or both latitude + longitude must be provided.

thd-api

Response — valid location

Response200

{
"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

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

Response200

{
"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 found

How suggestions are generated

The fallback runs three attempts, stopping at the first that returns results:

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

Response — ISO country code

If the input looks like a 2-letter country code, a hint field is added instead of suggestions:

Response200

{
"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"
}

Response fields at a glance

FieldPresent when
resolvedvalid: true only
errorvalid: false only
suggestionsvalid: false only
hintvalid: false + 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 like "US", "PH", "BG" will fail — use city names
  • Queries shorter than 3 characters are rejected
  • "Austin TX" format (no comma) returns valid: false but suggestions will include Austin, Texas — use the value from suggestions as the corrected input
  • Towns under 1,000 population are not in the dataset — collect latitude/longitude directly for very remote birthplaces

Notes

  • Location resolution is fully offline — no external geocoding APIs are called
  • birthDate and birthTime only affect the UTC offset calculation (historical DST rules), not which city is matched
  • Pass latitude + longitude instead of birthLocation to bypass city lookup entirely — useful for coordinates from a map picker