Punctuality
Infrabel: belgium's rail network manager
- Format
- JSON
- Updates
- Daily
- Latest snapshot
- Snapshots stored
- about 759
curl -H "Authorization: Bearer $MOBILITYTWIN_TOKEN" "https://api.mobilitytwin.brussels/infrabel/punctuality"
Infrabel's raw punctuality data: every stop of every train on one day, with its planned and actual arrival and departure. A file holds the trains of one day, given by datdep, and is stored at 06:00 UTC the next day: for day D, request any timestamp on D+1 after 06:00 UTC. Some days are missing (Infrabel outages, e.g. 2026-05-26 to 2026-06-23); a request for such a day returns an earlier file or an empty one, so check datdep.
Which days have data
Data from 21 Aug 2024 to 2 Nov 2026, on 788 days. 30 of the last 30 days have data. A typical day holds 1 snapshot. 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
| Name | Meaning |
|---|---|
timestamp | Return 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. |
Python example
import requests
import pandas as pd
from datetime import datetime, time, timedelta, timezone
day = datetime.now(timezone.utc).date() - timedelta(days=2) # the day you want
# The trains of day D are stored at 06:00 UTC on D+1
params = {
"timestamp": datetime.combine(day + timedelta(days=1), time(12), timezone.utc).isoformat(), # ISO accepted; epoch also works
}
url = "https://api.mobilitytwin.brussels/infrabel/punctuality"
data = requests.get(url, headers={
'Authorization': 'Bearer [MY_API_KEY]'
}, params=params).json()
df = pd.DataFrame(data)
assert df['datdep'].iloc[0] == day.isoformat() # a missing day returns an earlier file
# Compute delay statistics
df['delay_min'] = df['delay_arr'] / 60 # seconds late at arrival
avg_delay = df['delay_min'].mean()
late_trains = df[df['delay_min'] > 5]
print(f"Average delay: {avg_delay:.1f} min")
print(f"Trains >5min late: {len(late_trains)} / {len(df)}")
# Most delayed stations
worst = df.groupby('ptcar_lg_nm_nl')['delay_min'].mean().nlargest(10)
print("\nMost delayed stations:")
print(worst)