Skip to main content
POST
Safe Bike Directions API

Overview

Get directions between waypoints with support for cycling, walking, and driving profiles. Optionally use the supersafe flag for bike routing on a dedicated cycling network with safety prioritization.

Authentication

This endpoint requires an API key. Include it in the Authorization header:
A valid user session is also accepted and grants higher rate limits — see Rate Limits.

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 bicycles
  • walking - Optimized for pedestrians
  • driving - Optimized for cars
  • driving-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 success
number | 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

Use bearings 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

When supersafe: true is enabled for cycling routes, the API uses Cyclemate’s proprietary network router that:
  1. Prioritizes Safety: Routes prefer protected bike lanes, bike paths, and low-traffic streets
  2. Local Knowledge: Uses city-specific bike infrastructure data (NYC, LA, SF, Chicago, London)
  3. Elevation Aware: Minimizes elevation gain when possible
  4. 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 is 429 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