Overview
This service exposes the Bangladesh geo dropdown catalog as HTTP. Each request returns only the children of one parent — do not download the full catalog. Responses use stable catalog IDs as option values.
| URL | Purpose |
|---|---|
| https://geoapi.labmirai.org | Marketing portal |
| https://geoapi.labmirai.org/login | Sign in / sign up |
| https://geoapi.labmirai.org/app | Control panel |
| https://geoapi.labmirai.org/v1/… | Keyed catalog API |
| https://geoapi.labmirai.org/demo/options | Public live demo (no key) |
| https://geoapi.labmirai.org/docs | This page |
Authentication
All /v1/* routes require an API key. Create keys in the dashboard (shown once). Send:
X-API-Key: bgc_…
Also accepted: Authorization: Bearer bgc_… or query ?api_key= (prefer the header).
GET /health, GET /demo/options, /docs, and the portal pages do not need a key.
Rate limits & quotas
- IP — requests per minute (global setting in dashboard).
- Per key, per minute — each key has
rate_limit_per_min. - Per key, per day — each key has
daily_quota. - Exceeded limits return 429 with
retry_after(seconds) and aRetry-Afterheader. - Admins can disable a key, reset today’s usage, or turn the whole API off (503).
Address cascade
Always store the returned value (catalog id). On parent change, clear every descendant.
Under a district, next choices mix upazila, city_corporation, and municipality:
GET /v1/options?parent={districtId}&types=upazila,city_corporation,municipality
Example (Dhaka district zilla:3026): Dhaka North / South City Corporation appear alongside upazilas such as Savar.
If you pick a city corporation, continue with wards/thanas (not union → village):
GET /v1/options?parent={cityCorporationId}&types=metropolitan_thana,urban_ward
Rural path after upazila:
GET /v1/options?parent={upazilaId}&types=union
GET /v1/options?parent={unionId}&types=village
District IDs are often zilla:… even though type is district. Use the id the API returns; do not invent codes from names.
After union, only village is exposed. Rural ward and mauza are not available.
Option shape
{
"options": [
{
"value": "division:30",
"label": "Dhaka",
"type": "division",
"parent_id": "country:BD",
"name_en": "Dhaka",
"name_bn": "ঢাকা",
"translated_label_available": true,
"child_counts": { "district": 13 },
"cross_source_match": null,
"boundary_review_required": false,
"children_mapping_status": null,
"legacy_only": false
}
]
}
Use value as the select value and next parent. Use label for display (duplicate names are already disambiguated).
Endpoints
GET /health
No key. Returns service status, catalog assemble time, uptime, and whether the public demo is on.
GET /v1/options
| Query | Required | Description |
|---|---|---|
| parent | yes | Parent catalog id, e.g. country:BD, division:30, zilla:3026 |
| types | no | Comma-separated types. Omit to return all allowed children. |
| lang | no | en (default) or bn |
| q | /no | Filter labels / aliases |
| includeLegacy | no | 1 to include legacy paths |
Allowed types: division, district, upazila, city_corporation, municipality, union, village, urban_ward, metropolitan_thana, cantonment, forest_range.
rural_ward and mauza return 400 if requested.
GET /v1/search
Name/alias search. Query: q (required), optional types, within, limit (max 500), lang, includeLegacy.
GET /v1/search?q=Suapur&types=union&limit=20
GET /v1/path/:id
Ancestor chain from country to the node. 404 if missing or a blocked type.
GET /v1/node/:id
Single node plus path. Same 404 rules as path.
GET /v1/meta
Catalog metadata: coverage, assembled_at, schema_version, country.
GET /demo/options
Same cascade as /v1/options but no API key. Can be disabled from admin settings.
Errors
| Status | Meaning |
|---|---|
| 400 | Missing parent / q, or unsupported types |
| 401 | Missing or invalid API key |
| 403 | Key disabled, or public demo disabled |
| 404 | Unknown id, or blocked type |
| 429 | IP rate limit, per-key rate limit, or daily quota |
| 503 | Admin turned the API off |
Bodies are JSON: { "error": "…" }, plus quota fields when relevant.
Frontend integration
- Load divisions with
parent=country:BD&types=division. - On pick, set form field to option
value. - Fetch the next level with that id as
parent. - After union, fetch villages. If empty, offer free-text “Not listed”.
const res = await fetch(
'https://geoapi.labmirai.org/v1/options?parent=country:BD&types=division&lang=en',
{ headers: { 'X-API-Key': process.env.GEO_API_KEY } }
);
const { options } = await res.json();
IDs vs names
Catalog ids are the source of truth (division:30, zilla:3026, upazila:302614, union:30261488, village:404147109101036). A village id is one 15-digit number (the old hyphenated BBS path with the hyphens removed). Names are for humans and can collide; the API already appends context to label when needed.
The last code — the village, or whatever deepest id you stored — still resolves to the full chain. For village:404147109101036 (same place as 40-41-47-109-101-036):
{
"division": "division:40",
"district": "zilla:4041",
"upazila": "upazila:404147",
"union": "union:40414710",
"village": "village:404147109101036"
}
Not included
- Rural ward and mauza selectors
- EIS wire format
- Postcodes
- The full national JSON dump over HTTP