Another "document it, so no one else has to research it" post

I've been using TfL's API a lot recently - in particular live arrivals at a station/stop. I've started making my application actually useful for people who don't live at my stop, by replacing the hard-coded station identifiers for calls to the search API.

Some background

In the UK, we have a system called NaPTAN (National Public Transport Access Nodes, that is essentially a large dataset of uniquely identified features of public transportation. For example - on a bus route, every bus stop will have an identifier (and the one across the road likely has a different one). Platforms and individual station entrances are also given identifiers.

In London, the variety of modes of transport allows for interchanges between them, often to many in one place. TfL's approach to handling these interchanges is to term them as HUB stations - for example, Stratford - HUBSRA - brings together:

  • Tube (Jubilee & Central lines) - 940GZZLUSTD,
  • DLR (Bank and Woolwich branches) - 940GZZDLSTD,
  • Overground - 910GSTFD,
  • TfL Rail (separate from tube, even when it becomes the Elizabeth line) - 910GSTFD,
  • National Rail (C2C & Greater Anglia) - 910GSTFD,
  • and Bus modes - 490G000773.

The main benefit of this is that if you search for a route between, say, Stratford and West Ham, you don't need to make searches for the stop-points at each mode and instead are given the Jubilee, DLR and Bus routes as if they started from the same place.

These aren't proper NaPTAN codes however, and TfL's StopPoint API won't always work with them. For example, if you attempt to get upcoming arrivals at /StopPoint/HUBSRA/Arrivals you will be given the ill-informative [] empty response.

Working with HUB stations

In this case you do need to get individual station codes for each mode. The trick is to get the StopPoint's information, for example Woolwich Arsenal, which will give you a comprehensive list of lines served (note, these include bus routes). Under the lineGroup property each line's station ATCO code and stop-point NaPTAN reference is included.

So the process becomes:

  1. Fetch /StopPoint/{id} for the HUB station,
  2. Scan .lineGroupModes for the modes you require and the lines they are served by,
  3. Scan .lineGroup for those lines to find their .stationAtcoCode NaPTAN IDs.

Illustrated in Python, when you only want particular modes: