STIB GTFS Realtime (GTFS-RT)
STIB-MIVB publishes no GTFS-RT feed. Its real-time API says where vehicles are, but not which vehicle or which trip. MobilityTwin.Brussels reconstructs both, every 20 seconds, and publishes them as standard GTFS-RT Vehicle positions and Trip updates, with the trip, route and stop ids of STIB's own GTFS.
The feeds
/stib/gtfs-rt-vehicle-position
Every tracked vehicle: position, stop it is at or heading to, and its trip where established (otherwise route and direction only).
GTFS-RT protobuf · every 20 s
/stib/gtfs-rt-trip-update
One update per trip with a vehicle on it: its measured delay and a predicted arrival at every stop still ahead.
GTFS-RT protobuf · every 20 s
/stib/vehicle-trip
The same in GeoJSON, for maps and notebooks: one point per vehicle with tripId, delay, stopId and a stable uuid.
GeoJSON · every 20 s · on the live map
Base URL https://api.mobilitytwin.brussels. Every vehicle-trip snapshot is archived: add ?timestamp= (epoch or ISO) for a past moment, or /index to list a period's snapshots. The two GTFS-RT feeds are built from it on request; add ?end_timestamp= (epoch) for a feed as it stood at a past moment.
How it works
Every 20 seconds STIB reports each vehicle as a line, a destination, the last stop it passed and the metres since. Nothing else — no vehicle number, no trip. Four steps turn that into GTFS-RT.
-
1. Place
Each position is put on its line as metres from the start, using the stop order of the day's GTFS.
-
2. Track
Vehicles on a line do not overtake each other, so their order is the same from one poll to the next. Each poll is aligned with the previous one in that order, like a diff, which gives every vehicle an identity that lasts its whole run.
-
3. Read stop calls
When the reported stop changes, the vehicle has just reached a new one; the moment is interpolated between the two polls around it.
-
4. Match trips
The vehicles on the road are paired with the timetabled trips that could be running, choosing the pairing with the smallest total gap between observed and scheduled stop times. The gap at the last stop is the delay, carried to the stops ahead.
It is the live form of the method behind the daily STIB punctuality table, which does the same with a whole day in hand.
Quality, and what it cannot tell
91%
of vehicles that have left their first stop carry a trip.
93%
of those trips are the ones the daily reconstruction picks with a whole day of hindsight (88% at the morning peak).
~1 min
after leaving its first stop, a vehicle has its trip (two stop calls).
- Inferred, not reported. A trip id is the best match to the timetable, not STIB's word. A wrong id is rarer than a missing one: when the match is unclear the vehicle is published without a trip.
- Vehicles waiting at a terminus have no trip yet. Which of the waiting vehicles makes the next departure cannot be read from the feed.
- No cancellations. STIB's data has no signal for them, so a trip with no vehicle on it is simply absent from the trip updates.
- Constant delay ahead. The delay measured at the last stop is applied to all stops still ahead; there is no traffic model.
- Identity per run. The
vehicle.idis a tracking id that lasts one run, not a fleet number.
How to use it
Create a free account and a token on your account page. Send it as Authorization: Bearer <token>, or, for tools that only take a URL, as ?token=<token>.
Python
import requests
from google.transit import gtfs_realtime_pb2 # pip install gtfs-realtime-bindings
url = "https://api.mobilitytwin.brussels/stib/gtfs-rt-trip-update"
data = requests.get(url, headers={"Authorization": "Bearer <token>"}).content
feed = gtfs_realtime_pb2.FeedMessage.FromString(data)
for entity in feed.entity:
update = entity.trip_update
print(update.trip.trip_id, update.delay, "s late")
OpenTripPlanner 2 (router-config.json)
"updaters": [
{
"type": "stop-time-updater",
"feedId": "stib",
"frequency": "20s",
"url": "https://api.mobilitytwin.brussels/stib/gtfs-rt-trip-update",
"headers": { "Authorization": "Bearer <token>" }
},
{
"type": "vehicle-positions",
"feedId": "stib",
"frequency": "20s",
"url": "https://api.mobilitytwin.brussels/stib/gtfs-rt-vehicle-position",
"headers": { "Authorization": "Bearer <token>" }
}
]
Pair it with STIB's static GTFS of the same day, available as /stib/gtfs (zip) or /stib/gtfs-parquet. For bulk history, the Python client downloads a period's snapshots, resumably.