The traffic component provides information about the traffic in Brussels. It
uses Telraam data to provide the number of vehicles per hour per day for certain segments of the
Brussels road network. Other endpoints provide the count of vehicles registered by the cameras present
in the tunnels of Brussels, the bike counters, what the variable message signs display, the public car parks, as
well as real-time traffic events and incident codes.
Endpoints
Telraam
/traffic/telraam
GEOJSONAPPLICATION/JSON
The Telraam counts of the Brussels segments, one feature per segment: date is the start of the last completed UTC hour, published about 15 minutes after that hour ends. A segment that sent no report for that hour has date "NaT" and empty counts.
Refresh: Every 5 minutes
From
2024-08-21 14:59:39
To
2026-10-31 10:33:29
Records
207,584
import requests
import pandas as pd
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/traffic/telraam"
data = requests.get(url, headers={
'Authorization': 'Bearer [MY_API_KEY]'
}, params=params).json()
gdf = gpd.GeoDataFrame.from_features(data["features"])
# Segments without a report for the hour have empty counts (and date "NaT")
for column in ['car', 'heavy', 'bike', 'pedestrian', 'v85']:
gdf[column] = pd.to_numeric(gdf[column], errors='coerce')
# Analyze traffic composition per segment
for _, seg in gdf.dropna(subset=['car', 'v85']).head(5).iterrows():
print(f"Segment {seg['segment_id']}: {seg['car']:.0f} cars, {seg['bike']:.0f} bikes, "
f"{seg['heavy']:.0f} heavy, v85={seg['v85']:.0f} km/h")
# Plot segments colored by speed
gdf.plot(figsize=(12, 10), column='v85', legend=True, linewidth=2, cmap='RdYlGn')
Tunnels
/traffic/tunnels
JSONAPPLICATION/JSON
The realtime traffic data for the tunnels of Brussels
Refresh: Every minute
From
2024-08-21 14:55:38
To
2026-10-31 10:33:45
Records
1,080,111
import requests
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/traffic/tunnels"
data = requests.get(url, headers={
'Authorization': 'Bearer [MY_API_KEY]'
}, params=params).json()
# Show real-time traffic per tunnel device
for device_id, device_data in data['data'].items():
results = device_data['results']
# Get 15-minute aggregate
r = results['15m']
for lane, stats in r.items():
if stats['speed'] is None: # no measurement for this lane in the period
continue
print(f"{device_id} ({lane}): {stats['count']} vehicles, "
f"speed={stats['speed']:.0f} km/h, occupancy={stats['occupancy']:.1f}%")
Tunnel devices
/traffic/tunnel-devices
GEOJSONAPPLICATION/JSON
The position of the traffic devices in the tunnels of Brussels
Refresh: Daily
From
2024-08-22 06:00:01
To
2026-10-31 05:00:00
Records
701
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/traffic/tunnel-devices"
data = requests.get(url, headers={
'Authorization': 'Bearer [MY_API_KEY]'
}, params=params).json()
gdf = gpd.GeoDataFrame.from_features(data["features"])
# List all tunnel monitoring devices
for _, device in gdf.iterrows():
print(f"{device['traverse_name']}: {device['descr_fr']}")
# Plot device locations on a map
gdf.plot(figsize=(10, 8), color="orange", markersize=20)
Bike count
/traffic/bike-count
JSONAPPLICATION/JSON
The bike count data for the different bike counters in Brussels
Refresh: Every minute
From
2024-08-21 14:55:38
To
2026-10-31 10:32:59
Records
1,080,419
import requests
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/traffic/bike-count"
data = requests.get(url, headers={
'Authorization': 'Bearer [MY_API_KEY]'
}, params=params).json()
# Show bike counts per counter (hourly, daily, yearly)
for counter_id, counter_data in data['data'].items():
hourly = counter_data.get('hour_cnt', 0)
daily = counter_data.get('day_cnt', 0)
yearly = counter_data.get('year_cnt', 0)
print(f"{counter_id}: {hourly} this hour, {daily} today, {yearly} this year")
Bike counters
/traffic/bike-counters
GEOJSONAPPLICATION/JSON
The position of the bike counters in Brussels
Refresh: Daily
From
2024-08-22 06:00:00
To
2026-10-31 05:00:00
Records
701
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/traffic/bike-counters"
data = requests.get(url, headers={
'Authorization': 'Bearer [MY_API_KEY]'
}, params=params).json()
gdf = gpd.GeoDataFrame.from_features(data["features"])
# List all active bike counters with their location
active = gdf[gdf['active'] == True]
for _, counter in active.iterrows():
print(f"{counter['device_name']}: {counter.get('road_fr', 'N/A')}")
# Plot counter locations
active.plot(figsize=(10, 8), color="green", markersize=30)
Message signs
/traffic/message-signs
JSONAPPLICATION/JSON
What the variable message signs on the Brussels motorways, ring and tunnels currently display, one entry per sign: up to four lines in French (fr_l1–fr_l4) and Dutch (nl_l1–nl_l4), an optional pictogram URL (picto), and a status ("OK" or "Non OK" when the sign is not reporting). last_update is in Brussels local time. There are no positions here: join on id_mobigis with the message-sign-locations endpoint.
Refresh: Every minute
From
2026-10-22 14:19:37
To
2026-10-31 05:09:45
Records
10,377
import requests
url = "https://api.mobilitytwin.brussels/traffic/message-signs"
data = requests.get(url, headers={
'Authorization': 'Bearer [MY_API_KEY]'
}).json()
# Print what each working sign displays, in French
for feature in data["features"]:
sign = feature["properties"]
lines = [sign[f"fr_l{i}"] for i in range(1, 5) if sign[f"fr_l{i}"]]
if sign["status"] == "OK" and lines:
print(f"{sign['id_mobigis']} ({sign['localisation']}): {' / '.join(lines)}")
Message sign locations
/traffic/message-sign-locations
GEOJSONAPPLICATION/JSON
The positions of the variable message signs in Brussels, with the road (localisation), direction (sens) and road type (typemg). id_mobigis links each sign to what it displays in the message-signs endpoint.
Refresh: Daily
From
2026-10-22 14:19:33
To
2026-10-31 05:20:00
Records
9
import requests
import pandas as pd
import geopandas as gpd
headers = {'Authorization': 'Bearer [MY_API_KEY]'}
locations = requests.get(
"https://api.mobilitytwin.brussels/traffic/message-sign-locations", headers=headers
).json()
displays = requests.get(
"https://api.mobilitytwin.brussels/traffic/message-signs", headers=headers
).json()
gdf = gpd.GeoDataFrame.from_features(locations["features"])
shown = pd.DataFrame([f["properties"] for f in displays["features"]])
# Put each sign's current text on the map
gdf = gdf.merge(shown[["id_mobigis", "status", "fr_l1", "fr_l2"]], on="id_mobigis", how="left")
gdf.plot(figsize=(10, 8), column="status", legend=True, markersize=20)
Car parks
/traffic/car-parks
GEOJSONAPPLICATION/JSON
The public car parks of Brussels, as published by Brussels Mobility: name, address, operator, contact, capacity and disabledcapacity, number of floors and maximum height (-999 when unknown). There is no occupancy.
Refresh: Daily
From
2026-10-22 14:19:34
To
2026-10-31 05:10:00
Records
9
import requests
import geopandas as gpd
url = "https://api.mobilitytwin.brussels/traffic/car-parks"
data = requests.get(url, headers={
'Authorization': 'Bearer [MY_API_KEY]'
}).json()
gdf = gpd.GeoDataFrame.from_features(data["features"])
# The ten largest public car parks
for _, park in gdf.nlargest(10, "capacity").iterrows():
print(f"{park['name_fr']} ({park['operator_fr']}): {park['capacity']} places")
gdf.plot(figsize=(10, 8), markersize=gdf["capacity"] / 20, color="#1B2A4A")
Bus speed
/traffic/bus-speed
JSONAPPLICATION/JSON
The speed of the vehicles of STIB/MIVB over their last move, per line, stop and direction: STIB refreshes a vehicle's distance about every 40 seconds, and a move is timed from when the previous distance first appeared to when the new one did.
Refresh: Every 20 seconds (computed from STIB vehicle-distance)
From
2024-08-21 02:00:04
To
2026-10-31 10:33:47
Records
3,145,467
import requests
import pandas as pd
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/traffic/bus-speed"
data = requests.get(url, headers={
'Authorization': 'Bearer [MY_API_KEY]'
}, params=params).json()
df = pd.DataFrame(data)
# Average bus speed per line
speed_per_line = df.groupby('lineId')['speed'].mean().sort_values()
print("Average bus speed per line (km/h):")
print(speed_per_line)
# Find the slowest segments
slowest = df.nsmallest(5, 'speed')[['lineId', 'pointId', 'speed']]
print("\nSlowest bus segments:")
print(slowest)
Events
/traffic/events
GEOJSONAPPLICATION/JSON
Real-time traffic events in the Brussels-Capital Region (roadworks, accidents, congestion, etc.).
Refresh: Every 5 minutes
From
2026-03-30 11:05:24
To
2026-10-31 10:32:11
Records
51,194
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/traffic/events"
data = requests.get(url, headers={
'Authorization': 'Bearer [MY_API_KEY]'
}, params=params).json()
gdf = gpd.GeoDataFrame.from_features(data["features"])
# Show active traffic events
active = gdf[gdf['is_active'] == True]
for _, event in active.head(10).iterrows():
print(f"[{event['type_fr']}] {event['location_fr']} — {event['consequences_fr']}")
# Plot active events on a map
active.plot(figsize=(10, 8), color="red", markersize=15)
Tmc event codes
/traffic/tmc-event-codes
GEOJSONAPPLICATION/JSON
TMC (Traffic Message Channel) event codes reference data used for classifying traffic events in Brussels.
Refresh: Weekly
From
2026-04-29 11:31:02
To
2026-10-26 19:04:59
Records
7
import requests
import pandas as pd
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/traffic/tmc-event-codes"
data = requests.get(url, headers={
'Authorization': 'Bearer [MY_API_KEY]'
}, params=params).json()
# Build a lookup table of TMC event codes
codes = pd.DataFrame([f['properties'] for f in data['features']])
print("TMC Event Codes:")
print(codes[['datex_code', 'descr_en', 'datex2_class']].to_string(index=False))