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

Route through waypoints (curl)
# 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]]}'

Sends a real request, with your key or the keyless allowance.

Request body

FieldTypeDescription
waypointsrequiredarray2 to 100 points, [lon, lat]. A leg (between two waypoints) may be at most 60 km in a straight line.
profilestring: hike | run | bike
max_sacintegerHike and run: the hardest SAC grade allowed, 1 (T1) to 6 (T6). Refused for a bike.
marked_trailsstring: strict | prefer | offHike and run: how strongly to keep to waymarked trails. Refused for a bike.
avoid_pavedboolean
hillsnumberFrom -1 (avoid climbing) to 1 (seek it).
shortestbooleanThe shortest route under the other settings, not the most suitable.
bicycle_typestring: road | hybrid | mountainBike only.
straight_legsarrayLeg numbers, counted from 0 (leg 0 joins waypoint 0 to waypoint 1), to draw straight, on no mapped path.
snap_radius_mnumberHow far a waypoint may lie from a usable path.

Response

FieldTypeDescription
schemastring: route/1
engineobject
statusstring: complete | best_effortcomplete: every leg is a proven cheapest path under the settings. best_effort: a leg hit the search budget (a warning says which).
settingsobjectWhat was applied, in the request’s words.
settings_defaultedarray
distance_mnumber
ascent_mnumber
descent_mnumber
ascent_methodstring
timeobject or nullHike only: moving time from the calibrated trailsplits-hike/2 model, with its scope and error. Null for run and bike.
surfacesobjectPaved, gravel, trail and unknown metres; mapped or assumed from the kind of way; stretches placed along the route.
hazardsarray
waysobject
difficultyobject or nullSAC-tagged metres by grade, untagged path and road metres, and the hardest stretch.
warningsarray
unknownsarray
geometryobject
legsarray
snapsarray
maneuversarray
cap_alternativeobject or nullWhen 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" }
FieldDefault
waypointsrequired2 to 100 points, [lon, lat]. A leg (between two waypoints) may be at most 60 km in a straight line
profilehikehike, run or bike
max_sac3Hike 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_trailspreferHike and run: strict (strongly prefer waymarked trails), prefer or off. Bikes: off
avoid_pavedfalsePrefer unpaved ways
hills0−1 (avoid climbing) to 1 (seek it)
shortestfalseThe shortest route under the other settings, not the most suitable
bicycle_typeBike: 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_mHow far a waypoint may lie from a usable path, 10 to 5,000 m
callerOptional 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

FieldMeaning
schemaroute/1
statuscomplete: 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_defaultedWhat 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_mAlong the route
ascent_m, descent_m, ascent_methodClimb by the elevation contract (route-smoothed-100m)
timeHike 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)
surfacesPaved, 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
waysMetres by way type; marked metres; max_sac; ford, aided, informal and steps metres
difficultySAC-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
geometryGeoJSON 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_alternativeWhen the grade cap was left at its default and a harder path is much shorter: max_sac, distance_m, ascent_m, minutes; else null
timingMilliseconds and work, for logs

Errors

{ "error": { "code", "message" } }.

HTTPcodeWhen
400invalid_requestNot 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
405method_not_allowedAny method but POST (and OPTIONS)
413body_too_largeOver 1 MB
422leg_too_longA leg over 60 km in a straight line: add waypoints
422snap_failedA waypoint is farther than the snap radius from any path the settings allow
422no_path_under_settings, no_path_in_corridor, disconnectedNo 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
422bad_straight_legA straight leg the router cannot draw
422budget_exhausted, corridor_too_largeThe search ran out of budget, or the area is too large for one request: add waypoints
503overloadedThe 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.