Route through waypoints
POST https://api.trailsplits.com/v1/route
Checked by a live probe Key optional · 500 credits a day without one
A walking, running or cycling route through 2 to 100 waypoints, on signed trails by default. Returns the route as a GeoJSON line with its legs and the same facts as round trips and route analysis: climb, the hike time with its scope, surfaces, ways, SAC grades, fords and aided passages, turn-by-turn steps and every unknown. The same router as the Valhalla-compatible POST /route/v1, in the /v1 style: [lon, lat] waypoints, flat snake_case settings, errors as { error: { code, message } }. Credits as /route/v1: 1, plus 1 per further 10 waypoints.
Example
# Without a key, leave out the Authorization line (500 credits a day per address).
curl --fail-with-body 'https://api.trailsplits.com/v1/route' \
-H "Authorization: Bearer $TRAILSPLITS_API_KEY" \
-H 'Content-Type: application/json' \
--data '{"waypoints":[[7.7475,46.0207],[7.7174,46.0091],[7.7355,46.0036]]}' Request body
| Field | Type | Description |
|---|---|---|
waypointsrequired | array | 2 to 100 points, [lon, lat]. A leg (between two waypoints) may be at most 60 km in a straight line. |
profile | string: hike | run | bike | |
max_sac | integer | Hike and run: the hardest SAC grade allowed, 1 (T1) to 6 (T6). Refused for a bike. |
marked_trails | string: strict | prefer | off | Hike and run: how strongly to keep to waymarked trails. Refused for a bike. |
avoid_paved | boolean | |
hills | number | From -1 (avoid climbing) to 1 (seek it). |
shortest | boolean | The shortest route under the other settings, not the most suitable. |
bicycle_type | string: road | hybrid | mountain | Bike only. |
straight_legs | array | Leg numbers, counted from 0 (leg 0 joins waypoint 0 to waypoint 1), to draw straight, on no mapped path. |
snap_radius_m | number | How far a waypoint may lie from a usable path. |
Response
| Field | Type | Description |
|---|---|---|
schema | string: route/1 | |
engine | object | |
status | string: complete | best_effort | complete: every leg is a proven cheapest path under the settings. best_effort: a leg hit the search budget (a warning says which). |
settings | object | What was applied, in the request’s words. |
settings_defaulted | array | |
distance_m | number | |
ascent_m | number | |
descent_m | number | |
ascent_method | string | |
time | object or null | Hike only: moving time from the calibrated trailsplits-hike/2 model, with its scope and error. Null for run and bike. |
surfaces | object | Paved, gravel, trail and unknown metres; mapped or assumed from the kind of way; stretches placed along the route. |
hazards | array | |
ways | object | |
difficulty | object or null | SAC-tagged metres by grade, untagged path and road metres, and the hardest stretch. |
warnings | array | |
unknowns | array | |
geometry | object | |
legs | array | |
snaps | array | |
maneuvers | array | |
cap_alternative | object or null | When the grade cap was left at its default and a harder path is much shorter: its max_sac, distance, climb and minutes. |
Full response schema (JSON Schema)
{
"type": "object",
"properties": {
"schema": {
"type": "string",
"enum": [
"route/1"
]
},
"engine": {
"type": "object"
},
"status": {
"type": "string",
"enum": [
"complete",
"best_effort"
],
"description": "complete: every leg is a proven cheapest path under the settings. best_effort: a leg hit the search budget (a warning says which)."
},
"settings": {
"type": "object",
"description": "What was applied, in the request’s words."
},
"settings_defaulted": {
"type": "array",
"items": {
"type": "string"
}
},
"distance_m": {
"type": "number"
},
"ascent_m": {
"type": "number"
},
"descent_m": {
"type": "number"
},
"ascent_method": {
"type": "string"
},
"time": {
"type": "object",
"nullable": true,
"description": "Hike only: moving time from the calibrated trailsplits-hike/2 model, with its scope and error. Null for run and bike."
},
"surfaces": {
"type": "object",
"description": "Paved, gravel, trail and unknown metres; mapped or assumed from the kind of way; stretches placed along the route."
},
"hazards": {
"type": "array",
"items": {
"type": "object",
"properties": {
"kind": {
"type": "string",
"enum": [
"ford",
"aid",
"via_ferrata"
]
},
"from_m": {
"type": "number"
},
"to_m": {
"type": "number"
},
"length_m": {
"type": "number"
},
"way_id": {
"type": "number",
"nullable": true
}
},
"required": [
"kind",
"from_m",
"to_m",
"length_m",
"way_id"
]
}
},
"ways": {
"type": "object"
},
"difficulty": {
"type": "object",
"nullable": true,
"description": "SAC-tagged metres by grade, untagged path and road metres, and the hardest stretch."
},
"warnings": {
"type": "array",
"items": {
"type": "object",
"properties": {
"code": {
"type": "string"
},
"message": {
"type": "string"
},
"location": {
"type": "array",
"items": {
"type": "number"
},
"nullable": true
}
},
"required": [
"code",
"message",
"location"
]
}
},
"unknowns": {
"type": "array",
"items": {
"type": "object",
"properties": {
"code": {
"type": "string"
},
"message": {
"type": "string"
}
},
"required": [
"code",
"message"
]
}
},
"geometry": {
"type": "object",
"properties": {
"type": {
"type": "string",
"enum": [
"LineString"
]
},
"coordinates": {
"type": "array",
"minItems": 2,
"items": {
"type": "array",
"items": {
"type": "number"
}
},
"description": "[lon, lat, height]"
}
},
"required": [
"type",
"coordinates"
]
},
"legs": {
"type": "array",
"items": {
"type": "object",
"properties": {
"leg": {
"type": "integer"
},
"distance_m": {
"type": "number"
},
"ascent_m": {
"type": "number"
},
"descent_m": {
"type": "number"
},
"from_snap_m": {
"type": "number"
},
"to_snap_m": {
"type": "number"
}
},
"required": [
"leg",
"distance_m",
"ascent_m",
"descent_m",
"from_snap_m",
"to_snap_m"
]
}
},
"snaps": {
"type": "array",
"items": {
"type": "object"
}
},
"maneuvers": {
"type": "array",
"items": {
"type": "object",
"properties": {
"instruction": {
"type": "string"
},
"type": {
"type": "string"
},
"location": {
"type": "array",
"items": {
"type": "number"
}
},
"distance_m": {
"type": "number"
}
},
"required": [
"instruction",
"type",
"location",
"distance_m"
]
}
},
"cap_alternative": {
"type": "object",
"nullable": true,
"description": "When the grade cap was left at its default and a harder path is much shorter: its max_sac, distance, climb and minutes."
}
},
"required": [
"schema",
"engine",
"status",
"settings",
"distance_m",
"ascent_m",
"descent_m",
"time",
"surfaces",
"hazards",
"warnings",
"unknowns",
"geometry",
"legs",
"maneuvers"
]
}Errors
400, 413, 422, 503, plus the gateway's 401, 429 and 503 (error conventions). A refused request costs no credits.
Contract
A walking, running or cycling route through your waypoints, in the /v1 family's style:
-
[lon, lat]waypoints and flat snake_case settings; - the route as a GeoJSON line, with its legs;
- the facts that round trips and route analysis give: climb, the hike time with its scope, surfaces, ways, tagged grades, fords and aided passages, and every unknown;
-
errors as
{ "error": { "code", "message" } }.
Request
{ "waypoints": [[7.7475, 46.0207], [7.7174, 46.0091], [7.7355, 46.0036]] }
{ "waypoints": [[8.4926, 47.3631], [8.4913, 47.3496]], "profile": "run", "max_sac": 2, "marked_trails": "strict" }
{ "waypoints": [[8.5402, 47.3779], [8.5417, 47.3667]], "profile": "bike", "bicycle_type": "hybrid" }
| Field | Default | |
|---|---|---|
waypoints | required | 2 to 100 points, [lon, lat]. A leg (between two waypoints) may be at most 60 km in a straight line |
profile | hike | hike, run or bike |
max_sac | 3 | Hike and run: the hardest SAC grade allowed, 1 (T1) to 6 (T6). Paths tagged harder are never used. When it is left at 3 and a harder path would be much shorter, a warning and cap_alternative say so |
marked_trails | prefer | Hike and run: strict (strongly prefer waymarked trails), prefer or off. Bikes: off |
avoid_paved | false | Prefer unpaved ways |
hills | 0 | −1 (avoid climbing) to 1 (seek it) |
shortest | false | The shortest route under the other settings, not the most suitable |
bicycle_type | Bike: road, hybrid or mountain | |
straight_legs | [] | Leg numbers (0 joins the first waypoint to the second) to draw straight, on no mapped path: across a glacier or a beach, say |
snap_radius_m | How far a waypoint may lie from a usable path, 10 to 5,000 m | |
caller | Optional label for logs and receipts |
A bike's settings are those of /route/v1 and the Planner: no grade cap below T6, marked trails off. max_sac and marked_trails are refused for a bike, and bicycle_type for hike and run.
Counting. In fields, waypoints and legs count from 0 (legs[].leg, straight_legs, snaps[].waypoint); in messages they count from 1, as people do ("leg 1 is 62.6 km").
Response
| Field | Meaning |
|---|---|
schema | route/1 |
status | complete: every leg is a proven cheapest path under the settings. best_effort: a leg hit the search budget and is the best route seen when it stopped (a search_budget_exhausted warning says which) |
settings, settings_defaulted | What was applied, in the request's words, and which fields were not given. snap_radius_m null is the router's own radius for the profile |
distance_m | Along the route |
ascent_m, descent_m, ascent_method | Climb by the elevation contract (route-smoothed-100m) |
time | Hike only: the calibrated trailsplits-hike/2 model's moving time, with applies (marked paths up to T3), its held-out error, outside_calibration with reasons, and every named model's minutes. Null for run and bike (time_not_estimated) |
surfaces | Paved, gravel, trail and unknown metres; what is mapped and what is assumed from the kind of way; stretches placed along the route |
hazards[] | kind (ford, aid, via_ferrata), from_m, to_m, length_m, way_id |
ways | Metres by way type; marked metres; max_sac; ford, aided, informal and steps metres |
difficulty | SAC-tagged metres by grade, untagged path metres, road metres, and the hardest stretch placed |
warnings[] | code, message, location ([lon, lat] or null); leg and meters where they apply (a straight leg, a defaulted grade cap) |
unknowns[] | code, message, length_m where it applies: as for round trips |
geometry | GeoJSON LineString, [lon, lat, height] |
legs[] | leg, distance_m, ascent_m, descent_m, and how far the route left each end's waypoint to join a path (from_snap_m, to_snap_m) |
snaps[] | Where joint snapping placed each waypoint, when it ran (it runs when a waypoint's nearest paths make a detour): waypoint (from 0), location, distance_m, nearest_m. Empty otherwise. A waypoint moved off its nearest path for another reason shows as a snap_moved warning with its location, and one more than 50 m from its path as start_snapped or end_snapped |
maneuvers[] | instruction, type, modifier, name, location; distance_m is the stretch leading up to this step from the one before |
cap_alternative | When the grade cap was left at its default and a harder path is much shorter: max_sac, distance_m, ascent_m, minutes; else null |
timing | Milliseconds and work, for logs |
Errors
{ "error": { "code", "message" } }.
| HTTP | code | When |
|---|---|---|
| 400 | invalid_request | Not JSON, an unknown field (including settings and locations), fewer than two or more than 100 waypoints, a waypoint off the map, a setting out of range or for the wrong profile |
| 405 | method_not_allowed | Any method but POST (and OPTIONS) |
| 413 | body_too_large | Over 1 MB |
| 422 | leg_too_long | A leg over 60 km in a straight line: add waypoints |
| 422 | snap_failed | A waypoint is farther than the snap radius from any path the settings allow |
| 422 | no_path_under_settings, no_path_in_corridor, disconnected | No path joins two waypoints under the settings, or none within the area searched; the message names the leg, and the error carries cap_alternative when a harder grade would join them |
| 422 | bad_straight_leg | A straight leg the router cannot draw |
| 422 | budget_exhausted, corridor_too_large | The search ran out of budget, or the area is too large for one request: add waypoints |
| 503 | overloaded | The worker's heavy-job queue is full; Retry-After |
Versioning
Additive fields may appear in route/1. A change of meaning or a removed field is route/2.