Data APIdocs

Public transport

The transport layer in Data API is built differently from POIs. Where POIs are merged from three overlapping sources, transport is sourced from a single authoritative provider and structured around what's actually timetabled.

This page explains where the data comes from, what we mean by "station" vs "stop", how lines and agencies fit together, and how to read the frequency rollups in the API.

TravelTime's transport graph is the authoritative source

The transport graph behind these endpoints is the same one the sibling TravelTime Routing API uses — two products, one underlying dataset. It ingests GTFS-style timetable data from operators and agencies, both public feeds and licensed ones, and produces a unified national transport graph: every stop, every line, every timetabled trip, with consistent IDs and operator metadata.

Why a single source instead of a merged layer like POIs?

  • GTFS is messy in practice. Raw feeds from operators disagree on stop names, IDs, and geometry; a single light-rail station can appear under five different IDs across operator boundaries. Merging is non-trivial and easy to get wrong.
  • A consolidated graph is what the question actually needs. "How well can you get around from here" requires a coherent network with timetable data. Two half-merged sources don't add up to a working graph.
  • Operators come and go; trips run on schedules. Getting it right means handling service patterns, calendar dates, and routing semantics. We outsource that and consume the result.

Stops vs stations

Two important concepts that look similar and aren't:

  • A stop is a single physical boarding location — a bus stop pole, a tube platform, a ferry pier. Stops have unique IDs in the underlying GTFS data and they're what trips actually call at.
  • A station is a logical grouping of stops that the public thinks of as one place. "Liverpool Street" in London has dozens of stops underneath it (Central line eastbound, Central line westbound, the National Rail concourse, several bus stops outside) — they all roll up into one station.

We derive stations from stops by spatial clustering combined with name similarity. The result: when you query GET /v1/pt/stations/near, you get back recognisable place names ("Waterloo", "Manchester Piccadilly") rather than a forest of granular stop rows. When you need stop-level detail, the line endpoints return the stops a line calls at on its way through a station.

Lines, trips, and frequency

Three nested concepts:

  • A line is a named timetabled service. "Central line", "London Overground", a specific bus route number, a specific train operator's named service. Lines have an operating agency and a primary transport mode.
  • A trip is one journey along a line — one specific timetabled run from one terminus to another. A line that runs every 4 minutes for three hours has roughly 45 trips in that window.
  • A trip variant is a distinct routing pattern. "Most morning trips terminate at A; three a day terminate at B" — those are two variants of the same line.

Our frequency rollups count trips at a station within an arrival-time window. We use the transport graph's weekday-morning arrival period as our representative slice — roughly the busiest commute hour. So when an endpoint returns {station: "Waterloo", line: "Northern", trip_count: 36}, you should read it as "36 Northern Line trips called at Waterloo in our representative morning hour." It's a density signal, not a real-time number.

Agencies and modes

Every line carries:

  • An agency — who runs it. Transport for London, Northern Trains, a coach operator, a ferry company. The full agency list per country is at GET /v1/pt/agencies.
  • A modesubway, train, tram, light_rail, bus, ferry, cable_car, coach, plus anything else GTFS recognises. Available modes per country at GET /v1/pt/modes.

Mode is useful for filtering: you might want to count "rail-served" (rail, subway, tram, light rail) separately from "bus-served." Agency is useful for branded queries — "stations operated by ScotRail" or "lines on the Tyne and Wear Metro."

Interchange awareness

When you fetch a station's detail, you get the lines that call there. When you fetch a specific line's stop list, each stop carries the other lines that interchange at the same station. So a query for "Stratford on the Jubilee line" returns the Jubilee-line stop plus a list saying "you can also catch Central, Elizabeth, DLR, Overground, and National Rail here."

The interchange computation is done from the network graph — two lines are interchangeable at a station if their stops sit close enough together and the station name is consistent. You don't need to compute this client-side; the API surfaces it directly via GET /v1/pt/lines/{line_sid}/stops/{station_sid}/interchanges.

Finding your way around the endpoints

The transport endpoints divide into three jobs. Everything is a GET and everything takes country (default GB).

Get oriented. /meta reports what the country's graph contains and when it was built. /search takes q and matches stations and lines by name — the right entry point when a user typed something. /highlights returns superlatives (busiest interchanges, longest lines) for an overview screen.

Find stations. /stations lists them inside a bbox, which is required — there is no country-wide listing. /stations/near takes lng + lat and returns stations ordered by distance, which is what you want whenever you have a coordinate rather than a rectangle. /stations/{sid} is the detail view, including the lines that call there.

curl -H "Authorization: Bearer $POI_API_KEY" \
  "$POI_BASE/v1/pt/stations/near?country=GB&lat=51.5033&lng=-0.1145&radius_m=800&limit=10"

Follow lines. /lines filters by agency, mode, or q, and serves_stations takes a comma-separated list of station ids with AND semantics — every id must be served, so it answers "which lines connect all of these?" rather than "any of these". /lines/{sid} gives the stop sequence.

Ids throughout are 12-character stable_id hashes rather than raw GTFS ids, so a deep link into a station or line survives a pipeline rerun.

Where next