What’s new in GBFS v2.0 🚲🛴
New micromobility standard improves shared mobility
Have you ever heard of the penny-farthing? Even if you don’t know the name, I bet you’ve seen it in pictures:
The penny-farthing is the bicycle of yesteryear with the big wheel in front and the tiny wheel at the back. When the penny-farthing arrived on the market, it offered faster speeds and better shock absorption than its wheeled predecessors.
It remained popular for about a decade in the late 1800’s, but it rapidly went out of style with the next incarnation of the bicycle, the so-called “safety bicycle,” which looks a lot more like the bikes we ride today.
Was the penny-farthing a game changer? Yes 👍. Was the safety bicycle better? Absolutely 👍👍.
The 2.0 version of the General Bikeshare Feed Specification (GBFS) for shared bike and scooter data also makes key improvements to something that was already good. GBFS v2.0 is the successor to the original GBFS launched by the North American Bikeshare Association (NABSA) in 2015.
In 2019, NABSA chose MobilityData to coordinate the GBFS community to change processes and meet evolving industry needs. However, we didn’t create v2.0 alone: we’ve worked together as a community to make it easier than ever to find mobility options. We are full of gratitude for the community’s ongoing participation at MobilityData workshops and on GitHub 🙌.
New in v2.0 🎉
GBFS v1.0 saw great success — over 230 shared bike and scooter operators have adopted GBFS as a way to share real-time information with mobile apps used by millions of travellers around the world. However, as shared micromobility grew in popularity, there were an increasing number of use cases that GBFS did not perfectly accommodate, as well as clarifications needed for how certain fields should be used. Following are improvements new to GBFS v2.0.
Deep links 🔗
A primary use case of GBFS is being able to show vehicles available for rent in third-party apps. When the traveler sees a bike or scooter they want, the next step is tapping on that vehicle icon to rent it 👆. However, this is where GBFS v1.0 fell short — it lacked any information for integrators to use to link the third party app to the provider’s native app.
GBFS v2.0 fixes this by adding new fields to support “deep links”. This enables a seamless traveler experience — you can now tap on a bike or scooter in the third party app, and the provider’s rental app is immediately opened to that particular vehicle or station.
Deep links are supported in a few different ways, outlined in the following subsections.
App Links on Android and Universal Links on iOS 📱
The preferred method for GBFS providers to implement deep links is called “Android App Links” on Android and “Universal Links” on iOS. These deep link formats require additional configuration on the provider’s website in addition to the provider’s native app, but add the following benefits over older deep link formats:
- When tapping on the vehicle to rent, the traveler is always directed straight to the correct provider app — they don’t have to deal with any intermittent dialogs to select if they should open the link in a browser or native app.
- Third party apps don’t have to manually check if the provider’s app is installed on the device and redirect to the respective app store if not — this situation is automatically handled by the operating system.
- You can define deep links that work both in the traveler’s web browser, as well as on native platforms.
After a GBFS provider has implemented Android App Links and Universal Links in their native apps and website, they will need to add a rental_apps
section with discovery_uris
to their GBFS system_information.json
file.
system_information.json
{
"last_updated": 1572447999,
"data": {
"system_id": "abc_cityname",
"short_name": "ABC Bike Rental",
"rental_apps": {
"android": {
"discovery_uri": "com.abcrental.android://"
},
"ios": {
"discovery_uri": "com.abcrental.ios://"
}
}
}
…
discovery_uris
are Universal Resource Identifiers (URIs) that can be used to discover if the provider’s native app is installed on the respective platforms (e.g., using PackageManager.queryIntentActivities() on Android and UIApplication canOpenURL: on iOS). This field allows a third-party app to prioritize rental apps for an individual traveler based on whether they already have a particular rental app installed. For example, if there are 3 bikeshare providers in a city, and the traveler already has a native app for bikeshare provider “ABC Rental” installed on their phone, a third party app should prioritize options using “ABC Rental” over other providers when showing the traveler multimodal travel options.
Next, for each station (in station_information.json
) or floating vehicle (in free_bike_status.json
), a unique deep link should be created in rental_uris
that will take the traveler to that station or vehicle in the provider’s app.
Here’s an example for a station:
station_information.json
"stations": [
{
"station_id": "425",
"name": "Coppertail",
"lat": 27.9563335328521,
"lon": -82.430436084371,
"rental_uris": {
"android": "https://www.abc.com/app?sid=1234567890&platform=android",
"ios": "https://www.abc.com/app?sid=1234567890&platform=ios"
}
}
…
The deep link can be any format useful for a particular provider’s implementation and can contain various parameters meaningful to the provider. In the above example, the provider is using the sid
parameter as an identifier for this station, which the provider’s native app can then extract to show the traveler this exact station. Some parameters are reserved for communicating analytics between third-party and provider apps — more on that in a bit.
Third-party apps can then open the rental_uris
fields as deep links on the respective platforms — for example, using the android.intent.action.VIEW
intent on Android. Third-party apps should see the respective documentation for “Deep Links” on Android and “Communicate with Other Apps” on iOS for more details.
Note that as a best practice providers should rotate identifiers within deep links after each rental to avoid unintentionally exposing private vehicle trip origins and destinations — see the section “Emphasizing traveler privacy” below for a discussion of privacy with bike_id
.
Basic native deep links format 📱
Older provider apps may not support “Android App Links” on Android and “Universal Links” on iOS but still may have basic support for deep links.
To allow third-party apps to use these deep links, in addition to the discovery_uri
field, providers must also provide a store_uri
field for each platform. This allows the third-party app to direct travelers to the respective app stores if the provider’s app is not installed:
system_information.json
{
"last_updated": 1572447999,
"data": {
"system_id": "abc_cityname",
"short_name": "ABC Bike Rental",
"rental_apps": {
"android": {
"discovery_uri": "com.abcrental.android://"
"store_uri": "https://play.google.com/store/apps/details?id=com.abcrental.android",
},
"ios": {
"store_uri": "https://apps.apple.com/app/apple-store/id123456789",
"discovery_uri": "com.abcrental.ios://"
}
},
…
If you’re a provider, you can find the application IDs to use in the store_uri
fields for your app using the respective documentation for iOS and Android.
Similar to the previous deep link method, providers will need to specify a unique deep link in rental_uris
for each station or floating vehicle. The main difference here is that your URI scheme will likely be custom and not start with http
. In the below example, the provider has defined a custom URI scheme of com.abcrental.android://
for Android and com.abcrental.ios://
for iOS:
station_information.json
"stations": [
{
"station_id": "425",
"name": "Coppertail",
"lat": 27.9563335328521,
"lon": -82.430436084371,
"rental_uris": {
"android": "com.abcrental.android://open.abc.app/app?sid=1234567890",
"ios": "com.abcrental.ios://open.abc.app/app?sid=1234567890"
}
},
…
Prior to opening the deep links, third-party apps will now also need to first check if the traveler has the app installed by using the discovery_uri
. If not, then the third party app should use the store_uri
to open the app store to the listing for the provider’s app.
Web apps 🌐
Finally, GBFS v2.0 also adds support for deep links in web apps (i.e., not native iOS or Android) for stations and floating vehicles via the rental URI web
:
"stations": [
{
"station_id":"425",
"name":"Coppertail",
"lat":27.9563335328521,
"lon":-82.430436084371,
"rental_uris": {
"web":"https://www.abc.com/app?sid=1234567890",
}
},
…
Third party applications can direct travelers to this URL to see this particular station in a web browser.
Analytics 📊
Third party apps can also append a few parameters to deep links to pass analytics information to provider apps. The following parameters are specifically listed in v2.0, although more may be supported in the future:
client_id
— the domain name for the third party app. (e.g.,google.com
for Google)ad_id
— Advertising ID issued to the third party app (e.g., Identifier for Advertiser (IFDA) on iOS or Google Advertising ID (AAID) on Android)token
— An identifier issued by the provider’s app to the third-party app
See the GBFS Deep Links Analytics documentation for more information.
Easier feed discovery and communication 🤝
GBFS v2.0 also makes it easier to discover various files within the feed: the “autodiscovery” file, gbfs.json
, which contains links to all other feed files, is now required in v2.0. The name field for each file within gbfs.json
has now been standardized as well, ensuring that a consumer’s software can keep track of a single link to gbfs.json
, versus tracking multiple URLs to various files for a single feed.
To better facilitate communication between feed consumers and producers, a new field feed_contact_email
has been added to system_information.json
. This email gives consumers a clear method to contact the operator of a feed to report technical issues.
Spec clarifications 🛠️
A few key clarifications have also been made to fields in GBFS v2.0.
In v1.0, the exact meaning of num_bikes_available
and num_docks_available
wasn’t clear when considering whether or not the dock was open for use. For example, if there are physically 5 bikes at a dock, but is_renting
is set to false, should num_bikes_available
be 0 or 5?
In v2.0, the definitions of num_bikes_available
and num_docks_available
have been clarified to be the physical state of the number of bikes and docks available at the station, regardless of whether or not you could rent or return a bike. As a result, consumers must now be sure to check the is_renting
field to know if an “available” bike can be rented, and the is_returning
field to know if a customer can return a bike to an “available” dock.
In another change new to v2.0, all fields in the spec that were either true or false are now represented as canonical JSON boolean values of true
or false
, instead of the previous integer representation of 0
and 1
.
So, feeds that looked like this in v1.0:
{
"station_id": "2721922",
"is_installed": 1,
"is_renting": 1,
"is_returning": 1,
…
},
…will now look like this in v2.0:
{
"station_id": "2721922",
"is_installed": true,
"is_renting": true,
"is_returning": true,
…
},
Emphasizing traveler privacy 🕵️🔒
In a move to reduce the potential exposure of private data, the rotation of bike_id
after each rental for floating vehicles is now required in v2.0. This means that the ID of a particular vehicle in the GBFS feed prior to it being rented must be different from the ID of the same vehicle when it re-appears in the feed after the rental is complete. This technique makes it harder to reconstruct individual traveler trips from raw data.
Easing the transition to v2.0 😌
GBFS v2.0 is a major version release, which means that some changes will break existing consumers. For example, when a producer changes boolean values from 0/1
to false/true
, consumer software that isn’t yet updated won’t be able to understand this.
Therefore, as producers update their feeds, it is important to support both the current v1.0 version as well as the new v2.0 version in parallel at two different URLs. GBFS defines the following best practices in this area for version endpoints:
- GBFS producers should provide endpoints that conform to both the current specification long term support (LTS) branch (currently v1.0) as well as the latest release branch (currently v2.0) within at least 3 months of a new spec MAJOR or MINOR version release. It is not necessary to support more than one MINOR release of the same MAJOR release group because MINOR releases are backwards-compatible.
- GBFS consumers should, at a minimum, support the current LTS branch. It is highly recommended that GBFS consumers support later releases.
- Default GBFS feed URLs (e.g. https://www.example.com/data/gbfs.json or https://www.example.com/data/fr/system_information.json) must direct consumers to the feed that conforms to the current LTS documentation branch (currently v1.0).
If producers aren’t ready to support multiple feed versions simultaneously but still want to add some of the non-breaking features in v2.0, a minor update to GBFS v1.1 has also been released. This allows producers to add deep links and feed_contact_email
to their feeds, both of which are compatible with existing v1.0 consumers.
For more information on GBFS spec versioning, see the Specification Versioning documentation.
And, if you want to see the full GBFS v2.0 specification, check it out here.
What’s next?
We’ve laid the groundwork for the future evolution of GBFS, because we want to maintain continuity in the format while expanding its use cases. We’re already working on a new minor release, GBFS v2.1, which includes official support for geofencing zones (think dockless systems and virtual stations) as well as specifying different vehicle types. New breaking changes are also being prepared for a future v3.0 release, including requiring producers to define the license for their feeds.
Finally, we welcome you to get involved in the GBFS change process! Head over to the GBFS GitHub repository to see open discussions on various issues as well as proposed changes in “pull requests”. We’ll see you there! 👋