Unified API Part 3: RoT – Routes of Things

In Part 2 of this series, Dan gave examples of finding the “Locations of Things” using the Unified API. This week, I’ll focus on the “Routes of Things” – the lines, routes and services that join up the locations on the TfL network.

As before, all of the API examples in this page are live, however they do not include API authentication tokens. This means that if you follow the link as is, you will be using anonymous access, which is throttled for fair use, so you may get a 403 response. It is recommended for your own development you obtain an “app_key” and “app_id” by registering here. The data in these examples will be in JSON format, so installing a JSON formatter plugin in your browser will help you read the data returned.

Let’s begin with the most prominent use of routes on the website – the status board. In the Unified API a line is the top-level entity which groups the routes of a service together. The Line endpoint allows us to request all of the lines for a given set of modes, so is a useful starting point for navigating into the available routes. Showing the status of a line is such a common use case that the endpoint also includes an option to include the service status for each line by appending /Status to the URL:


The API response tells us a number of useful things for our chosen modes (the full list of modes is available from https://api.tfl.gov.uk/Line/Meta/Modes):

  • The id of each line in the API, this is important for requesting route information;
  • The status of each line as a severity ordinal, along with a human-readable description;
  • The mode of each line (the homepage requests additional modes such as river and bus from the API, and groups the lines by mode for clarity).

Now that we have some line ids, we can look at one of those lines in more detail to see the available routes. I’ve deliberately chosen a complicated line with lots of routes – the London Overground line.

To get this information from the API we use the line id from the earlier API call, requesting the outbound services on the regular and (proposed) night timetables:


The structure of the API query string highlights a few key things to understand about routes: that they are the result of joining together a sequence of stops, using a service timetable in a particular direction (inbound or outbound). The resulting route, while running on physical infrastructure, can (and does) change dynamically in the API based on the timetable and the direction. For example, for most services one cannot simply reverse the route when changing direction – this is especially true of bus routes (think one-way streets) or complex tube lines (the District Line through Earl’s Court).

The API result gives us:

  • A list of the StopPoints on the Line (i.e. across all routes), with their ids, and information about other lines and modes that route through them;
  • A LineString, which is a list of lat/long co-ordinates connecting all the stops (for tube and rail services, this is straight lines StopPoint-to-StopPoint, for bus services this is a higher resolution line that follows the road);
  • A collection of RouteSequences for the line where for each RouteSequence:
    • The name of the RouteSequence is provided;
    • The RouteSequence is sorted in the order that the service visits the StopPoints;
    • The StopPoints are referenced by their id in the aforementioned StopPoint collection.

This information is used to build the page above – showing the full A-Z StopPoint list initially, and then allowing the user to select a RouteSequence by name from the drop-down. If the user switches direction, a new API call will be made to request the inbound services, which may follow different routes.

Routes manoeuvre
In a RouteSequence we resolve the timetabled route to the most relevant StopPoint in the NaPTAN hierarchy (which Dan explained last week). The timetabled route actually runs through platform-level StopPoints, but this would cause additional difficulties for developers in the most common use-case – showing the interchanges and facilities available along a route. By resolving to the station-level StopPoint, we are able to easily show, using the example above, that Highbury & Islington has additional Tube (Victoria line) and National Rail modes available, without having to make additional API calls to /StopPoint.

For bus routes, the platform-level StopPoint is used, as this is actually the place where bus interchanges take place, unless the bus stop is part of a station hierarchy (e.g. at Old Street Station, below). Either way, for each mode and heirarchy, the API takes care of this resolution to make life easier for developers. Routing through bus stops is clearly illustrated if we look at the LineString for a bus route:

What’s the frequency Kenneth?
Remember that Routes are built from the services that run on a timetable through the network of StopPoints? We can also request those timetables from the API.

Let’s look at the Northern Line through Camden Town Underground Station, and the timetable from there to Old Street. Since there are multiple routes out of Camden Town (representing the different branches of the Northern line) we make the user choose their destination station. We do this by requesting the valid destinations from Camden Town from the API, and presenting them in the right-hand drop-down on this page:


The API call https://api.tfl.gov.uk/StopPoint/940GZZLUCTN/CanReachOnLine/northern to returns the valid destination stations. Once the user has chosen one (Old Street in this case) we can request the timetable from the API using the two StopPoint ids https://api.tfl.gov.uk/Line/northern/Timetable/940GZZLUCTN/to/940GZZLUODS. The response tells us:

  • A list of all of the Stations involved in the timetable for the route (i.e. the station-level StopPoints in the NaPTAN hierarchy);
  • A list of all of the StopPoints involved in the timetable for the route (i.e. the platform-level StopPoints in the NaPTAN hierarchy);
  • A detailed list of the schedules, giving the arrival time at platform for the service;
  • A breakdown of the day into service periods – highlighting the period where the service is frequent (e.g. 6 am to midnight, every 2-5 mins).

Currently we display the exact service times grouped by hourly period for tube trains, but there is a lot of flexibility in the API response for application designers to display them differently. For example, when displaying a bus timetable, we use the periods of high frequency from the API instead of the exact times:

Walk the Line
The Routes API relies heavily on StopPoint ids and Line ids. Routes are the links between the StopPoints – determined by the service timetables – that group together to form a Line. As such, to uniquely identify a route, you will often need at least a Line id, StopPoint id and direction, or two StopPoint ids and a Line id so the direction can be determined by the API. Dan covered numerous ways to discover StopPoints in the API, so how about finding Lines?

As always, please let the TfL Digital team know what you think of the API in the comments below, or send us a message through the API portal. We’d love to hear how you’re using it in your projects and what improvements we can make.


  1. Thank you so much for writing this up, Tim! And also congratulations on the stellar job the team is doing on the unified API, I’ve been using it for a while now and it’s great to see an open data API reaching this level of quality and developer ergonomics!

    But while the API docs at https://api.tfl.gov.uk/ are reasonably good at describing the input parameters for requesting a journey, the possible responses are not documented at all 🙁

    So for example there are a lot of possible `Tfl.Api.Presentation.Entities` like `Tfl.Api.Presentation.Entities.JourneyPlanner.DisambiguationResult` (which is basically a total breakdown in an app when you expect journeys back), but I couldn’t find any documentation on what entities there are and what they mean.

    Other example I ran into was getting a disruption back on a journey leg, which turned out to be a vaguely related planned bus diversion, knowing how to parse the severity of a disruption would be great.

    So would it be possible to publish a list of entities? Even in a very raw state it would be helpful to have them. Could even be in a wiki format for people to contribute to!

    Thank you!

  2. Sorry for yet another comment, but ran into another issue.

    I’m trying to narrow routes down to very particular ones based on traveller preferences, and normally filtering by modes gets me to the right one, but not always.

    So as a second option I wanted to use ‘via’, but it seems to be a bit too limited. Let me illustrate the point with an example. In this particular case between two given postcodes the normal route returns bus 242, but as an alternative I’m trying to force the journey planner to return one with route 48. Please have a look at the responses for these two requests:
    1. https://api.tfl.gov.uk/journey/journeyresults/E9%207AL/to/E1%206JJ?mode=bus&via=Hackney%20(London),%20St%20Thomas%27s%20Square
    2. https://api.tfl.gov.uk/journey/journeyresults/E9%207AL/to/E1%206JJ?mode=bus&via=-0.05527733293,51.54206348122

    In the first case I get a disambiguation result and in the second case specifying the lon/lat value just fails. So there’s no way to specify a valid via going through this bus stop.

    Maybe it’s just a bug with the lon/lat method, but in general it would be great if it was possible to specify points using ids, just as the online journey planner accepts ‘viaid’ in the URL.


    1. Hi @daain, you’re right that the from/to supports stop point ids, but I’ll check for you about via. It seems that it should be possible to specify this as an id, postcode, lat/lon or free text. If it doesn’t support one of those things, I can add it as a feature request for you.

      I agree that the documentation can be improved, especially around responses. We have this task on our backlog.

      Also, yes, I’ll admit the JP response changing types is ugly, we will look into that – the RESTful way would be a redirect to a Disambiguation endpoint I think. But a way that will break things less for devs now that it’s in the wild might be to have a single return type that can have a Disambiguation property when needed.

      1. Thanks a lot for the reply Tim!

        As far as I found out so far, `via` doesn’t like neither lat/lon, nor IDs 🙁

        So sticking with my previous example, this comes back with a disambiguation response:

        Got the ID from here: https://tfl.gov.uk/bus/stop/490012462N/st-thomass-square?lineId=48

        I don’t really mind the JP disambiguation response itself as it is, if you added the URL for a disambiguation API into it that would already be a great improvement. Especially if it also had the right parameters applied taken from the JP request!

      2. @daiin I’ve added all of your suggestions and the bug with JP “via” to our backlog. Thanks again for your feedback and I’ll let you know as fixes come through.

  3. Hello there, and congratulations on the Unified API – a great step forward for TfL.

    We’ve noticed that there are plenty of $type fields in the JSON that would be ideal for deserialising into POCOs — by the looks of the namespaces, you already have a data model behind the scenes. Is this available to download anywhere, to make deserialisation easier?

    1. Hello Carl, well spotted! We are cleaning up the .NET assembly so that we can make it available for developers working on that platform. In the meantime, a little trick you can do is “Paste as Classes” in Visual Studio 2012 and higher. http://goo.gl/l5COSZ

      1. That’s rather exciting – very pleased about that.. ..any idea on time frame for that, Tim? If you need anybody to test out the assembly, we’re .NET-based here at Bus Checker and really keen to take a look!

Leave a Reply

Your email address will not be published. Required fields are marked *