Skip to content

πŸ“‘Nexus Metro Real Time Information - REST API Documentation

Updated: 11 min read

An undocumented API, maintained by Nebula Labs, provides live train locations, predicted arrival/departure times, timetables, journey planning, and car park occupancy for the Tyne and Wear Metro. The API powers their geographic live map and the Pop mobile app (captured by reverse engineering the Pop Android app).

INFO

🚨 API Update (****v1.4.2.5 β†’ v1.9.0****)

When this article was first written in April 2024, the main RTI API was on version 1.4.2.5 (model 3.5.4.5). It has since been updated to version 1.9.0 (model 4.6.3), and this article has been updated to reflect those changes. \n\nThe main difference is that the RTI API now requires authorization via an Authorization: Bearer <token> JWT header obtained from GET https://ken.nebulalabs.cc/realtime/token/.\n

Other updates include a bulk GET /api/times/all endpoint with scheduled and predicted arrival/departure timestamps, coordinates / atcoCode / naptanCode metadata on GET /api/stations/platforms, and expanded cache, train count, and timetable telemetry on GET /api/statuses.

Authentication: JWT Bearer Token ​

Access to the main RTI API is authorised by a Bearer JWT obtained freely from GET https://ken.nebulalabs.cc/realtime/token/. The token expires approximately 20 minutes after issue. A sample request flow from the Pop app is listed below:

http
GET /realtime/token/ HTTP/1.1
Host: ken.nebulalabs.cc
Connection: Keep-Alive
Accept-Encoding: gzip
User-Agent: okhttp/4.12.0
http
HTTP/1.1 200 OK
Server: nginx
Date: Thu, 09 Jul 2026 18:09:41 GMT
Content-Type: application/json
Content-Length: 401
Connection: keep-alive
X-Cache-Hit: true
Vary: Accept, Origin, Cookie
Allow: GET, HEAD, OPTIONS
X-Frame-Options: DENY
X-Content-Type-Options: nosniff
Referrer-Policy: same-origin
Cross-Origin-Opener-Policy: same-origin
X-Cache: HIT
json
{
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJINUlBSElINzNPVlc5UjhIQldQSVQiLCJqdGkiOiJlZTdmNjAxZi05NzM0LTRkMzgtOWMyMy04NmZiMDcxNTZmNDYiLCJlbWFpbCI6ImR5bGFuQG5lYnVsYWxhYnMuY28udWsiLCJ1aWQiOiJkOTU2NTZjNS0xY2M2LTQzMjItYjBmOS05NGViMDFlMmNhOTIiLCJyb2xlcyI6IkFwaVJlYWRlciIsImV4cCI6MTc4MzYyMTc3MCwiaXNzIjoiU2VjdXJlQXBpIiwiYXVkIjoiU2VjdXJlQXBpVXNlciJ9.C5kiOcjlwxHeeCm4CfcLyEdhAb_5BdOwuWy3nlChpmg"
}
json
{
    "sub": "H5IAHIH73OVW9R8HBWPIT",
    "jti": "ee7f601f-9734-4d38-9c23-86fb07156f46",
    "email": "[email protected]",
    "uid": "d95656c5-1cc6-4322-b0f9-94eb01e2ca92",
    "roles": "ApiReader",
    "exp": 1783621770,
    "iss": "SecureApi",
    "aud": "SecureApiUser"
}

TIP

The token above is valid for 20 minutes from time of issue and must be passed as Authorization: Bearer <token> on requests to metro-rti.nexus.org.uk.


Main RTI API (metro-rti.nexus.org.uk) ​

The RTI API is served from https://metro-rti.nexus.org.uk/api/.

All Live Trains (/api/times/all) ​

Returns a JSON object where each key represents a station and platform combination (e.g. APT;1 for Airport Platform 1). Each value is an array of predicted arrival and departure times for trains at that specific platform, along with that train's last known status, location, destination, and line.

http
GET /api/times/all HTTP/2.0
Host: metro-rti.nexus.org.uk
Authorization: Bearer <Valid JWT Token>
Accept-Encoding: gzip
User-Agent: okhttp/4.12.0
http
HTTP/2.0 200
date: Thu, 09 Jul 2026 16:11:45 GMT
content-type: application/json; charset=utf-8
cache-control: no-store,no-cache
pragma: no-cache
set-cookie: ARRAffinity=7af9f2cee10158bcdf7dc006ab0e6d8416004fb6e3a812ff85ad7fe54fe92364;Path=/;HttpOnly;Secure;Domain=app-metrortiapi-prod-001.azurewebsites.net
set-cookie: ARRAffinitySameSite=7af9f2cee10158bcdf7dc006ab0e6d8416004fb6e3a812ff85ad7fe54fe92364;Path=/;HttpOnly;SameSite=None;Secure;Domain=app-metrortiapi-prod-001.azurewebsites.net
vary: Accept-Encoding
x-correlation-id: 400233d1-0000-ea00-b63f-84710c7967bb
x-powered-by: ASP.NET
x-azure-ref: 20260709T161145Z-154bf944499r8v2chC1LONzy6s00000003sg000000002k3x
x-cache: CONFIG_NOCACHE
content-length: 202264
json
{
    "APT;1": [
        {
            "trn": "102",
            "lastEvent": "ARRIVED",
            "lastEventLocation": "Airport Platform 1",
            "lastEventTime": "2026-07-09T17:09:19.0000000+01:00",
            "destination": "South Hylton",
            "dueIn": -1,
            "line": "GREEN",
            "actualScheduledTime": "2026-07-09T17:08:30.0000000+01:00",
            "actualPredictedTime": "2026-07-09T17:12:15.4770109+01:00",
            "scheduledDepartureTime": "2026-07-09T17:14:00.0000000+01:00",
            "predictedDepartureTime": "2026-07-09T17:17:45.4770113+01:00"
        },
        {
            "trn": "103",
            "lastEvent": "ARRIVED",
            "lastEventLocation": "Kingston Park Platform 2",
            "lastEventTime": "2026-07-09T17:11:43.0000000+01:00",
            "destination": "South Hylton",
            "dueIn": 7,
            "line": "GREEN",
            "actualScheduledTime": "2026-07-09T17:20:30.0000000+01:00",
            "actualPredictedTime": "2026-07-09T17:19:13.0000000+01:00",
            "scheduledDepartureTime": "2026-07-09T17:26:00.0000000+01:00",
            "predictedDepartureTime": "2026-07-09T17:24:43.0000000+01:00"
        }
    ],
    "BDE;1": [
        {
            "trn": "132",
            "lastEvent": "DEPARTED",
            "lastEventLocation": "Pelaw Platform 1",
            "lastEventTime": "2026-07-09T17:09:19.0000000+01:00",
            "destination": "South Shields",
            "dueIn": 7,
            "line": "YELLOW",
            "actualScheduledTime": "2026-07-09T17:16:40.0000000+01:00",
            "actualPredictedTime": "2026-07-09T17:18:24.0000000+01:00",
            "scheduledDepartureTime": "2026-07-09T17:17:00.0000000+01:00",
            "predictedDepartureTime": "2026-07-09T17:18:44.0000000+01:00"
        }
    ]
}

Single-Platform Live Trains (/api/times/{sta}/{pno}) ​

Returns the predicted train arrivals array for a single station code ({sta}) and platform number ({pno}).

http
GET /api/times/PLW/1 HTTP/2.0
Host: metro-rti.nexus.org.uk
Authorization: Bearer <Valid JWT Token>
Accept-Encoding: gzip
User-Agent: okhttp/4.12.0
json
[
    {
        "destination": "South Hylton",
        "dueIn": -1,
        "lastEvent": "ARRIVED",
        "lastEventLocation": "Pelaw Platform 1",
        "lastEventTime": "2024-04-01T12:00:00.0000000+01:00",
        "line": "GREEN",
        "trn": "154"
    },
    {
        "destination": "South Shields",
        "dueIn": 7,
        "lastEvent": "DEPARTED",
        "lastEventLocation": "Heworth Platform 2",
        "lastEventTime": "2024-04-01T19:42:12.0000000+01:00",
        "line": "YELLOW",
        "trn": "162"
    },
    {
        "destination": "South Hylton",
        "dueIn": 14,
        "lastEvent": "APPROACHING",
        "lastEventLocation": "Pelaw Platform 2",
        "lastEventTime": "2024-04-01T19:42:36.1200000+01:00",
        "line": "GREEN",
        "trn": "155"
    }
]

Station Codes (/api/stations) ​

Returns a flat map of every RTI station code to its display name. There are 61 entries β€” one more than the 60 physical Metro stations β€” because Monument is split into two codes: MTS (North–South) and MTW (West–East), despite being a single physical station under one NaPTAN stop.

This is the authoritative list of codes the RTI feed will ever emit. It carries no platform, coordinate, or identifier data β€” use /api/stations/platforms for that.

http
GET /api/stations HTTP/2.0
Host: metro-rti.nexus.org.uk
Authorization: Bearer <Valid JWT Token>
Accept-Encoding: gzip
User-Agent: okhttp/4.12.0
json
{
    "APT": "Airport",
    "BDE": "Bede",
    "BFT": "Bank Foot",
    "BTN": "Benton",
    "BYK": "Byker",
    "MTS": "Monument N-S",
    "MTW": "Monument W-E",
    "SUN": "Sunderland",
    "...": "..."
}

Station Platforms (/api/stations/platforms) ​

Returns every platform for every station code, keyed by code (61 codes / 120 platforms).

This is the most complete first-party description of platform identity available, and the only source that states which platform belongs to which RTI code β€” the MTS platforms 1–2 versus MTW platforms 3–4 split at Monument is visible here directly rather than having to be inferred.

http
GET /api/stations/platforms HTTP/2.0
Host: metro-rti.nexus.org.uk
Authorization: Bearer <Valid JWT Token>
Accept-Encoding: gzip
User-Agent: okhttp/4.12.0
json
{
    "APT": [
        {
            "platformNumber": 1,
            "direction": "IN",
            "helperText": "Towards South Hylton",
            "coordinates": {
                "longitude": -1.7111200094223,
                "latitude": 55.0357666015625
            },
            "atcoCode": "9400ZZTWAPT1",
            "naptanCode": "twrjagjw"
        },
        {
            "platformNumber": 2,
            "direction": "OUT",
            "helperText": "Towards Airport",
            "coordinates": {
                "longitude": -1.71116101741791,
                "latitude": 55.035758972168
            }
        }
    ],
    "BDE": [
        {
            "platformNumber": 1,
            "direction": "IN",
            "helperText": "Towards South Shields",
            "coordinates": {
                "longitude": -1.45735096931458,
                "latitude": 54.9788208007812
            },
            "atcoCode": "9400ZZTWBDE1",
            "naptanCode": "twrjdgwm"
        },
        {
            "platformNumber": 2,
            "direction": "OUT",
            "helperText": "Towards St. James via Whitley Bay",
            "coordinates": {
                "longitude": -1.45721900463104,
                "latitude": 54.9788208007812
            },
            "atcoCode": "9400ZZTWBDE2",
            "naptanCode": "twrjdgwp"
        }
    ]
}

Platform Caveats ​

  • atcoCode and naptanCode are optional: Nine platforms omit both (APT;2, NPK;1, NPK;2, SHL;2, SJM;2, SSS;2, SUN;1, WMN;1, WMN;2). For most of these NaPTAN holds a record, so the two sources must be unioned rather than either being treated as complete.
  • platformNumber does not match the ATCO suffix, and does not always match NaPTAN's <Indicator>: Nexus reports the operationally-signed platform number; NaPTAN numbers Metro platforms as a local ordinal within the stop. They diverge wherever Metro occupies only part of a shared numbering range:
StationNaPTAN PlatformsNexus Platforms
Sunderland…SND1 ("platform 1"), …SND2 ("platform 2")1, 3
South Shields…SSH1 ("platform 1")2
South Hylton…SHY1 ("platform 1")2

At Sunderland the Metro shares a 1–4 platform range with National Rail and occupies 1 and 3; NaPTAN renumbers those two as its own 1 and 2. Never derive an ATCO code from a platform number by string concatenation β€” 9400ZZTWSND3 looks plausible and does not exist.

  • Coordinates are float32****-rounded (55.0357666015625) and sit 20–50 m from the NaPTAN values for the same platform. Prefer NaPTAN for geometry and Nexus for identity.

System & Model Status (/api/statuses) ​

Returns operational status, model initialization timestamps, active timetable metadata, train counts, and backend version information.

http
GET /api/statuses HTTP/2.0
Host: metro-rti.nexus.org.uk
Authorization: Bearer <Valid JWT Token>
Accept-Encoding: gzip
User-Agent: okhttp/4.12.0
http
HTTP/2.0 200
date: Fri, 18 Sep 2026 15:22:44 GMT
content-type: application/json; charset=utf-8
cache-control: no-store,no-cache
pragma: no-cache
x-powered-by: ASP.NET
json
{
    "model_status": "Initialised",
    "initialisation_time": "2026-09-18T03:00:52.9160345+01:00",
    "operational_day": "2026-09-18T03:00:00.0000000+01:00",
    "current_model_time": "2026-09-18T16:38:13.9382342+01:00",
    "timetable_service_id": "31A8D224DBFBF00F610230C8E82E536C",
    "timetable_description": "May '26 Base Timetable",
    "count_of_operational_trains": 28,
    "count_of_valid_prediction_maps": 28,
    "cache_status": {
        "honcho": "72ED79F191C157C35C0EDF8E8B9944F6",
        "age": "2026-09-18T16:38:12.0470538+01:00"
    },
    "model_version": "4.6.3+08c7d72b454c2efc0e86891781d3b23ada522afe",
    "api_version": "1.9.0+08c7d72b454c2efc0e86891781d3b23ada522afe",
    "instance_id": "72ED79F191C157C35C0EDF8E8B9944F6"
}

Status Payload Fields ​

FieldTypeDescription
model_statusstringCurrent operational state of the prediction engine (e.g. "Initialised").
initialisation_timestring (ISO 8601)Timestamp when the prediction engine initialized for the current operational day.
operational_daystring (ISO 8601)Operational day boundary timestamp. Metro operations roll over at 03:00:00 rather than midnight.
current_model_timestring (ISO 8601)Wall-clock simulation timestamp maintained by the model.
timetable_service_idstring (Hex 32)128-bit identifier ([0-9A-F]{32}) for the active timetable schedule import.
timetable_descriptionstringDescriptive title of the active base timetable (e.g. "May '26 Base Timetable").
count_of_operational_trainsnumberCount of active trains currently tracked by the system.
count_of_valid_prediction_mapsnumberCount of tracked trains that have a resolved prediction map.
cache_status.honchostring (Hex 32)Instance identifier of the backend node that generated the cached prediction snapshot. Matches instance_id when the responding node is the cache writer.
cache_status.agestring (ISO 8601)Timestamp when the prediction snapshot was last refreshed in the cache.
model_versionstring (SemVer 2.0)Core model engine version. The build metadata suffix (+...) contains the 40-character Git commit SHA.
api_versionstring (SemVer 2.0)API service version. Carries the same 40-character Git commit SHA build metadata.
instance_idstring (Hex 32)Unique identifier for the responding server instance.

Data Model Quirks & Gotchas ​

direction is a departure direction, not a journey direction ​

Each platform carries "direction": "IN" | "OUT" alongside helper text like "Towards South Hylton and South Shields". This describes the way a train faces when it leaves that platform β€” which is not the same thing as the direction of a journey calling there, and the distinction is load-bearing.

The two agree everywhere except at reversal points. Comparing all 50 GTFS patterns' platform calls against this field:

text
agree: 1180   disagree: 25   unknown: 0
position of disagreements:  first call: 1   last call: 24   mid-pattern: 0
platforms: SSH1 Γ—11, STJ1 Γ—5, SHY1 Γ—4, APT1 Γ—3, MMT3 Γ—1, TYM2 Γ—1

Every disagreement is a pattern endpoint at a platform where trains turn round. An arriving journey sits on the platform that departs the other way, because the unit changes ends: South Shields platform 1 is where IN journeys end and OUT journeys begin, and its helper text reads "Towards St. James via Whitley Bay" accordingly.

The field degenerates on arrival-only platforms. Airport platform 2 is "direction": "OUT" with helper text "Towards Airport", which is meaningless at Airport β€” it reflects the direction of arriving services, there being no departures to describe. Read direction as the departure direction where one exists.

dueIn and actualPredictedTime can contradict each other ​

A prediction can carry a dueIn saying the call has already happened alongside an actualPredictedTime well in the future. Observed on 2026-08-17 for TRN 107 at SGF;1:

text
dueIn: -1     actualScheduledTime: 08:52:10     actualPredictedTime: 09:27:43

The train's own lastEventTime showed it had arrived at 08:51:33 β€” on time β€” so neither the 35-minute lateness nor the "already gone" flag described what the train was doing. The same train's downstream calls were simultaneously reporting roughly 71 minutes down. Treat a prediction as unreliable when dueIn <= 0 and its actualPredictedTime is far in the future.


Companion Endpoints (Timetable, Journey, Parking & RSS) ​

Timetable API (ken.nebulalabs.cc) ​

Provides scheduled timetable information at each station at a given time.

http
POST https://ken.nebulalabs.cc/timetable/stop/
Content-Type: application/json

{
    "date": "2024-04-01T12:00:00",
    "stop_id": "1:$$STATION_CODE$$_$$PLATFORM_NO$$"
}
json
[
    {
        "arrival_date": "2024-04-01T12:00:00",
        "date": "2024-04-01T12:00:00",
        "departure_date": "2024-04-01T12:01:00",
        "line": "GREEN",
        "name": "To South Hylton Platform 2",
        "route_id": "1:154",
        "time_string": "12:00:00",
        "timestamp": 43200,
        "trip_id": "1:48B94425097131ADD3453F9923E91A7F"
    }
]

Journey Planning API (ken.nebulalabs.cc) ​

Provides journey options from one station/coordinate pair to another.

http
POST https://ken.nebulalabs.cc/journey/plan/
Content-Type: application/json

{
    "arrive_by": false,
    "date": "2024-04-01T12:00:00",
    "end": {
        "latitude": 54.95259455,
        "longitude": -1.542170635
    },
    "start": {
        "latitude": 54.95734882,
        "longitude": -1.486040092
    }
}
json
{
    "start_point": {
        "latitude": 54.9573488,
        "longitude": -1.4860401,
        "name": "Origin",
        "vertexType": "NORMAL"
    },
    "end_point": {
        "latitude": 54.9525946,
        "longitude": -1.5421706,
        "name": "Destination",
        "vertexType": "NORMAL"
    },
    "journey_options": [
        {
            "duration": 318,
            "end_time": "2024-04-01T19:20:30",
            "start_time": "2024-04-01T19:16:30",
            "start_timestamp": 1711998990000,
            "steps": [
                {
                    "duration": 240.0,
                    "end_point": {
                        "arrival": "2024-04-01T19:20:30",
                        "departure": "2024-04-01T19:20:30",
                        "helper_text": "Towards Airport and St. James via Whitley Bay",
                        "latitude": 54.9526329,
                        "longitude": -1.5419919,
                        "name": "Pelaw Platform 2",
                        "platform_number": 2,
                        "station_id": "PLW",
                        "station_name": "Pelaw",
                        "stopId": "1:PLW_2",
                        "stopIndex": 12,
                        "stopSequence": 6306,
                        "vertexType": "TRANSIT"
                    },
                    "end_time": "2024-04-01T19:20:30",
                    "line": "YELLOW",
                    "start_point": {
                        "arrival": "2024-04-01T19:16:30",
                        "departure": "2024-04-01T19:16:30",
                        "helper_text": "Towards Airport",
                        "latitude": 54.9574509,
                        "longitude": -1.485661,
                        "name": "Fellgate Platform 2",
                        "platform_number": 2,
                        "station_id": "FGT",
                        "station_name": "Fellgate",
                        "stopId": "1:FGT_2",
                        "stopIndex": 11,
                        "stopSequence": 6304,
                        "vertexType": "TRANSIT"
                    },
                    "start_time": "2024-04-01T19:16:30",
                    "stops_between": [],
                    "train_name": "Heworth - South Shields - Heworth",
                    "train_number": "161"
                }
            ],
            "transfers": 0
        }
    ]
}

Car Park Occupancy API (hce-prod1.nexusaws.net) ​

Provides the occupancy levels of Nexus-owned car parks, sorted by distance if coordinates are provided.

http
GET https://hce-prod1.nexusaws.net/api/parking/spaces/find?latitude=54.9533745&longitude=-1.5526666
json
[
    {
        "capacity": 393,
        "description": "Low Heworth Lane, Heworth, Gateshead, NE10 0YJ",
        "distance": 1053,
        "free_spaces": 390,
        "idCode": "VMSLCP002",
        "is_live": true,
        "latitude": 54.9533745486823,
        "longitude": -1.55266667253195,
        "name": "Heworth Interchange (Long Stay)",
        "occupancy": 3,
        "status": "SPACES"
    }
]

RSS Travel Updates ​

Live travel updates and service disruptions are published as an RSS feed at: https://rss.nexus.org.uk/