Geo Search — MDDB API
Radius search (R-tree or geohash) #
POST /v1/geo-search
Find documents within radiusMeters of a (lat, lng) point. Documents
must have geo_lat+geo_lng, geo_hash, or a resolvable
geo_postcode+geo_country in their metadata. Results are sorted by
ascending distance and composable with filterMeta.
Authorization: BearerAuth or APIKeyAuth or none
Request body #
Required.
Content type application/json: object
| Property | Type | Description |
|---|---|---|
collection (required) |
string |
|
lat (required) |
number (double) |
|
lng (required) |
number (double) |
|
radiusMeters (required) |
number (double) |
|
topK |
integer |
Default 10. |
algorithm |
string |
One of "rtree", "geohash". Default "rtree". |
filterMeta |
object |
|
includeContent |
boolean |
Default false. |
{
"algorithm": "rtree",
"collection": "string",
"includeContent": false,
"lat": 0,
"lng": 0,
"radiusMeters": 0,
"topK": 10
}
Responses #
| Status | Description | Body |
|---|---|---|
200 |
OK | |
400 |
Invalid request | |
503 |
Index still loading |
Bounding-box search #
POST /v1/geo-within
Find documents inside an axis-aligned bbox. Does not cross the anti-meridian.
Authorization: BearerAuth or APIKeyAuth or none
Request body #
Required.
Content type application/json: object
| Property | Type | Description |
|---|---|---|
collection (required) |
string |
|
minLat (required) |
number |
|
maxLat (required) |
number |
|
minLng (required) |
number |
|
maxLng (required) |
number |
|
filterMeta |
object |
|
includeContent |
boolean |
{
"collection": "string",
"includeContent": false,
"maxLat": 0,
"maxLng": 0,
"minLat": 0,
"minLng": 0
}
Responses #
| Status | Description | Body |
|---|---|---|
200 |
OK | |
400 |
The request is malformed (a body that is not JSON, a missing or invalid field, an unreadable body) or names a subsystem that is not initialized. The message says which. | ErrorResponse (application/json) |
401 |
Auth is on (MDDB_AUTH_ENABLED=true) and the request carries no credentials, an invalid or expired JWT, or an unknown API key. | ErrorResponse (application/json) |
403 |
The caller lacks the permission the route needs (SEC-019 access gate, auth on: read, write or admin on the collections the request names), or a write meets read-only mode (MDDB_MODE=read or the protocol's own mode). | ErrorResponse (application/json) |
413 |
The body is over MDDB_MAX_BODY_BYTES (32 MiB by default). | ErrorResponse (application/json) |
429 |
The client is over the API rate limit (MDDB_RATE_LIMIT_ENABLED=true, MDDB_RATE_LIMIT_REQUESTS per MDDB_RATE_LIMIT_WINDOW seconds). A call that embeds can also be refused by the collection's daily embedding budget, with a QuotaExceeded body. | RateLimitError (application/json) |
400 example (application/json)
{
"error": "missing collection"
}
401 example (application/json)
{
"error": "missing authentication"
}
403 example (application/json)
{
"error": "forbidden"
}
413 example (application/json)
{
"error": "request body too large"
}
Find documents inside a GeoJSON polygon or multi-polygon #
POST /v1/geo-polygon
Returns every indexed point whose coordinates fall inside the supplied
GeoJSON Polygon (outer ring + optional holes) or MultiPolygon (union
of polygons). Exactly one of polygon or multiPolygon must be set.
The R-tree's bounding-box query prefilters candidates, so response
time tracks the shape's bbox rather than the full collection size.
GeoJSON coordinate order is [lng, lat] per RFC 7946. Rings may be
open (first ≠ last) or closed (first == last); both forms work.
Does not cross the anti-meridian — split the query into hemispheres if you need that.
Authorization: BearerAuth or APIKeyAuth or none
Request body #
Required.
Content type application/json: GeoPolygonRequest
{
"collection": "venues",
"includeContent": false,
"multiPolygon": {
"coordinates": [
[
[]
]
],
"type": "MultiPolygon"
},
"polygon": {
"coordinates": [
[
[]
]
],
"type": "Polygon"
}
}
Responses #
| Status | Description | Body |
|---|---|---|
200 |
Matches (empty array if none) | GeoPolygonResponse (application/json) |
400 |
Invalid request — missing collection, both/neither shape supplied, or malformed geometry | |
503 |
Geo index still loading |
Force-rebuild geo indexes #
POST /v1/geo-reindex
Rebuild both R-tree and geohash indexes from the persisted geo bucket. Optionally load one or more postcode CSVs first.
Authorization: BearerAuth or APIKeyAuth or none
Request body #
Required.
Content type application/json: object
| Property | Type | Description |
|---|---|---|
collection |
string |
Empty = rebuild all collections |
loadPostcodes |
array of object |
|
loadPostcodes[].country |
string |
|
loadPostcodes[].csvPath |
string |
{
"collection": "string",
"loadPostcodes": [
{
"country": "string",
"csvPath": "string"
}
]
}
Responses #
| Status | Description | Body |
|---|---|---|
200 |
OK | |
400 |
The request is malformed (a body that is not JSON, a missing or invalid field, an unreadable body) or names a subsystem that is not initialized. The message says which. | ErrorResponse (application/json) |
401 |
Auth is on (MDDB_AUTH_ENABLED=true) and the request carries no credentials, an invalid or expired JWT, or an unknown API key. | ErrorResponse (application/json) |
403 |
The caller lacks the permission the route needs (SEC-019 access gate, auth on: read, write or admin on the collections the request names), or a write meets read-only mode (MDDB_MODE=read or the protocol's own mode). | ErrorResponse (application/json) |
413 |
The body is over MDDB_MAX_BODY_BYTES (32 MiB by default). | ErrorResponse (application/json) |
429 |
The client is over the API rate limit (MDDB_RATE_LIMIT_ENABLED=true, MDDB_RATE_LIMIT_REQUESTS per MDDB_RATE_LIMIT_WINDOW seconds). A call that embeds can also be refused by the collection's daily embedding budget, with a QuotaExceeded body. | RateLimitError (application/json) |
503 |
Not now: the search concurrency limit is reached, the subsystem is disabled, or a write meets the database while its freelist is rebuilt (#333; the body then adds phase and progress). Retry after Retry-After seconds. | ErrorResponse (application/json) |
400 example (application/json)
{
"error": "missing collection"
}
401 example (application/json)
{
"error": "missing authentication"
}
403 example (application/json)
{
"error": "forbidden"
}
413 example (application/json)
{
"error": "request body too large"
}
Geo index statistics #
GET /v1/geo-stats
Authorization: BearerAuth or APIKeyAuth or none
Responses #
| Status | Description | Body |
|---|---|---|
200 |
OK | |
401 |
Auth is on (MDDB_AUTH_ENABLED=true) and the request carries no credentials, an invalid or expired JWT, or an unknown API key. | ErrorResponse (application/json) |
403 |
The caller lacks the permission the route needs (SEC-019 access gate, auth on: read, write or admin on the collections the request names), or a write meets read-only mode (MDDB_MODE=read or the protocol's own mode). | ErrorResponse (application/json) |
429 |
The client is over the API rate limit (MDDB_RATE_LIMIT_ENABLED=true, MDDB_RATE_LIMIT_REQUESTS per MDDB_RATE_LIMIT_WINDOW seconds). A call that embeds can also be refused by the collection's daily embedding budget, with a QuotaExceeded body. | RateLimitError (application/json) |
401 example (application/json)
{
"error": "missing authentication"
}
403 example (application/json)
{
"error": "forbidden"
}
Encode (lat, lng) → geohash string #
POST /v1/geo-encode
Authorization: BearerAuth or APIKeyAuth or none
Request body #
Required.
Content type application/json: object
| Property | Type | Description |
|---|---|---|
lat (required) |
number |
|
lng (required) |
number |
|
precision |
integer |
Default 12. Minimum 1. Maximum 12. |
{
"lat": 0,
"lng": 0,
"precision": 12
}
Responses #
| Status | Description | Body |
|---|---|---|
200 |
OK | |
400 |
The request is malformed (a body that is not JSON, a missing or invalid field, an unreadable body) or names a subsystem that is not initialized. The message says which. | ErrorResponse (application/json) |
401 |
Auth is on (MDDB_AUTH_ENABLED=true) and the request carries no credentials, an invalid or expired JWT, or an unknown API key. | ErrorResponse (application/json) |
413 |
The body is over MDDB_MAX_BODY_BYTES (32 MiB by default). | ErrorResponse (application/json) |
429 |
The client is over the API rate limit (MDDB_RATE_LIMIT_ENABLED=true, MDDB_RATE_LIMIT_REQUESTS per MDDB_RATE_LIMIT_WINDOW seconds). A call that embeds can also be refused by the collection's daily embedding budget, with a QuotaExceeded body. | RateLimitError (application/json) |
400 example (application/json)
{
"error": "missing collection"
}
401 example (application/json)
{
"error": "missing authentication"
}
413 example (application/json)
{
"error": "request body too large"
}
Decode geohash → centroid + bbox #
POST /v1/geo-decode
Authorization: BearerAuth or APIKeyAuth or none
Request body #
Required.
Content type application/json: object
| Property | Type | Description |
|---|---|---|
geohash (required) |
string |
{
"geohash": "string"
}
Responses #
| Status | Description | Body |
|---|---|---|
200 |
OK | |
400 |
The request is malformed (a body that is not JSON, a missing or invalid field, an unreadable body) or names a subsystem that is not initialized. The message says which. | ErrorResponse (application/json) |
401 |
Auth is on (MDDB_AUTH_ENABLED=true) and the request carries no credentials, an invalid or expired JWT, or an unknown API key. | ErrorResponse (application/json) |
413 |
The body is over MDDB_MAX_BODY_BYTES (32 MiB by default). | ErrorResponse (application/json) |
429 |
The client is over the API rate limit (MDDB_RATE_LIMIT_ENABLED=true, MDDB_RATE_LIMIT_REQUESTS per MDDB_RATE_LIMIT_WINDOW seconds). A call that embeds can also be refused by the collection's daily embedding budget, with a QuotaExceeded body. | RateLimitError (application/json) |
400 example (application/json)
{
"error": "missing collection"
}
401 example (application/json)
{
"error": "missing authentication"
}
413 example (application/json)
{
"error": "request body too large"
}