Transit, Micromobility & Offline
Transit, Micromobility & Offline
Move people the last mile and keep the map alive where the network isn't. This module surfaces Navvo transit feeds (stops and route lines for systems like the Riyadh Metro or the Amman BRT), Navvo micromobility feeds (shared bikes and scooters across the GCC), a multimodal journey planner that stitches walking legs onto a connecting transit route, and offline map regions so the map and search keep working in dead zones — from a packed downtown garage to the open desert of Wadi Rum.
Everything is served and branded by Navvo from a single API. There are no outside services to wire up.
Scope required: places for stops, routes, and stations; routing for journey planning; tiles for offline regions and manifests.
lat, lng). Distance fields (distanceM) are in metres. A bbox is west,south,east,north. Nearby endpoints accept a radius in metres. Endpoints degrade gracefully — an out-of-coverage viewport returns an empty count:0 collection rather than an error.GET /transit/stops
Nearby public-transit stops (metro, bus, tram, rail) around a point — the stops you'd draw as markers near the user or use as journey origins. Each stop lists the routeIds that serve it.
curl -s "https://navvo.io/api/v1/transit/stops?lat=24.7136&lng=46.6753&radius=800" \
-H "x-api-key: nvvo_YOUR_KEY"
Scope required: places
Query parameters
500; capped server-side for readability.Response
| Field | Type | Description |
|---|---|---|
provider | string | Always navvo-transit. |
count | number | Number of stops returned. |
stops[] | array | The nearby stops, nearest first. |
Each stops[] element:
| Field | Type | Description |
|---|---|---|
id | string | Navvo stop id. |
stopId | string | Stable feed-level stop identifier (use with the route's routeId). |
name | string | Stop name. |
lat / lng | number | Stop location. |
distanceM | number | Distance from the search centre, in metres. |
routeIds | string | Ids of the routes that serve this stop. |
{
"provider": "navvo-transit",
"count": 2,
"stops": [
{
"id": "stop-9a31",
"stopId": "RYD-METRO-1-OLAYA",
"name": "Olaya",
"lat": 24.6913,
"lng": 46.6857,
"distanceM": 142,
"routeIds": ["RYD-METRO-BLUE"]
},
{
"id": "stop-7c08",
"stopId": "RYD-BUS-300-KINGFAHD",
"name": "King Fahd Rd",
"lat": 24.6951,
"lng": 46.6840,
"distanceM": 410,
"routeIds": ["RYD-BUS-300", "RYD-BUS-305"]
}
]
}
GET /transit/routes
Route lines for a viewport — the coloured polylines you overlay on the map to draw a metro or BRT network. Each route carries an encoded shape you can render directly.
curl -s "https://navvo.io/api/v1/transit/routes?bbox=46.55,24.60,46.80,24.80" \
-H "x-api-key: nvvo_YOUR_KEY"
Scope required: places
Query parameters
west,south,east,north. Only routes whose shape passes through the box are returned (routes with no geometry are kept).route_type: 0 tram, 1 metro, 2 rail, 3 bus, 4 ferry, … Each returned route also carries a human typeLabel.The response echoes the active filters ({ type, bbox }). Combine them — e.g. ?bbox=…&type=1 for just the metro lines in view.
Response
| Field | Type | Description |
|---|---|---|
provider | string | Always navvo-transit. |
count | number | Number of routes returned. |
routes[] | array | The route lines (see below). |
Each routes[] element:
| Field | Type | Description |
|---|---|---|
id | string | Navvo route id. |
routeId | string | Stable feed-level route identifier (matches stops[].routeIds). |
shortName | string | Short label (e.g. Blue, 300). |
longName | string | Full route name. |
type | string | Mode: metro, bus, tram, rail, or ferry. |
color | string | Hex colour for the line. |
shape | string | Encoded polyline for the route geometry. |
{
"provider": "navvo-transit",
"count": 1,
"routes": [
{
"id": "route-blue",
"routeId": "RYD-METRO-BLUE",
"shortName": "Blue",
"longName": "Riyadh Metro — Line 1 (Blue)",
"type": "metro",
"color": "#1E61F0",
"shape": "qkn~Eqd}xK}@a@k@e@_@e@"
}
]
}
shape is a standard encoded polyline (precision 5). Decode it to [lat, lng] pairs to draw the line, or hand it to the Navvo Maps renderer as a LineString source.GET /transit/gbfs
Nearby shared micromobility stations — docked bikes, e-bikes, and scooters across the GCC — with live availability counts. Use it to show riders where they can pick up a vehicle now.
curl -s "https://navvo.io/api/v1/transit/gbfs?lat=25.2048&lng=55.2708&radius=600&type=scooter" \
-H "x-api-key: nvvo_YOUR_KEY"
Scope required: places
Query parameters
500.bike, ebike, or scooter. Omit to return all types.minBikes=1 to hide empty stations when a rider wants to grab a vehicle now.minDocks is set.)Response
| Field | Type | Description |
|---|---|---|
provider | string | Always navvo-micromobility. |
filters | object | The active filters echoed back: { vehicleType, minBikes, minDocks }. |
count | number | Number of stations returned. |
stations[] | array | The nearby stations, nearest first — each with bikesAvailable / docksAvailable. |
Each stations[] element:
| Field | Type | Description |
|---|---|---|
id | string | Navvo station id. |
name | string | Station name. |
vehicleType | string | bike, ebike, or scooter. |
lat / lng | number | Station location. |
bikesAvailable | number | Vehicles available to take now. |
docksAvailable | number | Free docks to return a vehicle (0 for dockless/free-floating stations). |
distanceM | number | Distance from the search centre, in metres. |
{
"provider": "navvo-micromobility",
"count": 2,
"stations": [
{
"id": "mm-4471",
"name": "Dubai Marina Walk",
"vehicleType": "scooter",
"lat": 25.0805,
"lng": 55.1403,
"bikesAvailable": 6,
"docksAvailable": 0,
"distanceM": 88
},
{
"id": "mm-4490",
"name": "JBR Station",
"vehicleType": "ebike",
"lat": 25.0789,
"lng": 55.1340,
"bikesAvailable": 3,
"docksAvailable": 9,
"distanceM": 520
}
]
}
POST /transit/plan
A multimodal journey suggestion: given an origin and destination, Navvo finds a connecting transit route and stitches walking legs onto each end (walk to the boarding stop → ride → walk to the destination). When no useful connection exists, it returns a walking alternative plus the nearest stops at both ends so you can offer a fallback.
curl -s "https://navvo.io/api/v1/transit/plan" \
-H "x-api-key: nvvo_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"from": { "lat": 31.9539, "lng": 35.9106 },
"to": { "lat": 31.9870, "lng": 35.8650 }
}'
Scope required: routing
Request body
{ lat, lng }.{ lat, lng }.Response — connection found
| Field | Type | Description |
|---|---|---|
provider | string | Always navvo-transit. |
found | boolean | true when a connecting route was found. |
legs[] | array | Ordered journey legs: a walk leg, a transit leg, then a walk leg. |
A transit leg carries the route it uses (a route object as in /transit/routes) plus the board and alight stops (stop objects as in /transit/stops). walk legs carry their own distance and duration.
{
"provider": "navvo-transit",
"found": true,
"legs": [
{
"mode": "walk",
"from": { "lat": 31.9539, "lng": 35.9106 },
"to": { "lat": 31.9551, "lng": 35.9119 },
"distanceM": 210,
"durationS": 168
},
{
"mode": "transit",
"route": {
"routeId": "AMM-BRT-1",
"shortName": "BRT 1",
"longName": "Amman BRT — Sweileh Corridor",
"type": "bus",
"color": "#0EA15B"
},
"board": { "stopId": "AMM-BRT-1-ABDALI", "name": "Abdali", "lat": 31.9551, "lng": 35.9119 },
"alight": { "stopId": "AMM-BRT-1-SWEIFIEH", "name": "Sweifieh", "lat": 31.9860, "lng": 35.8669 },
"durationS": 720
},
{
"mode": "walk",
"from": { "lat": 31.9860, "lng": 35.8669 },
"to": { "lat": 31.9870, "lng": 35.8650 },
"distanceM": 240,
"durationS": 192
}
]
}
Response — no connection
When no connecting route fits, found is false and Navvo returns a reason, a direct walking alternative, and the nearest stops at each end so you can prompt the rider toward transit anyway.
| Field | Type | Description |
|---|---|---|
found | boolean | false. |
reason | string | Why no transit journey was returned (e.g. no_common_route, out_of_coverage). |
walkAlternative | object | A direct walking leg { distanceM, durationS }. |
nearestOriginStops[] | array | Nearest stops to the origin (stop objects). |
nearestDestStops[] | array | Nearest stops to the destination (stop objects). |
{
"provider": "navvo-transit",
"found": false,
"reason": "no_common_route",
"walkAlternative": { "distanceM": 5400, "durationS": 4320 },
"nearestOriginStops": [
{ "stopId": "AMM-BUS-26-DOWNTOWN", "name": "Downtown", "lat": 31.9515, "lng": 35.9239, "distanceM": 310 }
],
"nearestDestStops": [
{ "stopId": "AMM-BUS-26-SHMEISANI", "name": "Shmeisani", "lat": 31.9700, "lng": 35.9000, "distanceM": 480 }
]
}
GET /offline/regions
The catalogue of predefined, downloadable offline map regions — bounded areas a client can pre-cache so the map keeps working without a network. Use it to present a "Download for offline" picker (a whole country or a city).
curl -s "https://navvo.io/api/v1/offline/regions" \
-H "x-api-key: nvvo_YOUR_KEY"
Scope required: tiles
Response
| Field | Type | Description |
|---|---|---|
provider | string | Always navvo-offline. |
regions[] | array | Available offline regions (see below). |
Each regions[] element:
| Field | Type | Description |
|---|---|---|
id | string | Region id (use with /offline/regions/{id}/manifest). |
name | string | English name. |
nameAr | string | Arabic name. |
bbox | number | Bounds as [west, south, east, north]. |
minZoom / maxZoom | number | Zoom range the region is packaged for. |
estTiles | number | Estimated number of tiles to download. |
estSizeMb | number | Estimated download size in megabytes. |
countryCode | string | ISO country code (e.g. SA, JO, AE). |
scope | string | country or city. |
version | string | Version token — changes whenever the pack is updated. Compare it against a stored download to show "Update available". |
{
"provider": "navvo-offline",
"regions": [
{
"id": "05f81944-b193-4fb2-a61d-af3c96b91af4",
"name": "Jordan",
"nameAr": "الأردن",
"scope": "country",
"bbox": [34.88, 29.18, 39.30, 33.38],
"minZoom": 5,
"maxZoom": 12,
"estTiles": 3999,
"estSizeMb": 117,
"countryCode": "JO",
"version": "2026-06-25T14:55:16.529Z"
},
{
"id": "d86f0ed5-9821-43cc-af67-e5c8fa0ff429",
"name": "Palestine",
"nameAr": "فلسطين",
"scope": "country",
"bbox": [34.22, 31.22, 35.57, 32.55],
"minZoom": 5,
"maxZoom": 12,
"estTiles": 446,
"estSizeMb": 13,
"countryCode": "PS",
"version": "2026-06-25T14:55:16.529Z"
}
]
}
GET /offline/regions/{id}/manifest
The download manifest for one region — everything a client needs to pre-cache a complete offline base map: style, vector tiles, fonts (glyphs), icons (sprite), and raster fallbacks. Fetch it after the user picks a region, then enumerate the tiles across bbox × zooms and warm your cache.
curl -s "https://navvo.io/api/v1/offline/regions/{id}/manifest" \
-H "x-api-key: nvvo_YOUR_KEY"
Scope required: tiles
Response
| Field | Type | Description |
|---|---|---|
provider | string | Always navvo-offline. |
region | object | The region descriptor (same shape as a /offline/regions entry). |
styleUrl | string | The map style URL to cache and render offline. |
vectorTileTemplate | string | Vector tile URL template — expand {z}/{x}/{y} across bbox × zooms. |
glyphs | object | Font glyphs needed for offline labels: template ({fontstack}/{range} URL template), fontstacks (the stacks the style uses), and ranges (the Unicode ranges to fetch — Latin + Arabic blocks, so EN and AR labels render offline). Cache every fontstack × range combination. |
sprite | object | The icon sprite atlas: json (index), png, and png2x (hi-DPI) URLs — cache all three. |
rasterTemplates | object | Raster tile templates (standard, satellite) for clients that also want an offline raster/satellite fallback. Optional — the vector map alone renders fully offline. |
tiles | number | Estimated count of vector tiles for the full bbox × zoom download. |
sizeMb | number | Estimated download size in megabytes. |
zooms | object | { min, max } — the zoom levels to download. The renderer overzooms past max, so deeper zooms still display from the cached tiles. |
version | string | Pack version token — re-fetch the manifest and compare to offer "Update available". |
bbox | number | Region bounds [west, south, east, north]. |
note | string | Caching guidance for the client. |
{
"provider": "navvo-offline",
"region": { "id": "…", "name": "Jordan", "nameAr": "الأردن", "scope": "country",
"bbox": [34.88, 29.18, 39.30, 33.38], "minZoom": 5, "maxZoom": 12,
"estTiles": 3999, "estSizeMb": 117, "countryCode": "JO", "version": "2026-06-25T14:55:16.529Z" },
"styleUrl": "/api/v1/tiles/style?style=standard",
"vectorTileTemplate": "/api/v1/tiles/vector/navvo/planet/{z}/{x}/{y}.pbf",
"glyphs": {
"template": "/api/v1/tiles/glyphs/navvo/{fontstack}/{range}.pbf",
"fontstacks": ["Noto Sans Regular", "Noto Sans Bold"],
"ranges": ["0-255", "256-511", "1536-1791", "1792-2047", "64256-64511", "65024-65279"]
},
"sprite": {
"json": "/api/v1/tiles/sprites/navvo/sprite.json",
"png": "/api/v1/tiles/sprites/navvo/sprite.png",
"png2x": "/api/v1/tiles/sprites/navvo/sprite@2x.png"
},
"rasterTemplates": {
"standard": "/api/v1/tiles/raster/standard/{z}/{x}/{y}.png",
"satellite": "/api/v1/tiles/raster/satellite/{z}/{x}/{y}.png"
},
"tiles": 3999,
"sizeMb": 117,
"zooms": { "min": 5, "max": 12 },
"version": "2026-06-25T14:55:16.529Z",
"bbox": [34.88, 29.18, 39.30, 33.38],
"note": "Cache vector tiles across bbox × zooms (plus glyphs/sprite above) for a complete offline base map; the map overzooms past the cached level."
}
GET /search / GET /places responses) alongside the map pack.x-api-key header — append ?api_key=nvvo_YOUR_KEY when you expand styleUrl and the tile/glyph/sprite templates. See Map Tiles & Styles for details.