curl 'https://api.cyclemate.club/custom-routes/78360cf7-0f4a-48f1-91ca-25e83db44aff/' \
-H 'Authorization: Bearer ACCESS_TOKEN'
{
"id": "78360cf7-0f4a-48f1-91ca-25e83db44aff",
"alias": "culver-city-coffee-loop-1792536692813",
"url": "https://cyclemate.club/explore/los-angeles/routes/culver-city-coffee-loop-1792536692813",
"name": "Culver City coffee loop",
"description": "An easy Sunday loop with a stop at the bakery.",
"emoji": "☕",
"privacy": "public",
"route_mode": "dynamic",
"categories": [{ "slug": "coffee", "name": "Coffee", "emoji": "☕" }],
"show_elevation": true,
"show_safety_colors": true,
"show_why_this_route": true,
"show_street_by_street": true,
"city": "los_angeles",
"host": { "name": "Tom", "slug": "tom", "image_url": null },
"route": {
"distance_m": 833.5,
"duration_s": 200,
"polyline": "ycunEbsoqUMu@EU??M?YH{Er…",
"geometry": { "type": "LineString", "coordinates": [[-118.377621, 34.028292], [-118.37735, 34.028364], "…"] },
"stops": [
{ "name": "Home", "location": [-118.377621, 34.028292] },
{ "name": "Bakery", "location": [-118.378755, 34.027277] },
{ "name": "Park", "location": [-118.37675, 34.02727] }
]
},
"route_status": "ready",
"images": [],
"created_at": "2026-10-20T22:51:32.816+00:00",
"updated_at": "2026-10-20T22:51:32.816+00:00"
}
Custom routes
Get a route
Read one custom route, with its line, stops, status and images
curl 'https://api.cyclemate.club/custom-routes/78360cf7-0f4a-48f1-91ca-25e83db44aff/' \
-H 'Authorization: Bearer ACCESS_TOKEN'
{
"id": "78360cf7-0f4a-48f1-91ca-25e83db44aff",
"alias": "culver-city-coffee-loop-1792536692813",
"url": "https://cyclemate.club/explore/los-angeles/routes/culver-city-coffee-loop-1792536692813",
"name": "Culver City coffee loop",
"description": "An easy Sunday loop with a stop at the bakery.",
"emoji": "☕",
"privacy": "public",
"route_mode": "dynamic",
"categories": [{ "slug": "coffee", "name": "Coffee", "emoji": "☕" }],
"show_elevation": true,
"show_safety_colors": true,
"show_why_this_route": true,
"show_street_by_street": true,
"city": "los_angeles",
"host": { "name": "Tom", "slug": "tom", "image_url": null },
"route": {
"distance_m": 833.5,
"duration_s": 200,
"polyline": "ycunEbsoqUMu@EU??M?YH{Er…",
"geometry": { "type": "LineString", "coordinates": [[-118.377621, 34.028292], [-118.37735, 34.028364], "…"] },
"stops": [
{ "name": "Home", "location": [-118.377621, 34.028292] },
{ "name": "Bakery", "location": [-118.378755, 34.027277] },
{ "name": "Park", "location": [-118.37675, 34.02727] }
]
},
"route_status": "ready",
"images": [],
"created_at": "2026-10-20T22:51:32.816+00:00",
"updated_at": "2026-10-20T22:51:32.816+00:00"
}
Overview
Returns one route. Public and unlisted routes are readable by any signed-in caller; a private route only by its hosts.Authentication
A Login with Cyclemate access token, or a signed-in rider’s session token. Every route belongs to a rider: your app acts for the rider who connected it, and the route is theirs. Your app reaches only the routes it created: a route the rider made in our apps, or through another app, answers404.
Authorization: Bearer ACCESS_TOKEN
Rate limits
Riders and apps acting for them may make 60 requests a minute; a request that routes betweenstops counts once per hop. Every rider may create 10 routes in any 24 hours, counted across our apps and every connected app: the eleventh answers 429 with code: "daily_limit" and the seconds until a slot frees.
Path parameters
string
required
The route’s id.
Response body
string
The route’s id, a UUID string. Pass it as
event_id to POST /multi-modal/v2/ to ride it.string
The route’s link slug, chosen or generated from the name.
string
The route’s page on cyclemate.club.
string
string
string
string
public, unlisted or private.string
dynamic (we re-plan the line between the stops as roads change) or fixed (the line is kept as given).object
The page theme, when one is set. The same object you send.
array
Each
{ slug, name, emoji }.string
Present on a group ride, with
end_time, and max_rsvps and rsvp_questions when set.boolean
With
show_safety_colors, show_why_this_route and show_street_by_street: which sections the page shows.string
The city the route is in:
london, new_york_city, los_angeles or bay_area.object
The rider who created the route:
{ name, slug, image_url }. null once that rider has deleted their account.object
distance_m, duration_s, polyline (precision 5), geometry (a GeoJSON LineString; omitted in lists) and stops: the start then every stop in order, each { name, location }, with hidden: true on a shaping point that is not shown as a stop.string
pending while we match the line to the road network (a fixed route) or plan it between the stops (a dynamic route), ready once turn-by-turn steps and the safety summary are stored, unavailable when the line could not be matched or no route exists between the stops. A pending route is already viewable and rideable; poll every few seconds.array
Each
{ id, url, status, cover }. status is pending until our image check has run, then approved. Hosts see every image with its status; everyone else sees approved images only.string
string
curl 'https://api.cyclemate.club/custom-routes/78360cf7-0f4a-48f1-91ca-25e83db44aff/' \
-H 'Authorization: Bearer ACCESS_TOKEN'
{
"id": "78360cf7-0f4a-48f1-91ca-25e83db44aff",
"alias": "culver-city-coffee-loop-1792536692813",
"url": "https://cyclemate.club/explore/los-angeles/routes/culver-city-coffee-loop-1792536692813",
"name": "Culver City coffee loop",
"description": "An easy Sunday loop with a stop at the bakery.",
"emoji": "☕",
"privacy": "public",
"route_mode": "dynamic",
"categories": [{ "slug": "coffee", "name": "Coffee", "emoji": "☕" }],
"show_elevation": true,
"show_safety_colors": true,
"show_why_this_route": true,
"show_street_by_street": true,
"city": "los_angeles",
"host": { "name": "Tom", "slug": "tom", "image_url": null },
"route": {
"distance_m": 833.5,
"duration_s": 200,
"polyline": "ycunEbsoqUMu@EU??M?YH{Er…",
"geometry": { "type": "LineString", "coordinates": [[-118.377621, 34.028292], [-118.37735, 34.028364], "…"] },
"stops": [
{ "name": "Home", "location": [-118.377621, 34.028292] },
{ "name": "Bakery", "location": [-118.378755, 34.027277] },
{ "name": "Park", "location": [-118.37675, 34.02727] }
]
},
"route_status": "ready",
"images": [],
"created_at": "2026-10-20T22:51:32.816+00:00",
"updated_at": "2026-10-20T22:51:32.816+00:00"
}
Variants
Polling for the plan
After a create or a route edit,route_status is pending for a few seconds. Poll this endpoint until it reads ready (steps and the safety summary are stored) or unavailable (the line could not be matched, or no route exists between the stops: edit the route to try again).
Error responses
Every error body is{ "error", "code" }; error is a sentence, code is stable.
400 Bad request
Invalid body or query: the message names the field.code is invalid_body, or for a route input outside_coverage (the route starts outside the cities we cover), cross_city (its stops span two cities) or no_route (no cycling route exists between two stops).
{ "error": "unknown field: colour", "code": "invalid_body" }
401 Unauthorized
Missing or invalid credentials, or an anonymous caller.{ "error": "Missing user credentials" }
404 Not found
No route with that id that you may see. A private route you do not host, a route you may read but not edit, and a route your app did not create all answer404, never 403.
{ "error": "Custom route not found", "code": "not_found" }
405 Method not allowed
{ "error": "Method not allowed", "code": "method_not_allowed" }
429 Too many requests
The burst limit, with aRetry-After header; or the daily creation cap, which adds code: "daily_limit".
{ "error": "Daily limit of 10 custom routes reached", "code": "daily_limit", "retry_after_seconds": 3600 }
500 Internal server error
{ "error": "Internal server error", "code": "internal_error" }