Guides
Docs
Journey planning that knows about hills
Most journey planners rank routes by time, then by how far you walk. Neither
tells you that one option's ten-minute walk is up a hill. This guide plans a
journey across Great Britain and compares the options by how much climbing
their walks involve, using the terrain figures TransCAPI adds to
every walking leg.
What you need
- Python 3.9 or later, and
pip install requests - A TransCAPI key: sign up free,
then
export TRANSCAPI_KEY=your_key
The code
Sheffield station to Crookes is 4 km, and Crookes sits about 180 metres above the station. It's a good test.
import os
import requests
API = "https://api.transcapi.com/v1"
HEADERS = {"X-Api-Key": os.environ["TRANSCAPI_KEY"]}
# Sheffield station to Crookes: a short hop, but up a big hill
plan = requests.get(f"{API}/journey.json", headers=HEADERS, params={
"from_lat": 53.3781, "from_lon": -1.4623,
"to_lat": 53.3855, "to_lon": -1.5130,
}).json()
options = plan["itineraries"] + plan["alternatives"].get("walk", {}).get("itineraries", [])
for it in options:
modes = " > ".join(leg["mode"].lower() for leg in it["legs"])
walk = it.get("walking") or {}
print(f"{it['duration_s'] / 60:4.0f} min {modes:<32} "
f"walk {it['walk_distance_m']:5.0f} m, climb {walk.get('climb_m', '?')} m "
f"({walk.get('shape', 'n/a')}), ~{walk.get('calories_kcal', '?')} kcal")
Run it:
$ python journey_climb.py
38 min walk > tram > walk > bus > walk walk 482 m, climb 15 m (gentle climb), ~35 kcal
42 min walk > bus > walk walk 1265 m, climb 17 m (gentle climb), ~79 kcal
41 min walk > bus > walk > bus > walk walk 754 m, climb 7 m (flat), ~48 kcal
40 min walk > bus > walk > bus > walk walk 1122 m, climb 25 m (steady climb), ~76 kcal
55 min walk walk 4008 m, climb 182 m (steep climb), ~302 kcal
Real output, 29 September 2026.
Walking takes 55 minutes, not far off the bus, but the bus does the 180 metres of climbing for you. Among the transit options, the third walks 750 m and climbs 7 m, while the fourth walks 1.1 km and climbs 25 m. That's worth knowing if you're carrying shopping, pushing a buggy or using a stick.
The figures
Each itinerary has a walking summary:
"walking": {
"climb_m": 180,
"descent_m": 17,
"steepest_up_pct": 43.6,
"shape": "steep climb",
"direction": "up",
"calories_kcal": 208,
"complete": true
}
Each walking leg has the same figures in terrain, plus
steepest_down_pct, so you can say which part of the journey is
the hard bit. shape gives a label you can show as it is:
flat, gentle climb, steady climb,
steep climb, the three matching descents, rolling or
hilly.
Where the numbers come from
Heights come from Ordnance Survey's OS Terrain 50, sampled every 25 metres along the walk's actual path and smoothed, so small errors in the model don't turn a flat street into a climb. Calories use a published model of the energy cost of walking on a slope, for a 70 kg adult. These are estimates for comparing routes, not measurements. A 50-metre grid softens short, sharp rises, so a flight of steps may read gentler than it is. The journey docs cover the method and its limits.
Combining it with walking limits
max_walk_distance caps the longest single walk and
max_total_walk_distance the total, and both are enforced rather
than just preferred. Use them to rule options out, then rank what's left by
walking.climb_m:
params["max_walk_distance"] = 500
options = sorted(plan["itineraries"],
key=lambda it: (it.get("walking") or {}).get("climb_m", 0))
What else is in a journey
- Bus, tram and metro legs follow the road or track, from the operator's published route, ready to draw on a map.
include_steps=trueadds turn-by-turn walking directions.- Buses already running carry their live position and delay.
- Fare estimates where fare data exists, currently bus in Yorkshire and West Sussex.
- Journeys can be planned up to two months ahead with
datetime, or to arrive by a time witharrive_by=true.
Coverage is England, Scotland and Wales, by bus, coach, rail, Underground,
metro, tram and ferry. Journey planning pauses on Sundays from 03:00 to 04:30
UK time while the week's timetables load. During that window it returns
503 with Retry-After, and every other endpoint keeps
working.
Try it. A free key gives you 1,000 requests a day with no card. Every endpoint covers Great Britain: see coverage for the parts that are regional.