How reliable is my bus?

A timetable says when a bus should come. This guide asks how often it actually does: by route, by hour and at a single stop, and what that means for the chance of making a change. The figures are measured, from every tracked bus passing every timing point.

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

import os

import requests

API = "https://api.transcapi.com/v1"
HEADERS = {"X-Api-Key": os.environ["TRANSCAPI_KEY"]}

# 1. How has a route performed over the last four weeks?
route = requests.get(f"{API}/bus/reliability.json", headers=HEADERS,
                     params={"operator": "FSYO", "line": "51"}).json()
print(f"Line {route['line']}: {route['on_time_pct']}% on time, "
      f"{route['very_late_pct']}% very late, "
      f"leaves the first stop on time {route['first_stop_on_time_pct']}% of the time")

# ... and when is it worst, between 6am and 10pm?
worst = min((h for h in route["by_hour"] if 6 <= h["hour"] <= 22 and h["observations"] >= 200),
            key=lambda h: h["on_time_pct"])
print(f"  worst hour: {worst['hour']:02d}:00, {worst['on_time_pct']}% on time")

# 2. How late is "late" at my stop?
stop = requests.get(f"{API}/bus/stop_reliability/370022870.json", headers=HEADERS).json()
for line in stop["lines"]:
    print(f"  at the stop, line {line['line']}: 1 bus in 10 is more than "
          f"{line['p90_delay_s'] // 60} min late")

# 3. Will I make my change? Plan a journey and read its connections
plan = requests.get(f"{API}/journey.json", headers=HEADERS, params={
    "from_lat": 53.4302, "from_lon": -1.3568,    # Rotherham
    "to_lat": 53.5543, "to_lon": -1.4776,        # Barnsley
    "datetime": "2026-10-01T09:00:00",
}).json()
for it in plan["itineraries"]:
    changes = it.get("connections", [])
    if not changes:
        continue
    lines = " > ".join(f"{c['from_line']} to {c['to_line']}" for c in changes)
    chance = it["connection_chance_pct"]
    print(f"  {it['start_time'][11:16]} {lines}: "
          f"{str(chance) + '% chance of making every change' if chance is not None else 'no history'}")

Run it:

$ python bus_reliability.py
Line 51: 73.8% on time, 4.1% very late, leaves the first stop on time 91.9% of the time
  worst hour: 16:00, 61.9% on time
  at the stop, line 95: 1 bus in 10 is more than 5 min late
  at the stop, line 51: 1 bus in 10 is more than 7 min late
  09:15 X3 to NT: 84% chance of making every change
  09:25 138 to X2 > X2 to NT: 30% chance of making every change
  09:23 X2 to NT: 81% chance of making every change
  09:26 TT to YELL > YELL to NT: no history

Real output, 30 September 2026. The 51 in Sheffield leaves on time but loses it through the day, worst at 4pm with the school run. And the 09:25 option from Rotherham looks fine on a timetable but works less than a third of the time.

Route and line punctuality

/v1/bus/reliability.json takes a route_id (from /v1/bus/routes.json), or an operator code and line, over the last days complete days (default 28). It returns the share on time, early, late and very late, mean delay, the on-time rate at timing points and at the first and last stops, and the same by hour. Ask with operator alone to list every line it runs, busiest first.

"On time" is the standard the Traffic Commissioners apply: no more than a minute early and less than 6 minutes late. The response spells the definitions out.

At one stop

/v1/bus/stop_reliability/{atcocode}.json gives each line at a stop with the median and 90th-percentile delay. The 90th percentile is the honest version of "usually on time": the lateness to allow for if you can't afford to miss it.

Making a connection

Every journey plan with a change carries connections: the time to spare at each change and chance_pct, how often that change actually worked for those exact buses over recent weeks. The whole journey's chance is connection_chance_pct. Where there's no history, from a train, tram or an untracked bus, it's null rather than a guess.

Coverage

Punctuality is measured wherever operators report bus positions to the Bus Open Data Service: most of England, some of Wales and little of Scotland. Northern England and West Sussex go back to 4 August 2026; everywhere else from 30 September 2026, so figures there firm up over the coming weeks.

All the parameters are in the bus docs and the journey docs.


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.