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 only resolves the location. It does not run Swiss Ephemeris and does not count against your Chart requests or SAGE responses. Use it to catch bad input before calling a chart endpoint. Location tools are free with any key.
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
| Parameter | Type | Required | Description |
|---|---|---|---|
birthLocation | string | No | City name to validate (e.g. "Manila", "London, UK", "New York, NY"). Required if latitude/longitude not provided. |
latitude | number | No | Override latitude. Bypasses city lookup when provided with longitude. |
longitude | number | No | Override longitude. Bypasses city lookup when provided with latitude. |
birthDate | string | No | Date (YYYY-MM-DD) for DST-aware offset calculation. Defaults to 2000-01-01. |
birthTime | string | No | Time (HH:MM) for DST-aware offset calculation. Defaults to 12:00. |
Either birthLocation or both latitude + longitude must be provided.
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"
}| Field | Description |
|---|---|
valid | true — city resolved successfully |
resolved.city | Canonical city name from the database |
resolved.timezone | IANA timezone identifier |
resolved.coordinates | { lat, lon } — exact coordinates used |
resolved.offset | DST-aware UTC offset in fractional hours (e.g. 8, -4, 5.5) |
resolved.source | "offline-city-lookup" or "offline-coordinates" |
input | The 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"
}
]
}| Field | Description |
|---|---|
valid | false — input could not be resolved |
error | Human-readable explanation |
suggestions | Array 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:
- Exact match on the full input
- First space-token —
"Austin TX"→ searches"Austin"✓ - 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
| Field | Present when |
|---|---|
resolved | valid: true only |
error | valid: false only |
suggestions | valid: false only |
hint | valid: 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) returnsvalid: falsebut suggestions will include Austin, Texas — use thevaluefrom suggestions as the corrected input- Towns under 1,000 population are not in the dataset — collect
latitude/longitudedirectly for very remote birthplaces
Notes
- Location resolution is fully offline — no external geocoding APIs are called
birthDateandbirthTimeonly affect the UTCoffsetcalculation (historical DST rules), not which city is matched- Pass
latitude+longitudeinstead ofbirthLocationto bypass city lookup entirely — useful for coordinates from a map picker
