Stations
Villo: brussels public bike-share
- Format
- GeoJSON
- Updates
- Every 5 minutes
- Latest snapshot
- Snapshots stored
- about 54,822
curl -H "Authorization: Bearer $MOBILITYTWIN_TOKEN" "https://api.mobilitytwin.brussels/villo/stations"
Real-time availability of Villo! bike-sharing stations in Brussels including available bikes and stands per station. The counts are current, but last_update, as published by opendata.brussels.be, lags the real time by the Brussels UTC offset (two hours in summer, one in winter).
Which days have data
Data from 30 Mar 2026 to 2 Nov 2026, on 218 days. 30 of the last 30 days have data. A typical day holds 264 snapshots. Days are in UTC.
2026
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. |
Response fields
A GeoJSON FeatureCollection. Field names without a prefix are in each feature's properties.
| Field | Type |
|---|---|
number | string |
name_fr | string |
name_nl | string |
address_fr | string or null |
address_nl | string or null |
municipality_fr | string or null |
municipality_nl | string or null |
available_bikes | integer |
available_bike_stands | integer |
bike_stands | integer |
last_update | string |
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/villo/stations"
data = requests.get(url, headers={
'Authorization': 'Bearer [MY_API_KEY]'
}, params=params).json()
gdf = gpd.GeoDataFrame.from_features(data["features"])
# Show stations with most available bikes
top = gdf.nlargest(10, 'available_bikes')[['name_fr', 'available_bikes', 'available_bike_stands', 'municipality_fr']]
print("Stations with most available bikes:")
print(top.to_string(index=False))
# Summary statistics
print(f"\nTotal stations: {len(gdf)}")
print(f"Total available bikes: {gdf['available_bikes'].sum()}")
print(f"Total available stands: {gdf['available_bike_stands'].sum()}")
# Plot stations colored by availability
gdf.plot(figsize=(10, 8), column='available_bikes', legend=True, markersize=15, cmap='YlGn')