Overview
The Maimaps API powers full map experiences: render vector maps, search places and addresses, compute drive/walk/cycle routes, reverse-geocode coordinates, resolve tapped points, and look up Maiaddy loccodes — all authenticated with a single MAIMAPS API key.
Two hosts, one key
/sdk/v1/*on the API gateway — REST endpoints for search, routing, geocoding, point details, and loccodes./sdk/styles · /sdk/tiles · /sdk/sprites · /sdk/fontson the map host — style documents, vector tiles, and glyphs for map display.
The SDKs hide the split behind one options object: environment: 'staging' | 'production' selects baked-in host defaults. Production hostnames are published before GA; until then, production requires explicit hosts.
Every successful REST response uses a stable envelope:
{ "status": "success", "message": "...", "data": { ... } }Route responses are OSRM-shaped (routes[].distance|duration|geometry|legs); polylines use precision 5. /sdk/v1 is the stable contract — breaking changes require /sdk/v2; additive fields are non-breaking.
Quickstart
From zero to a rendered map with your first search and route in three steps.
- 1
Create a MAIMAPS key
In the console, create an API key for the Maimaps product. TEST keys (mk_test_…) are free to burn during development; LIVE keys (mk_live_…) carry the full monthly caps.
- 2
Install an SDK
Official SDKs for JavaScript, React, React Native, and Flutter wrap every endpoint, inject your key, and ship typed errors and retries.
- 3
Render a map, then search and route
Load the keyed style.json into MapLibre (one map load per fetch), then call search and route for your first results.
Replace mk_test_your_key with a key from the console.
import { MaimapsClient } from "@maimaps/js";
import maplibregl from "maplibre-gl";
const client = new MaimapsClient({ apiKey: "mk_test_your_key" });
// 1. Render a map (each keyed style.json fetch = one map load)
const map = new maplibregl.Map({
container: "map",
style: client.styleUrl({ mode: "dark" }),
transformRequest: (url) => ({ url: client.transformMapResource(url) }),
});
// 2. First search
const results = await client.search({
q: "garki market",
latitude: 9.05,
longitude: 7.49,
});
// 3. First route (OSRM-shaped: routes[].distance|duration|geometry|legs)
const trip = await client.route({
originLat: 9.05,
originLng: 7.49,
destLat: 6.45,
destLng: 3.39,
mode: "drive",
});Authentication
Keys are minted in this portal for the MAIMAPS product: mk_live_… for the LIVE environment and mk_test_… for TEST. Pass the key with every request — as the X-Api-Key header (preferred for REST calls) or as a ?key= query parameter (required for map resources, which cannot set headers portably).
GET /sdk/v1/search?q=garki%20market HTTP/1.1
Host: maps-staging-api.maiaddy.com
X-Api-Key: mk_test_your_key# Map resources can't set headers portably — pass the key as a query param.
GET /sdk/styles/maimaps/style.json?mode=dark&key=mk_test_your_keyTEST vs LIVE
TEST keys behave identically but carry ~10% of LIVE monthly limits; usage is recorded with environment=TEST. Use TEST keys for development and CI, then switch the key string for launch.
Keys are not secrets — but they are quota-bearing
Maimaps keys ship inside client apps by design. Treat them like quota handles, not credentials: rotate from the console if abused. Origin / bundle-ID restrictions arrive at GA hardening.
Rate limits & quotas
The free tier meters every billable dimension from day one. LIVE keys get the limits below; TEST keys share the same per-second rates with 10% of the monthly caps.
- Tiles are never billed — the map load is the display unit; tile limits exist purely as an abuse guard.
- Per-second limits are enforced at the edge; monthly caps are computed from usage rollups, and an over-quota key is switched off at the next validation refresh (≤ 5 minutes).
- Every limited route returns
X-RateLimit-Limit,X-RateLimit-Remaining, andX-RateLimit-Resetheaders, plusRetry-Afteron 429.
Errors
Failures return a stable error body with a machine-readable code:
{ "status": "error", "code": "rate_limited", "message": "Per-key rate limit hit. Retry after 2 seconds." }invalid_api_keyMissing, malformed, revoked, or wrong-product key.restriction_violation(Post-GA) origin/bundle restriction failed.rate_limitedPer-key rate limit hit — honor Retry-After.quota_exceededMonthly cap reached for the dimension.auth_unavailableKey validation backend unreachable and key not in stale cache.The SDKs raise typed errors (InvalidApiKeyError, RateLimitError(retryAfter), QuotaExceededError) and automatically retry idempotent GETs only, honoring Retry-After.
REST endpoints
All REST endpoints live under /sdk/v1 on the API gateway and accept the X-Api-Key header.
/sdk/v1/searchFree-text place/address/POI/loccode search. Params: q (required), latitude, longitude, limit, bbox, type, category./sdk/v1/routeSimple A→B routing. Params: origin_lat, origin_lng, dest_lat, dest_lng, mode (drive | walk | cycle)./sdk/v1/routeWaypoints + loccode origins/destinations. Body: {origin_lat/lng | origin_loccode, dest_lat/lng | dest_loccode, waypoints[], mode}./sdk/v1/reverse-geocodeCoordinates → address + loccode. Params: latitude, longitude./sdk/v1/point-detailsTapped-point resolution. Params: latitude, longitude, osm_id?, osm_type?, search_name?, search_address?./sdk/v1/loccode/{code}Loccode → coordinates/geometry/metadata./sdk/v1/loccodes/nearestNearest loccode. Params: latitude, longitude./sdk/v1/place-categoriesCategory taxonomy (tracked, unbilled).Map display endpoints
/sdk/styles · /sdk/tiles · /sdk/sprites · /sdk/fontsMap assets are served from the map host and authenticate with the ?key= query parameter. The style.json references the /sdk/* tile/sprite/glyph URLs; the SDKs append the key via transformRequest / transformMapResource.
GET /sdk/styles/{family}/style.json?mode={light|dark}&key=…Style document — each keyed fetch counts as one map load.GET /sdk/tiles/{z}/{x}/{y}.pbf?key=…Vector tiles.GET /sdk/tiles.json?key=…TileJSON.GET /sdk/sprites/…?key=… · GET /sdk/fonts/…?key=…Sprites / glyphs.Map loads, not tiles
Each keyed style.json fetch counts as one map load — the metered display unit. Tile requests are rate-limited but never billed.
SDKs
Official Maimaps SDKs for JavaScript, React, React Native, and Flutter wrap every endpoint, handle key injection on both hosts, and ship typed errors with rate-limit-aware retries.
Browse SDKs