Skip to main content

bikeshare-client-python

Test Coverage Status

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 six dependency and the Python 2/3 compatibility shims it enabled.
  • Fixed an install-breaking bug in the vendored versioneer.py on Python 3.12+ (configparser.SafeConfigParser was removed in 3.12).
  • Replaced the unmaintained pytest-runner/setup.py test integration with running pytest directly.
  • Replaced the dormant Travis CI setup with GitHub Actions, testing across Python 3.9-3.14.
  • Extended tox to 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 NYC system_id (now lyft_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.

Metadata

Release files for gbfs-client 0.1.9

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for gbfs-client 0.1.9
File Size Uploaded Provenance Actions
gbfs_client-0.1.9.tar.gz 67.6 kB With provenance Details for gbfs_client-0.1.9.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for gbfs-client 0.1.9
File Interpreter ABI Platform Actions
gbfs_client-0.1.9-py3-none-any.whl Python 3 none any Details for gbfs_client-0.1.9-py3-none-any.whl

Total release size: 118.1 kB

Release files / gbfs_client-0.1.9.tar.gz

Download URL gbfs_client-0.1.9.tar.gz
Size 67.6 kB
Tags Source
SHA-256 checksum
How to use checksums
07405771159bb92f16d86b0a33aa03a9202bcfd8289b3b58a7ee99e6c75edfce
BLAKE2b-256 checksum
How to use checksums
caef33ea4b2bce4c64c035acd870650d183640ba2e8d5a884981bc77c216ff5e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 31, 2026.

Transparency log

Release files / gbfs_client-0.1.9-py3-none-any.whl

Download URL gbfs_client-0.1.9-py3-none-any.whl
Size 50.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
4cc89a0846c7103e63436d03147b06ef345bc3887f33faaab2d3b906d2034300
BLAKE2b-256 checksum
How to use checksums
0b4ff34ed5accd6f2999ed8c3ef032bbace176e487653906e89daf2c15f33227
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 31, 2026.

Transparency log

Security for gbfs-client 0.1.9

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for gbfs_client-0.1.9.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 31, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page