bikeshare-client-python
A Python client for discovering and capturing live bikeshare data feeds made publically available by hundreds of global bikeshare providers in accordance with the General Bikeshare Feed Specification (GBFS) standard.
This module is built with the intention of laying some of the groundwork for supporting more complex applications built around the consumption of live bikeshare data.
System coverage
As of writing, this Python client supports 256 bikeshare systems across 218 cities in 36 countries.
The list of bikeshare systems supported by this client is actively maintained by the GBFS community and can be found here:
The code example below demonstrates how to discover and filter these systems programatically.
Installation
Install from PyPi using pip, a package manager for Python.
pip install gbfs-client
Examples
A sample implementation (Flask JSON endpoint) can be found here:
Interactive walk-through
Searching for bikeshare systems in WI and NY using the system discovery service:
>>> from gbfs.services import SystemDiscoveryService
>>> ds = SystemDiscoveryService()
>>> len(ds.system_ids)
1519
>>> [x.get('System ID') for x in ds.systems if 'WI' in x.get('Location')]
['bcycle_bublr', 'bcycle_madison', 'provider-null-milwaukee']
>>> ds.get_system_by_id('bcycle_madison')
{'Country Code': 'US', 'Name': 'Madison B-cycle', 'Location': 'Madison, WI', 'System ID': 'bcycle_madison', 'URL': 'https://madison.bcycle.com', 'Auto-Discovery URL': 'https://gbfs.bcycle.com/bcycle_madison/gbfs.json', 'Supported Versions': '1.1', 'Authentication Info URL': '', 'Authentication Type': '', 'Authentication Parameter Name': ''}
>>> [x.get('System ID') for x in ds.systems if 'citi bike' in x.get('Name').lower()]
['lyft_nyc']
>>> ds.get_system_by_id('lyft_nyc')
{'Country Code': 'US', 'Name': 'Citi Bike', 'Location': 'New York, NY', 'System ID': 'lyft_nyc', 'URL': 'https://www.citibikenyc.com', 'Auto-Discovery URL': 'https://gbfs.citibikenyc.com/gbfs/2.3/gbfs.json', 'Supported Versions': '2.3', 'Authentication Info URL': '', 'Authentication Type': '', 'Authentication Parameter Name': ''}
Instantiating a GBFS client for Citi Bike (NYC) and exploring its available feeds:
>>> client = ds.instantiate_client('lyft_nyc')
>>> client.feed_names
['gbfs', 'system_information', 'station_information', 'station_status', 'free_bike_status', 'system_hours', 'system_calendar', 'system_regions', 'system_pricing_plans', 'system_alerts', 'gbfs_versions', 'vehicle_types']
>>> client.request_feed('system_alerts')
{'data': {'alerts': []}, 'last_updated': datetime.datetime(2026, 7, 31, 15, 11, 43), 'ttl': 60, 'version': '2.3'}
Instantiating a GBFS client directly (without the discovery service) using the auto-discovery URL for Citi Bike (found earlier):
>>> from gbfs.client import GBFSClient
>>> client = GBFSClient('https://gbfs.citibikenyc.com/gbfs/gbfs.json', 'en')
Searching Citi Bike's station_information feed for two specific stations, one near 49th/8th ave and the other near Barclay/Church:
>>> stations = client.request_feed('station_information').get('data').get('stations')
>>> [(x.get('name'), x.get('station_id')) for x in stations if '49' in x.get('name')]
[('Broadway & W 49 St', '173'), ('W 49 St & 8 Ave', '450'), ('49 Ave & 21 St', '3606')]
>>> home = next(filter(lambda x: x.get('station_id') == '450', stations))
>>> home
{'station_id': '450', 'name': 'W 49 St & 8 Ave', 'lat': 40.76227205, 'lon': -73.98788205, 'capacity': 59}
>>> [(x.get('name'), x.get('station_id')) for x in stations if 'Barclay' in x.get('name')]
[('Barclay St & Church St', '417')]
>>> work = next(filter(lambda x: x.get('station_id') == '417', stations))
>>> work
{'station_id': '417', 'name': 'Barclay St & Church St', 'lat': 40.71291224, 'lon': -74.01020234, 'capacity': 23}
Building a small app to poll a station's live status and print a nice message:
>>> def live_status_for(station):
... all_statuses = client.request_feed('station_status').get('data').get('stations')
... return next(filter(lambda x: x.get('station_id') == station.get('station_id'), all_statuses))
...
>>> def print_status_message(station):
... bikes_available = live_status_for(station).get('num_bikes_available')
... print('{} is currently at {}% capacity with {} bikes available to rent.'.format(
... station.get('name'), int(100*bikes_available/station.get('capacity')), bikes_available))
>>> print_status_message(home)
W 49 St & 8 Ave is currently at 16% capacity with 10 bikes available to rent.
>>> print_status_message(work)
Barclay St & Church St is currently at 91% capacity with 21 bikes available to rent.
Contributing
This project targets Python 3.7+, and is tested in CI against 3.9 through 3.14.
Set up a development environment
Clone the repo and install it in editable mode with the dev and test extras, which pull in tox, coverage, pytest, and related tooling:
git clone https://github.com/jakehadar/bikeshare-client-python.git
cd bikeshare-client-python
python3 -m venv venv
source venv/bin/activate
pip install -e ".[dev,test]"
Run the tests
pytest
With coverage:
coverage run -m pytest
coverage report -m
Run the full tox matrix
tox runs the test suite across every supported Python version (3.9-3.14) that's installed on your machine. Any versions you don't have installed locally are skipped; the full matrix still runs in CI on every push and pull request.
tox
To test against a single interpreter, editable-installed in place (usedevelop = True):
tox -e dev
Change log
0.1.9
Add support for Python 3.9 through 3.14, and drop support for Python 2 and versions of Python 3 below 3.7.
- Removed the
sixdependency and the Python 2/3 compatibility shims it enabled. - Fixed an install-breaking bug in the vendored
versioneer.pyon Python 3.12+ (configparser.SafeConfigParserwas removed in 3.12). - Replaced the unmaintained
pytest-runner/setup.py testintegration with runningpytestdirectly. - Replaced the dormant Travis CI setup with GitHub Actions, testing across Python 3.9-3.14.
- Extended
toxto cover the same matrix, skipping any interpreters not installed locally. - Added a Contributing section to this README outlining development environment setup, running tests, and using
tox. - Fixed New York City's outdated
NYCsystem_id (nowlyft_nyc) in the README and example script — thanks to @kjcole for reporting and fixing (#8).
0.1.8
Add support for bespoke feeds with tokenized URL templates.
For example, Barcelona's supplemental 'nearby_stations' URL is tokenized with {station_id}:
{
"name": "nearby_stations",
"url": "https://barcelona-sp.publicbikesystem.net/ube/gbfs/v1/en/station_information/{station_id}/nearby_stations"
}
The consumer is required to interpolate station_id into the URL string before requesting the feed.
GBFSClient's request_feed method now accepts kwargs for formatting URL templates of this kind.
Example:
c = GBFSClient('https://barcelona.publicbikesystem.net/ube/gbfs/v1/gbfs.json')
c.request_feed('nearby_stations', station_id=2)
0.1.5
Baseline release.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file gbfs_client-0.1.9.tar.gz.
File metadata
- Download URL: gbfs_client-0.1.9.tar.gz
- Upload date:
- Size: 67.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
07405771159bb92f16d86b0a33aa03a9202bcfd8289b3b58a7ee99e6c75edfce
|
|
| MD5 |
ff331ee15a65c2c8e90f5b53096326d3
|
|
| BLAKE2b-256 |
caef33ea4b2bce4c64c035acd870650d183640ba2e8d5a884981bc77c216ff5e
|
Provenance
The following attestation bundles were made for gbfs_client-0.1.9.tar.gz:
Publisher:
release.yml on jakehadar/bikeshare-client-python
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
gbfs_client-0.1.9.tar.gz -
Subject digest:
07405771159bb92f16d86b0a33aa03a9202bcfd8289b3b58a7ee99e6c75edfce - Sigstore transparency entry: 2306833157
- Sigstore integration time:
-
Permalink:
jakehadar/bikeshare-client-python@a4c14e1af3c9ed11232993bf96a0cf44de8b8884 -
Branch / Tag:
refs/heads/master - Owner: https://github.com/jakehadar
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@a4c14e1af3c9ed11232993bf96a0cf44de8b8884 -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file gbfs_client-0.1.9-py3-none-any.whl.
File metadata
- Download URL: gbfs_client-0.1.9-py3-none-any.whl
- Upload date:
- Size: 50.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4cc89a0846c7103e63436d03147b06ef345bc3887f33faaab2d3b906d2034300
|
|
| MD5 |
06aacadc7c02dd17611e1ec1ee6d3ae7
|
|
| BLAKE2b-256 |
0b4ff34ed5accd6f2999ed8c3ef032bbace176e487653906e89daf2c15f33227
|
Provenance
The following attestation bundles were made for gbfs_client-0.1.9-py3-none-any.whl:
Publisher:
release.yml on jakehadar/bikeshare-client-python
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
gbfs_client-0.1.9-py3-none-any.whl -
Subject digest:
4cc89a0846c7103e63436d03147b06ef345bc3887f33faaab2d3b906d2034300 - Sigstore transparency entry: 2306833207
- Sigstore integration time:
-
Permalink:
jakehadar/bikeshare-client-python@a4c14e1af3c9ed11232993bf96a0cf44de8b8884 -
Branch / Tag:
refs/heads/master - Owner: https://github.com/jakehadar
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@a4c14e1af3c9ed11232993bf96a0cf44de8b8884 -
Trigger Event:
workflow_dispatch
-
Statement type: