GTFS (Parquet)

STIB: brussels metro, tram and bus operator

Format
GTFS Parquet
Updates
Daily (regenerated from the GTFS feed)
Latest snapshot
Snapshots stored
about 689
curl -H "Authorization: Bearer $MOBILITYTWIN_TOKEN" -o gtfs-parquet.zip "https://api.mobilitytwin.brussels/stib/gtfs-parquet"

The STIB/MIVB timetable with one Parquet file per GTFS table, in a zip, 40-75% smaller than the GTFS zip. Polars or DuckDB can read single columns without loading a whole table. Built with gtfs-parquet v0.4.0.

A zip with one Parquet file per GTFS table.

Which days have data

Data from 5 Apr 2024 to 2 Nov 2026, on 803 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

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.

Python example

# pip install gtfs-parquet>=0.4.0
import requests
import tempfile

from gtfs_parquet import read_parquet
from gtfs_parquet.ops.network import describe
from gtfs_parquet.ops.calendar import get_first_week, compute_busiest_date
from gtfs_parquet.ops.routes import compute_route_stats

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/stib/gtfs-parquet"

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

with tempfile.NamedTemporaryFile(delete=False, suffix=".zip") as f:
    f.write(data)

feed = read_parquet(f.name)

# Quick summary of the feed
print(describe(feed))

# Find the busiest date in the first week
week = get_first_week(feed)
busiest = compute_busiest_date(feed, week)
print(f"Busiest date: {busiest}")

# Compute per-route statistics for that week
route_stats = compute_route_stats(feed, week)
print(route_stats.sort("num_trips", descending=True))