TransitRadar

Headcodes, UIDs, and why the same code runs twice

Four characters on the front of a diagram look like they identify a train. They identify it to a signaller, in one part of the country, for one day — which is not the same thing at all.

If you want to look up a particular service on the service page or in the planner on the map, it helps to know which identifier you are holding. There are three in common use and they behave very differently.

The headcode

A headcode — a train reporting number — is four characters, like 1S23. It has structure:

  • The digit is the class of service. 1 is an express passenger train, 2 a stopping passenger service, 5 empty stock, 6 a freight working, 0 a light locomotive.
  • The letter is broadly a destination region — S for Scotland, V for the West of England, and so on. It is a convention rather than a rule, and it has drifted over the decades.
  • The final two characters distinguish that working from its neighbours.

Headcodes are allocated per describer area, and they are reused freely. Two trains hundreds of miles apart can carry the same one at the same moment, entirely correctly. We measured this on a single sweep of the live map and found fifteen pairs of different trains sharing a headcode simultaneously.

So searching a bare headcode does not identify a train. Searching 1H26 against a real day's timetable returned five trains run by four different operators. If you read a headcode off a departure screen and type it in, expect to have to pick from the results.

The UID, or planning ID

The identifier that does pin down a train is the schedule UID: a letter followed by five digits, like C01943. Of the 298,941 UIDs in a national timetable extract, 298,931 follow exactly that shape. It identifies one schedule, and together with a date it identifies one run of one train.

This is what our URLs use, and what the API calls a planning ID. If you have one, you never have to disambiguate anything.

A UID also matches by prefix, which is deliberate: someone reading one off a diagram types it a character at a time, and each keystroke should narrow the results rather than finding nothing until the last digit lands. What keeps UIDs and headcodes apart in a search box is their first character — headcodes start with a digit and UIDs with a letter.

Why the search is not fuzzy, on purpose

Most search boxes forgive typos. This one deliberately does not, and it is worth explaining why, because it looks like a missing feature.

1H26, 1H24 and 1H2G are all real, all different, and all one character apart. In a code space this dense, approximate matching does not rescue you from a typo — it hands you somebody else's train while looking like it worked. A search that returns nothing is a search you can correct. A search that confidently returns the wrong train is one you act on.

Searching in plain language

Because a bare headcode is ambiguous, the picker reads several terms at once and requires all of them. These all work:

  • EMR 1H26 — operator and headcode
  • 12:19 from Corby — a time and a place
  • Corby to St Pancras — origin and destination
  • great western 1A20 — an operator's name in full, and a headcode

Three details make that behave sensibly, and each was a bug before it was a feature.

A time is a window, not an instant. There is no 12:19 from Corby; the real train is the 12:10. Demanding the exact minute recreates the problem the search exists to solve, so a time matches within about ten minutes either side.

A place is matched against every calling point, not just origin and destination. “EMR 08:30 from Leicester” finds trains that neither begin nor end there. Where a train matches somewhere in the middle we show you which stop it was, because otherwise a result reading “Sheffield → St Pancras” does not explain itself as an answer to a search for Leicester.

Operator codes are two letters, and the ones you know are not them. East Midlands Railway's code is EM, not EMR. We resolve the familiar three-letter brands anyway — EMR, GWR, LNER and SWR all fall out of taking the initials of the operator's name — but if a token could equally be a place, the place wins. “East Midlands” leads both an operator and a station: reading it as the operator once left the word “Parkway”, which duly matched Luton Airport Parkway and Liverpool South Parkway. A tidy list of entirely the wrong stations is worse than a longer list containing the right one.

A place, but only if trains stop there

Eleven locations in the timetable match the word “Corby”. Ten of them are signals, loops and junctions. A freight timed past “Corby Signal Km3974” at 07:47 is not a train anybody can catch, so place matching is restricted to actual stations, and to calling points where the train genuinely calls. A passing point carries no public time and must not match, or every junction a freight rumbles through lands in somebody's results.

Searching a yard or a signal by name still works — it simply falls through to a plain text match, which is the right behaviour for someone who meant it.

Where this comes from

Schedules, UIDs and headcodes come from Network Rail's timetable feed; operator names come from National Rail reference data. Both are listed on where the data comes from. The FAQ has quick definitions of these codes if that is all you need, and the API exposes the same search.