Skip to main content

Routing & Navigation

Turn-by-turn routing for Ghana's road network, powered by a self-hosted Valhalla engine. Because addresses are sparse in Ghana, routing here understands landmarks — the way people actually navigate.

Route between two points

POST /v2/route accepts hex codes, GPS codes, or coordinates as endpoints:

curl -X POST "https://api.afrihex.com/v2/route" \
-H "X-API-Key: $AFRIHEX_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"from": { "hex": "AF-GH-7-0GXTQD5RFZZZZ" },
"to": { "hex": "AF-GH-7-0GXTQD5RJ0000" },
"mode": "driving"
}'

Travel modes: driving, walking, cycling. The response includes distance, duration, a polyline, and turn-by-turn steps:

Request objectPOST /v2/route:

ParameterTypeRequiredDefinition
fromobjectrequiredOrigin — { hex }, { gps_code }, or { lat, lng }
toobjectrequiredDestination — same accepted shapes
modestringoptionaldriving, walking, or cycling (default driving)
{
"success": true,
"data": {
"distance_m": 4604,
"duration_s": 687.079,
"eta_s": 1122.7864,
"traffic_note": "morning rush",
"coordinates": [ [-0.195223, 5.604169], [-0.195249, 5.604189] ],
"steps": [
{
"instruction": "Drive northwest on Continental Close.",
"verbal_instruction": "Drive northwest on Continental Close. Then Turn left onto Continental Road.",
"verbal_post": "Continue for 300 meters.",
"name": "Continental Close",
"near_landmark": "Roman Ridge",
"distance_m": 308,
"duration_s": 37.063,
"surface": "paved",
"surface_color": "#4CAF50",
"bearing_before": 308,
"bearing_after": 308,
"turn_class": "straight"
}
],
"landmarks_passed": [],
"has_unpaved": true,
"unpaved_distance_m": 1072
}
}

Route-aware conditions

Routing can account for real-world conditions:

  • Flood zones — avoid currently flooded areas.
  • Weather alerts — official heavy-rain / flash-flood warnings.
  • Rain now — penalise routes through heavy rain when a drier option beats the fastest.
  • Surface quality — each step reports surface (paved / unpaved / unknown) and a surface_color (see the #4CAF50 / #FF9800 values in the response above). The top level also reports has_unpaved and unpaved_distance_m.

Distance matrices

POST /v2/route/matrix computes all-pairs travel times/distances for many origins and destinations in one call — perfect for optimising dispatch:

Request object:

ParameterTypeRequiredDefinition
sourcesarray<object>requiredOrigins — { hex }, { gps_code }, or { lat, lng }
destinationsarray<object>requiredDestinations — same shapes
modestringoptionaldriving, walking, or cycling (default driving)
curl -X POST "https://api.afrihex.com/v2/route/matrix" \
-H "X-API-Key: $AFRIHEX_API_KEY" \
-H "Content-Type: application/json" \
-d '{"sources": [{"hex":"AF-GH-7-0GXTQD5RFZZZZ"}], "destinations": [{"hex":"AF-GH-7-0GXTQD5VFZZZZ"}], "mode": "driving"}'
{
"success": true,
"data": {
"durations": [[687]],
"distances": [[4605]],
"sources": 1,
"destinations": 1
}
}

Isochrones

POST /v2/route/isochrone answers "how far can I get in 30 minutes?" — returns a reachable area polygon:

Request object:

ParameterTypeRequiredDefinition
locationobjectrequiredOrigin — { hex }, { gps_code }, or { lat, lng }
contoursarray<object>requiredReachability rings — { "time_minutes": 15 } (1–120)
modestringoptionaldriving, walking, or cycling (default driving)
curl -X POST "https://api.afrihex.com/v2/route/isochrone" \
-H "X-API-Key: $AFRIHEX_API_KEY" \
-H "Content-Type: application/json" \
-d '{"location": {"hex": "AF-GH-7-0GXTQD5RFZZZZ"}, "contours": [{"time_minutes": 15}, {"time_minutes": 30}], "mode": "driving"}'

The response returns each contour as a styled GeoJSON polygon (drive-time rings). This drives the Service Areas drive-time zones.

Optimized routes

POST /v2/route/optimized solves a multi-stop problem — visit N waypoints in the best order (traveling-salesman style).

GPX export

POST /v2/route/gpx returns a GPX file you can drop straight into Google Maps / Garmin / any GPS device. There's also POST /v2/route/match and POST /v2/route/match/gpx for snapping a GPS track to the road network.

POST /v2/navigate/route is the API behind a turn-by-turn navigation flow:

curl -X POST "https://api.afrihex.com/v2/navigate/route" \
-H "X-API-Key: $AFRIHEX_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"from": { "hex": "AF-GH-7-0GXTQD5RFZZZZ" },
"to": { "hex": "AF-GH-7-0GXTQD5RJ0000" },
"mode": "driving"
}'

POST /v2/navigation/arrival records when a navigator reaches the pin, scoring destination accuracy over time. There's also a public route endpoint (POST /v2/route/public, IP-throttled) used by the QR-code navigation flow so anyone can get directions without an account.

Static map images

GET /v2/route/static renders a route as a shareable map image (route line + start/end pins) — great for WhatsApp sharing or receipts.

Landmarks

Find places the way Ghanaians describe them:

  • GET /v2/landmarks/geocode?q=kaneshie%20market — fuzzy landmark search.
  • GET /v2/landmarks/around?lat=...&lng=...&radius=500 — landmarks near a point.
  • GET /v2/hexcode/{code}/landmarks — landmarks inside a hex cell.

Route health

GET /v2/route/health reports whether the routing engine's extract is current — clients can warn when routes may be stale.

SDK

const route = await afrihex.landmarks.geocode({ q: 'kaneshie market' })
console.log(route.matches[0].centroid) // { lat: 5.5641, lng: -0.2335 }

API reference

See Routing & Navigation — API Reference for every endpoint in this group.