Skip to content

POST /geo/resolve

Finds the Lowest Common Ancestor (LCA) of a list of place names — the most specific geographic region that contains all of them.

Costs 1 credit per request.

POST /geo/resolve
Authorization: Bearer gaz_<api-key>
Content-Type: text/plain
X-Min-Score: 0.6 (optional)

Body: newline-separated list of place names (plain text).

Cuiabá
Sinop
Rondonópolis
HeaderTypeDefaultDescription
AuthorizationstringrequiredBearer gaz_<api-key>
X-Min-Scorefloat0.6Minimum similarity score (0.0–1.0) for a name to be considered resolved. Names below this threshold are skipped and counted in unresolved.
{
"region": "Mato Grosso",
"type": "state",
"region_id": 3457419,
"unresolved": 0,
"outliers": 2,
"total": 3,
"locationIds": [
{ "name": "Cuiabá", "id": 3664728 },
{ "name": "Sinop", "id": 3449581 },
{ "name": "Rondonópolis", "id": 3451638 }
]
}
FieldTypeDescription
regionstringName of the common ancestor region
typestringworld | country | state | city
region_idintegerPlace ID — use with GET /geo/place/{id}
unresolvedintegerNumber of input names that couldn’t be matched above X-Min-Score
outliersintegerCount of resolved inputs whose best match fell outside the resolved region. These are excluded from the LCA using the 85% coverage rule. Non-zero values indicate possible data gaps; interpret their locationIds entries with care.
totalintegerTotal number of input names sent
locationIdsarrayEach resolved input mapped to its best-matching place within the common region. Each entry is { name, id } where name is the canonical place name and id is the place ID. Inputs that didn’t match above X-Min-Score are omitted. When multiple places match an input, the nearest descendant of the common region is preferred.

At least 90% of the input names must resolve (match above X-Min-Score) before an LCA is computed — an LCA over a small resolved subset isn’t trustworthy. If fewer resolve, the endpoint returns 200 with an explicit unresolved object instead of a region:

{
"region": null,
"resolved": false,
"resolvedCount": 7,
"total": 10,
"requiredRatio": 0.9,
"message": "Too few places resolved"
}
FieldTypeDescription
regionnullAlways null here — no LCA was computed
resolvedbooleanfalse — check this field to detect the unresolved case
resolvedCountintegerHow many inputs matched above X-Min-Score
totalintegerTotal number of input names sent
requiredRatiofloatThe minimum resolvedCount / total required (default 0.9)

When a name is ambiguous (e.g. SC matches both South Carolina and Santa Catarina with an identical score), the resolver breaks the tie using the context of the other inputs. It first finds the most specific region that the batch as a whole agrees on, then prefers the candidate that falls inside it.

So a batch of US state codes:

oh
nd
ok
or
pa
ri
sc

resolves to the United Statespa and sc are pulled toward Pennsylvania and South Carolina by the surrounding context, rather than matching Pará and Santa Catarina and forcing the result up to world. The same batch of Brazilian state codes (sc, pa, mt, go) resolves to Brazil for the mirror-image reason.

A candidate only contributes to the shared context if the place is substantial (a real city, state, or country) — so a coincidental same-named hamlet can’t hijack the result. A genuinely global batch like Berlin, Paris, London still resolves to its true common ancestor (Europe / world) instead of over-narrowing.

The resolver uses up to 3 passes. Inputs whose best match is ambiguous are re-ranked on each pass to prefer candidates inside the emerging region. If ≥85% of inputs agree on a region, the remaining ≤15% are treated as outliers — they don’t prevent the LCA from resolving, but their locationIds entries may be id: -1.

StatusDescription
401Missing or invalid API key
402Insufficient credits
404No common region found (all inputs resolved to places with no common ancestor, or all unresolved)
422Empty request body
429Rate limit exceeded
{
"detail": {
"message": "No common region found",
"unresolved": 1,
"total": 3
}
}
Terminal window
curl -X POST https://api.hlgsws.xyz/geo/resolve \
-H "Authorization: Bearer gaz_..." \
-H "Content-Type: text/plain" \
--data "Paris
Lyon
Marseille"
{
"region": "France",
"type": "country",
"region_id": 3017382,
"unresolved": 0,
"total": 3,
"locationIds": [
{ "name": "Paris", "id": 2988507 },
{ "name": "Lyon", "id": 2996944 },
{ "name": "Marseille", "id": 2995469 }
]
}

Raise X-Min-Score to reject ambiguous matches:

Terminal window
curl -X POST https://api.hlgsws.xyz/geo/resolve \
-H "Authorization: Bearer gaz_..." \
-H "X-Min-Score: 0.8" \
-H "Content-Type: text/plain" \
--data "São Paulo
Campinas"

A non-zero unresolved count means some inputs were skipped. The LCA was computed only over the resolved subset. If confidence matters, check:

confidence = (total - unresolved) / total