Vehicle position

SNCB: belgium's national train operator

Format
GeoJSON
Updates
Every 20 seconds (derived from the GTFS-RT feed)
Latest snapshot
Snapshots stored
about 3,283,138
curl -H "Authorization: Bearer $MOBILITYTWIN_TOKEN" "https://api.mobilitytwin.brussels/sncb/vehicle-position"
Get a token

The estimated positions of SNCB/NMBS trains: where the timetable (GTFS) has each train at the moment of every GTFS-RT poll, placed along Infrabel's track segments. Realtime delays are not applied, since the realtime feed's trip ids match the timetable for only about half the trains.

Which days have data

Data from 21 Aug 2024 to 2 Nov 2026, on 796 days. 30 of the last 30 days have data. A typical day holds 3776 snapshots. Days are in UTC.

2026
JanFebMarAprMayJunJulAugSepOctNovDec
2025
JanFebMarAprMayJunJulAugSepOctNovDec
2024
JanFebMarAprMayJunJulAugSepOctNovDec
  • Full day
  • Partial, 50–90% of a typical day
  • Low, under 50%
  • No snapshot
  • Not expected

Click a day above, or pick a date, to get its requests.

Parameters

NameMeaning
timestampReturn the last snapshot at or before this moment, in Unix seconds or ISO 8601 (e.g. 2026-05-06T12:00:00Z). Without it, the latest snapshot is returned.
start_timestampStart of the range, in Unix seconds or ISO 8601.
end_timestampEnd of the range, in Unix seconds or ISO 8601.
limitMaximum number of snapshots, at most 100.
offsetNumber of snapshots to skip, to page through a range.

Response fields

A GeoJSON FeatureCollection. Field names without a prefix are in each feature's properties.

FieldType
name_endstring
name_startstring
ptcarid_endstring
ptcarid_startstring
trip_headsignstring
trip_idstring

Python example

import requests
import geopandas as gpd

from datetime import datetime, timedelta, timezone

yesterday_noon = (datetime.now(timezone.utc) - timedelta(days=1)).replace(
    hour=12, minute=0, second=0, microsecond=0
)
params = {
    "timestamp": yesterday_noon.isoformat(),   # ISO accepted; epoch also works
}

url = "https://api.mobilitytwin.brussels/sncb/vehicle-position"

data = requests.get(url, headers={
        'Authorization': 'Bearer [MY_API_KEY]'
}, params=params).json()

gdf = gpd.GeoDataFrame.from_features(data["features"])

# Show all currently running trains with their route
for _, train in gdf.iterrows():
    print(f"{train['name_start']} → {train['name_end']} ({train['trip_headsign']})")

# Plot train positions on a map
gdf.plot(figsize=(10, 8), color="#006ab3", markersize=10)