GBFS
The node reads feeds following the General Bikeshare Feed Specification (GBFS) from sharing providers, either from the worldwide system directory maintained by MobilityData or from manually entered URLs. It evaluates the feeds free_bike_status, station_information and station_status, filters the results to your areas and emits free vehicles and stations through two outputs.
- Node type
- gbfs
- Package
- @htag/node-red-contrib-hypertegrity-transportation
- Inputs / outputs
- 1 / 2
- Data source
- Any GBFS feed (version 1.x to 2.x), system directory systems.csv by MobilityData
When to use this node
GBFS is the open standard used by providers of rental bikes, e-scooters and similar shared vehicles to publish their real-time data. A GBFS system consists of a discovery file (gbfs.json) that points to individual feeds, for example the list of free-floating vehicles or the stations with availabilities. The GBFS node takes care of locating the feeds, fetching them and filtering spatially, so you get the vehicles and stations of your municipality without writing parser logic.
Typical use cases are:
- Mobility dashboards showing available rental bikes and e-scooters in the urban area in real time.
- Utilisation analyses of sharing stations based on
num_bikes_availableandnum_docks_available. - Transfer into a context broker as Smart Data Model entities such as
BikeHireDockingStationorVehiclevia the NGSI-LD Builder.
How the node works
Trigger
An incoming message starts the fetch. Optionally it names different system ids or URLs.
Resolve systems
For selected system ids the node reads the discovery URL from the systems.csv directory by MobilityData (cached for 24 hours). Manual URLs are used directly.
Fetch feeds
Per system the node loads gbfs.json, then free_bike_status as well as station_information and station_status. All systems are processed in parallel.
Filter and emit
Vehicles and stations without valid coordinates or outside the geographic filters are dropped. Free vehicles leave output 1, stations output 2, one message per system each.
Result: Per system one array of free vehicles and one array of stations with availabilities, restricted to your areas.
The node is trigger-based. GBFS feeds change every minute; an Inject node with an interval of one to five minutes is common. The system directory systems.csv is kept in memory process-wide and reloaded only every 24 hours.
Configuration
The edit dialog has two tabs: Systems and Geographic Filter.
Tab "Systems"
| Field | Type | Default | Meaning |
|---|---|---|---|
| Name | Text | empty | Display name of the node in the flow. |
System list (systemIds) | List with checkboxes | empty | All systems from systems.csv, filterable by country, location and free text; "Selected only" hides unselected entries. The status bar shows the state of the directory cache; the refresh button reloads the directory. The system ids are stored, for example dott-kaiserslautern. |
Manual GBFS URLs (manualUrls) | List with name, URL, auth and parameter names | empty | Discovery URLs (gbfs.json) of systems not listed in the directory. Auth: None, Basic, Header or Query; for Header and Query you name the parameters, several separated by a vertical bar. |
| Authentication | Dynamic input fields | empty | For systems and manual URLs with authentication the dialog shows fields for username and password (Basic) or for the named parameters. |
apiKey | Password (credential) | empty | Stored as a credential of the node type. |
Tab "Geographic Filter"
| Field | Type | Default | Meaning |
|---|---|---|---|
| Map Selection | Leaflet map with drawing tools | Germany overview | A click on the map looks up the suburb, city and county at that position via Nominatim and offers them as areas. Use the drawing tools to create rectangles or circles. |
Active Filters (locations) | Sortable list | empty | All areas with name (editable) and type: point with coordinates (circle), rectangle or admin area (polygon). Several filters are combined with OR. |
| Search Place | Search field with suggestions | empty | Searches Nominatim for cities or districts and adds their boundary polygon (simplified to at most 50 vertices). |
| Manual Latitude & Longitude | Two number fields with "Add Point" button | empty | Creates a circle with a radius of 500 m around the given coordinates. |
Without a geographic filter the node emits all vehicles and stations of the selected systems.
Input
Every message triggers the fetch. Two properties can replace the configured sources for that message.
| Property | Type | Meaning |
|---|---|---|
msg.payload | any | Triggers the fetch. The content is not evaluated. |
msg.systemIds | array of strings | Replaces the configured system ids, for example ["dott-kaiserslautern"]. |
msg.manualUrls | array of strings or objects | Replaces the configured manual URLs. As an object with url and optionally name, authType and authParams. |
The geographic filters always come from the configuration. Properties of the incoming message are carried over to the output messages.
Output
The node sends a separate message per system and feed type, but only if at least one entry remains after filtering. Both outputs set msg.gbfs_system to the system id (manual for manual URLs) and msg.gbfs_type to the feed type. Missing or empty values of the mandatory fields listed below are set to the text unknown.
Output 1: Free Bikes
msg.gbfs_type is free_bikes. msg.payload is an array of free-floating vehicles from the feed free_bike_status.
[
{
"bike_id": "3f2a9c1e-5b7d-4e8a-9c2f-1d4b6e8a0c3f",
"lat": 49.4447,
"lon": 7.7689,
"is_reserved": false,
"is_disabled": false,
"vehicle_type_id": "escooter",
"last_reported": 1759305600,
"current_range_meters": 18500,
"rental_uris": {"android": "https://app.example.org/ride", "ios": "https://app.example.org/ride"},
"model": "unknown",
"type": "free_bike",
"system_id": "dott-kaiserslautern",
"system_name": "Dott Kaiserslautern"
}
]
| Field | Meaning |
|---|---|
bike_id, lat, lon, is_reserved, is_disabled, vehicle_type_id, last_reported, current_range_meters, rental_uris, model | Fields from the GBFS feed; missing values become unknown. current_range_meters is the remaining range in metres, last_reported a Unix timestamp in seconds. |
type | Always free_bike. |
system_id, system_name | System id and name from the directory or the manual URL. |
Output 2: Stations
msg.gbfs_type is stations. msg.payload is an array of stations in which station_information and station_status were merged by station_id. Only stations with at least one available vehicle (num_bikes_available greater than 0) are emitted.
[
{
"station_id": "4711",
"name": "Central Station North",
"short_name": "unknown",
"lat": 49.4360,
"lon": 7.7640,
"region_id": "unknown",
"rental_methods": ["KEY", "CREDITCARD"],
"capacity": 20,
"num_bikes_available": 7,
"num_bikes_disabled": 1,
"num_docks_available": 12,
"num_docks_disabled": 0,
"is_installed": true,
"is_renting": true,
"is_returning": true,
"last_reported": 1759305600,
"type": "station",
"system_id": "dott-kaiserslautern",
"system_name": "Dott Kaiserslautern"
}
]
| Field | Meaning |
|---|---|
station_id, name, short_name, lat, lon, region_id, rental_methods, capacity | Master data from station_information. |
num_bikes_available, num_bikes_disabled, num_docks_available, num_docks_disabled, is_installed, is_renting, is_returning, last_reported | Availabilities and operational status from station_status. |
type | Always station. |
system_id, system_name | System id and name. |
Further fields of the feeds are passed through unchanged.
Example flow
The package ships with an example flow. Import it in Node-RED via Menu → Import → Examples → @htag/node-red-contrib-hypertegrity-transportation → gbfs-data. Alternatively download gbfs-data.json and import it via Menu → Import.
Inject
An Inject node triggers the fetch manually or at a fixed interval.
GBFS
The node fetches the system dott-kaiserslautern without a geographic filter.
Two Debug nodes
Output 1 shows free scooters, output 2 shows stations in the sidebar.
Show example flow as JSON
[
{"id": "tab1", "type": "tab", "label": "Gbfs-data", "disabled": false, "info": ""},
{"id": "inject1", "type": "inject", "z": "tab1", "name": "", "props": [{"p": "payload"}], "repeat": "", "crontab": "", "once": false, "onceDelay": 0.1, "topic": "", "payload": "", "payloadType": "date", "x": 120, "y": 100, "wires": [["gbfs1"]]},
{"id": "gbfs1", "type": "gbfs", "z": "tab1", "name": "", "systemIds": ["dott-kaiserslautern"], "manualUrls": [], "locations": [], "x": 370, "y": 100, "wires": [["debug1"], ["debug2"]]},
{"id": "debug1", "type": "debug", "z": "tab1", "name": "Free scooters", "active": true, "tosidebar": true, "console": false, "tostatus": false, "complete": "payload", "targetType": "msg", "x": 600, "y": 100, "wires": []},
{"id": "debug2", "type": "debug", "z": "tab1", "name": "Stations", "active": true, "tosidebar": true, "console": false, "tostatus": false, "complete": "payload", "targetType": "msg", "x": 580, "y": 160, "wires": []}
]
Status and errors
| Indicator | Meaning |
|---|---|
| Green dot "Systems Ready" | The node is ready (status after start). |
| Yellow dot "Downloading systems.csv..." | The system directory is being downloaded (status after start). |
| Red ring "Fetch error: …" | The last download of the system directory failed (status after start). |
| Blue dot "Fetching data..." | The fetch is running. |
| Green dot "Data updated" | All sources were processed. Individual failed feeds only produce warnings. |
| Red ring "Error fetching data" | The fetch could not be started. Error message: "No systems or manual URLs configured". |
| Warning "Failed to load systems.csv: …" | The directory is unreachable (timeout after 10 seconds, connection or HTTP error, invalid file). System ids cannot be resolved then; manual URLs keep working. |
| Warning "System ID not found or missing URL: …" | The system id is not in the directory or has no discovery URL there. |
| Warning "Request timeout while fetching … (timeout after 8s)", "Connection error while fetching …", "HTTP error … for …" | A feed of the named system is unreachable. |
| Warning "Invalid GBFS discovery file for …", "Empty response from feed server for …", "Invalid JSON structure in feed …" | The response does not match the GBFS format. |
| Warning "Failed to fetch free bike data for …", "Failed to fetch station data for …", "Feed error (…)" | Other errors while processing a feed. |
Errors are reported via node.error and can be handled with a Catch node. Warnings appear in the debug sidebar.
Notes
- GBFS versions: The node finds feeds in discovery files of versions 1.x to 2.x (with and without language level) and reads the feeds
free_bike_status,station_informationandstation_status. - Rate limits: Many providers limit the polling frequency. GBFS feeds contain a field
ttlnaming the recommended validity period in seconds. Do not choose the Inject interval shorter than this value. Every request has a timeout of 8 seconds. - Smart Data Models: The output contains the raw fields of the GBFS feeds. Mapping to Smart Data Models such as
BikeHireDockingStation(stations) orVehicle(free vehicles) is done downstream in the NGSI-LD Builder. - Metric units: Radii in the geographic filter are specified in metres;
current_range_metersis the remaining range in metres. - Editor services: Map, place search and click detection use Leaflet, OSM map tiles and Nominatim over the internet. The directory
systems.csvis loaded from GitHub (MobilityData). - Personal data: Vehicle ids (
bike_id) and positions of free-floating vehicles can reveal movement profiles of individual rides when stored permanently. Although the GBFS specification requires rotating ids, store vehicle data only aggregated or with a short retention and never link rides to individual persons.rental_uriscontain links to the apps of the providers. - Licences: The terms of use of the feeds are set by the respective provider; for many systems the directory
systems.csvlinks to licence information.