Safe Bike Directions API
Routing
Safe Bike Directions API
Get turn-by-turn directions with optional super-safe bike routing
POST
Safe Bike Directions API
Overview
Get directions between waypoints with support for cycling, walking, and driving profiles. Optionally use thesupersafe flag for bike routing on a dedicated cycling network with safety prioritization.
Authentication
This endpoint requires an API key. Include it in theAuthorization header:
Request Body
array
required
Array of [longitude, latitude] waypoints. Minimum 2, maximum 25.Example:
[[lon1, lat1], [lon2, lat2]]string
default:"cycling"
Routing profile. Options:
cycling- Optimized for bicycleswalking- Optimized for pedestriansdriving- Optimized for carsdriving-traffic- Driving with traffic data
boolean
default:"false"
Enable super-safe bike routing using Cyclemate’s network router. Only applies to
cycling profile.When enabled, routes prioritize:- Protected bike lanes
- Low-traffic streets
- Bike paths and greenways
- Reduced elevation gain
number
Optional constraint on the direction of travel as the route leaves the start point, in degrees
[0, 360) (0 = north, 90 = east). Primarily useful for mid-ride reroutes: pass the user’s current heading so the new route doesn’t begin with a near-U-turn.All road segments within ~80m of the start point whose direction of travel is outside the server-configured tolerance (default ±45°) of bearing are treated as non-traversable in that direction only. Bidirectional roads with a legal direction remain traversable on their matching side. One-way roads are naturally dropped if their only legal direction is out of range.If the constraint leaves no traversable path out of the start bubble, the request returns NoRoute — in that case the client should retry without bearing. Fractional degrees are accepted and rounded to the nearest integer server-side. Cycling profile only.Example: 90 (traveling east).string
Optional wall-clock departure time the rider plans to leave, as
"YYYY-MM-DDTHH:mm" with no timezone offset (e.g. "2026-06-03T21:00"). The server interprets it in the route city’s timezone to decide day/night routing — when the picked time lands in the dark window (30 minutes after sunset to 30 minutes before sunrise) and the city has street-lighting data (London today), cycling routes use the lit-street-preferring nighttime costs (see metadata.nighttime). Omit to time the route to now. An unparseable value returns 400. Affects cycling only; walking ignores it, and cities without lighting data never go nighttime.The departure time also drives time-based avoidances: operators can mark a road as avoided on a recurring weekly window (e.g. a Saturday street market or a school street at drop-off) or a one-time date range (e.g. a festival closure from 15 Jan 2027 09:00 to 16:00 — one-time windows may span multiple days and cross midnight). When the departure falls inside such a window, cycling routes — both SuperSafe and Direct — route around the road; outside the window (or once a one-time window has passed) the road routes normally. Walking is unaffected, and these windows never change the map’s safety coloring (routing-time only).Example: "2026-06-03T21:00" (9 PM local at the route).object
Additional routing options (applied when not using supersafe)
Response
array
Array of route objects
array
Snapped waypoint locations
string
Response code:
Ok on successnumber | null
Identifier of the analytics row written for this request (
helloapp_route_search.id). Pass this back when persisting a route to helloapp_routehistory.route_search_id so the search → ride funnel can be computed. May be null if the server failed to persist the analytics row; in that case the route is still valid — analytics writes are best-effort.number[] | null
Ordered list of the internal graph edge ids (
ways.id) this route traverses, in travel order. Cycling responses populate it; walking responses leave it null. The event editor concatenates edge_ids across a multi-stop route’s segments and persists them on the event (helloapp_event.edges_router_ids) so event-edge generation rebuilds the route’s edges with a cheap id lookup instead of re-snapping the polyline. Opaque to API consumers — only meaningful together with graph_version.string | null
The graph version (
ways.version) that edge_ids belong to. Stamped alongside the ids so they can be detected as stale after a graph rebuild reassigns edge ids. null on walking responses.string | null
"Prefers well-lit roads" when the route was computed on the nighttime cost network (a cycling request that landed in the dark window — see metadata.nighttime), null otherwise. Walking routes never set this.array
Temporary road disruptions — one-time
graph_time_overrides windows (manual curator entries plus the TfL Road Disruptions and DfT Street Manager roadworks feeds) — that are active at the departure time or open within the next 60 minutes, and whose pin lies within ~150 m of this route’s cycling geometry. Absent (not empty) when none are nearby, and never present on walking responses. One entry per works site, pinned at the centroid of the affected ways.Entries have the exact same shape and semantics as the per-option disruptions field on /multi-modal/ — see Multi-Modal Routing for the field-by-field reference (id, source, status, severity, location, and the city-local window fields). status: "upcoming" windows do not influence routing, so an upcoming pin can sit directly on the route.object
Routing metadata
string
Identifier of the routing engine that produced the route. Always
network_router.string
Routing profile used
number
Number of waypoints
boolean
Whether the route was computed on the nighttime cost network, which prefers lit streets (streets with no OpenStreetMap lighting data, or tagged unlit, are penalised). Set for cycling requests in a city that has street-lighting data (London today) that land in the dark window — from 30 minutes after sunset until 30 minutes before sunrise at the route origin (the city’s local night, derived from the origin coordinates). Always
false for walking, and always false in cities without lighting data (their nighttime costs equal the base cost, so there’s no lit-road preference to surface).Request Example
Standard Cycling Route
cURL
JavaScript
Reroute With Current Heading
Usebearings when recomputing a route mid-ride so the new route leaves the user’s location in their current direction of travel rather than asking them to U-turn:
JavaScript
Super-Safe Bike Route
JavaScript
Python
Response Example
Super-Safe Routing
Whensupersafe: true is enabled for cycling routes, the API uses Cyclemate’s proprietary network router that:
- Prioritizes Safety: Routes prefer protected bike lanes, bike paths, and low-traffic streets
- Local Knowledge: Uses city-specific bike infrastructure data (NYC, LA, SF, Chicago, London)
- Elevation Aware: Minimizes elevation gain when possible
- Real-time Updates: Incorporates user feedback and recent infrastructure changes
Error Responses
object
Invalid request
object
Invalid profile
object
Cross-city or out-of-coverage routing. Includes
search_id so the failed search can still be joined back to a future ride from a different attempt (the analytics row is written with status='cross_route_not_supported').object
Rate limit exceeded. Includes a
Retry-After response header with the number of seconds until the current window closes.object
Service unavailable
Rate Limits
Requests are rate-limited. Authenticated requests (valid user session) are allowed a higher rate than anonymous requests. When the limit is exceeded, the response is429 Too Many Requests with a Retry-After header indicating the number of seconds to wait before retrying, and a matching retry_after_seconds field in the body. Clients should honor Retry-After.
Notes
- Coordinates must be in
[longitude, latitude]format - Maximum 25 waypoints per request
- Super-safe routing is only available in supported cities (NYC, LA, SF Bay Area, Chicago, London)
- Duration estimates assume average cycling speed of 15 km/h (9.3 mph)
- Turn-by-turn instructions are in English