Pace plan for a course

POST https://api.trailsplits.com/v1/pace-plan

Checked by a live probe Key optional · 500 credits a day without one

Splits every kilometre or mile for one course, from a target time or your pace on the flat: slower where it climbs or descends steeply (effort km = distance km + climb m / 100 + descent m / 300). Each split ends on its mark and names its terrain (steady climb, technical descent, …). Uses the file’s own heights, or the terrain model’s. The engine of the Pace & Split Calculator. A pacing rule, not a physiological model. 2 credits.

Example

Pace plan for a course (curl)
# Without a key, leave out the Authorization line (500 credits a day per address).
curl --fail-with-body 'https://api.trailsplits.com/v1/pace-plan' \
  -H "Authorization: Bearer $TRAILSPLITS_API_KEY" \
  -H 'Content-Type: application/json' \
  --data '{"geometry":{"type":"LineString","coordinates":[[7.7475,46.0207],[7.74,46.018],[7.73,46.014],[7.7174,46.0091]]},"target_time":"0:20:00"}'

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

Request body

FieldTypeDescription
gpxstringOne line as GPX. Or geometry, not both. Limits: 5,000 points, 300 km, 1 MB; one part only.
geometryobjectA GeoJSON LineString, [lon, lat, height?].
target_timestringh:mm:ss, mm:ss or minutes, e.g. "2:30:00". One of target_time, target_seconds or flat_pace_s_per_km.
target_secondsnumber
flat_pace_s_per_kmnumberYour pace on level ground, seconds per km.
unitstring: km | miSplits every kilometre or mile; paces are always per kilometre.

Response

FieldTypeDescription
schemastring: pace-plan/1
engineobject
settings_defaultedarray
unitstring: km | mi
targetobject
courseobject
effortobject
splitsarray
unknownsarray
Full response schema (JSON Schema)
{
  "type": "object",
  "properties": {
    "schema": {
      "type": "string",
      "enum": [
        "pace-plan/1"
      ]
    },
    "engine": {
      "type": "object"
    },
    "settings_defaulted": {
      "type": "array",
      "items": {
        "type": "string"
      }
    },
    "unit": {
      "type": "string",
      "enum": [
        "km",
        "mi"
      ]
    },
    "target": {
      "type": "object",
      "properties": {
        "kind": {
          "type": "string",
          "enum": [
            "time",
            "flat_pace"
          ]
        },
        "seconds": {
          "type": "number",
          "description": "The finish time the splits add up to, to 0.1 s."
        },
        "flat_pace_s_per_km": {
          "type": "number"
        }
      },
      "required": [
        "kind",
        "seconds"
      ]
    },
    "course": {
      "type": "object",
      "properties": {
        "distance_m": {
          "type": "number"
        },
        "gain_m": {
          "type": "number"
        },
        "loss_m": {
          "type": "number"
        },
        "heights": {
          "type": "string",
          "enum": [
            "recorded",
            "terrain",
            "none"
          ]
        },
        "smoothing": {
          "type": "string"
        }
      },
      "required": [
        "distance_m",
        "gain_m",
        "loss_m",
        "heights",
        "smoothing"
      ]
    },
    "effort": {
      "type": "object",
      "properties": {
        "rule": {
          "type": "string"
        },
        "flat_pace_s_per_km": {
          "type": "number",
          "description": "The pace on level ground that the target means on this course."
        }
      },
      "required": [
        "rule",
        "flat_pace_s_per_km"
      ]
    },
    "splits": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "label": {
            "type": "string"
          },
          "from_m": {
            "type": "number"
          },
          "to_m": {
            "type": "number"
          },
          "distance_m": {
            "type": "number"
          },
          "gain_m": {
            "type": "number"
          },
          "loss_m": {
            "type": "number"
          },
          "pace_s_per_km": {
            "type": "number"
          },
          "seconds": {
            "type": "number"
          },
          "cumulative_seconds": {
            "type": "number"
          },
          "character": {
            "type": "string",
            "enum": [
              "runnable",
              "steady climb",
              "major climb",
              "fast descent",
              "technical descent",
              "rolling mountain terrain"
            ]
          }
        },
        "required": [
          "label",
          "from_m",
          "to_m",
          "distance_m",
          "gain_m",
          "loss_m",
          "pace_s_per_km",
          "seconds",
          "cumulative_seconds",
          "character"
        ]
      }
    },
    "unknowns": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "code": {
            "type": "string"
          },
          "message": {
            "type": "string"
          }
        },
        "required": [
          "code",
          "message"
        ]
      }
    }
  },
  "required": [
    "schema",
    "engine",
    "unit",
    "target",
    "course",
    "effort",
    "splits",
    "unknowns"
  ]
}

Errors

400, 413, 422, plus the gateway's 401, 429 and 503 (error conventions). A refused request costs no credits.

Contract

Splits for a course, every kilometre or mile, from a target time or from your pace on the flat. They are slower where the course climbs and descends steeply. The trail splits behind the product's name, as an API.

The model

  • Effort. Each split's effort in kilometres is its distance, plus 1 km for every 100 m of climb, plus 1 km for every 300 m of descent.
  • A target time is shared among the splits in proportion to their effort.
  • A flat pace (your pace on level ground) is multiplied by the whole course's effort to give the target, which is then shared the same way. target.seconds states the result.
  • Heights are smoothed by a moving average over ±100 m along the line (the router's method) before climb is counted, so GPS jitter is not climb.
    • A file's own heights are used whenever it carries any, as the page uses them. Points without one are filled from their neighbours (heights_partial when under 90 % carry one).
    • A file without heights takes the terrain model's at its points (course.heights: terrain).
    • When the terrain model does not answer either, the plan treats the course as flat and says so (heights_unknown).
  • What it is not. A pacing rule from race planning, not a physiological model. It knows nothing of surface, altitude, heat or fatigue. character names each split's terrain ("steady climb", "technical descent") so a runner can adjust.

Request

{ "gpx": "<gpx…>", "target_time": "2:30:00" }
{ "geometry": { "type": "LineString", "coordinates": [[lon, lat, height?], …] }, "flat_pace_s_per_km": 330 }
{ "gpx": "<gpx…>", "target_seconds": 1500, "unit": "mi" }
Field
gpx or geometryone of themOne line (a GPX track segment or route, or a GeoJSON LineString), as for route analysis. Limits: 5,000 points, 300 km, 1 MB. A file of several parts is refused: a pace plan is for one course
target_timeone of the threeA string: "h:mm:ss", "mm:ss" or minutes, e.g. "2:30:00"; minutes and seconds after the first field are under 60
target_secondsThe same in seconds
flat_pace_s_per_kmYour pace on level ground, 120 to 1,800 s per km (2:00 to 30:00 min/km)
unitkm (default) or miSplits every kilometre or every mile; paces are always per kilometre
callerOptional label for logs and receipts

Response

FieldMeaning
schemapace-plan/1
settings_defaultedunit when not given
unitkm or mi
targetkind (time \flat_pace), seconds (the finish time the splits add up to, to 0.1 s), and flat_pace_s_per_km when that was given
coursedistance_m, gain_m, loss_m (on the smoothed heights), heights (recorded \terrain \none), smoothing in words
effortrule in words, and flat_pace_s_per_km: the pace on level ground that the target means on this course
splits[]Each ends on its kilometre or mile mark, however far apart the file's points are (the last ends at the finish): label ("1 km", "2 mi", the last one to two decimals), from_m, to_m, distance_m, gain_m, loss_m, pace_s_per_km, seconds, cumulative_seconds, character (runnable, steady climb, major climb, fast descent, technical descent, rolling mountain terrain)
unknowns[]code, message: heights_unknown, heights_partial, target_very_fast (the target means a flat pace under 2:00 min/km), target_very_slow (over 30:00 min/km)
timingtotal_ms

Errors

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

HTTPcodeWhen
400invalid_requestNot JSON, an unknown field, both or neither of gpx/geometry, not exactly one target, a target or unit out of range
400invalid_gpx, invalid_coordinatesAs for route analysis
405method_not_allowedAny method but POST (and OPTIONS)
413body_too_large, too_many_pointsOver 1 MB, or over 5,000 points
422too_long, no_lineOver 300 km, or no line of two points
422several_partsMore than one part
422too_shortUnder 0.2 km
422budget_exhaustedThe job's 25 s budget ran out, also while waiting for terrain heights

Versioning

Additive fields may appear in pace-plan/1. A change of meaning (another effort rule, for one) is pace-plan/2.