MAP
Concepts

How MAP works

Six concepts cover ~95% of the platform. Skim or read in order.

API keys & auth

Every /v1/* request requires a key, sent as a Bearer token:

Authorization: Bearer map_live_...

For tile requests (where most map clients can't set custom headers), you can put the key in the query string instead:

https://api.map.bd/v1/tiles/bangladesh/11/1456/880?api_key=map_live_...

Keys come in two prefixes: map_live_ for production and test_ for development. Both authenticate the same way; the prefix is purely a hint to your CI to make sure you don't commit live keys.

Revoke a key from the dashboard. Revoked keys are rejected with a 401 within 30 seconds (the gateway refresh interval).

bdnlp normalisation

The gateway runs every geocoding query through bdnlp before it reaches Nominatim. This is what makes Banglish + Bangla + landmark queries work where they fail on plain Nominatim or Google.

The pipeline:

  1. Detect Bangla numerals and convert to ASCII (১২ → 12)
  2. Parse house / road / block / sector tokens (with both EN and BN regexes)
  3. Detect landmark prepositions ("near", "কাছে") and translate landmark types ("হাসপাতাল" → hospital)
  4. Match the locality token against 1,400+ curated BD place spellings
  5. Issue up to 3 query variants against Nominatim in parallel
  6. Merge, then re-rank by parsed-component overlap

The result includes a _map_parsed block so you can see what the pipeline detected. Use this in your UI to give users feedback:

{
  "_map_parsed": {
    "Original": "House 23, Road 11, Banani",
    "House": "23", "Road": "11", "Locality": "Banani"
  },
  "_map_variants_tried": ["Banani, Bangladesh", "House 23, Road 11, Banani"]
}

To skip bdnlp and proxy raw to Nominatim, pass ?raw=true. Useful for A/B benchmarks.

Routing costing modes

The routing endpoints (/v1/directions, /v1/distance-matrix, /v1/optimized-route, etc.) all accept a costing field. Defaults to auto (private car).

ModeUse for
autoPrivate car, default ride-share use
taxiSame as auto but prefers main roads
truckCargo / parcel — respects weight + height restrictions
motorcycleAvoids no-bike roads
motor_scooterCNG / autorickshaw, scooters
bicycleGeneric bicycle
pedestrianWalking — uses footways, sidewalks
busBuses — respects bus lanes

For rickshaws specifically, use the dedicated /v1/directions/rickshaw endpoint — it presets the bicycle costing with Dhaka-tuned overrides (slow cruise, no motorways, side-street preference).

Webhooks & signatures

Subscribe URLs at /dashboard/webhooks. Then POST driver positions to /v1/events/track; the gateway fires callbacks on transitions (entering/exiting a geofence, ETA crossing a threshold).

Each callback includes an HMAC-SHA256 signature so you can verify authenticity:

POST https://yourapp/webhooks/map
X-MAP-Signature: sha256=<hex of HMAC-SHA256(secret, raw_body)>
X-MAP-Event: geofence.enter
X-MAP-Delivery: <uuid>
X-MAP-Attempt: 1

{ "event": "geofence.enter", "webhook_id": 42, "driver_id": "d-123", ... }

Verification in any language is the same shape:

// Node.js
const crypto = require('crypto');
const expected = 'sha256=' + crypto.createHmac('sha256', secret).update(rawBody).digest('hex');
const ok = crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(req.headers['x-map-signature']));

Failed deliveries (5xx, 408, 429, or network errors) are retried with exponential backoff: 1s, 4s, 16s. After 3 retries the event is dropped — you should make your webhook endpoint idempotent.

Rate limits

Each key has a per-minute quota set at creation time (default 100/min, max 10,000/min). The token bucket allows short bursts up to 2× the rate. Every response sets:

X-RateLimit-Limit:     100
X-RateLimit-Remaining: 87
X-RateLimit-Reset:     31    # seconds until full

When you exceed the rate, you get a 429 with a Retry-After header. The SDKs surface this as MapApiError.retry_after.

Vector tiles

Tiles come from a Bangladesh-only PMTiles bundle served by Martin. The schema is the OpenMapTiles spec, so any compatible style (Positron, Dark Matter, OSM Bright) works.

Three things to know:

  • Bangla labels by default. Layers with a name_bn attribute render Bangla in our packaged style.
  • Self-hosted style. Serve /style/positron.json from the same origin as your app to avoid cross-port CORS issues.
  • Tile range: zoom 0 – 14. Past 14, we serve the parent tile and let the client over-zoom.