Home/Guides/Developers
Developers

Real-Time Bike Availability: How to Display It in an Application?

Display bike availability with the ROOTE API: timestamping, freshness, unknown values, refreshing, and expired data within your application.

By ROOTE·7 min read
Real-Time Bike Availability: How to Display It in an Application?
An observed availability with its context.

The essentials at a glance

Present the available quantity along with its freshness status, timestamps, and bike pickup conditions. An unknown value remains unknown; an old observation does not become real-time just because your app just received it.

To display real-time bike availability in an application, associate the returned number with its freshness and vehicle pickup possibilities. An observation describes what the source knew at a moment in time; it does not guarantee that a bike will still be there upon arrival.

The ROOTE mobility contract distinguishes stations, individual vehicles, and their states. The interface must reflect these differences without confusing an unknown value, an empty station, and a temporarily unavailable source.

Separating a station from an individual vehicle

A station can expose counters for bikes and return docks. An individual vehicle has an availability state and possibly other information. Avoid counting a station as a bike or summing counters representing the same inventory.

In the ROOTE mobility DTO, availability.bikes and availability.docks can be unknown. Propulsion or battery fields should only be displayed if they exist and are interpreted according to the contract.

ROOTE OpenAPI contract

Read freshness and timestamps

FieldInterpretation
freshness.stateReported state: fresh, stale, unknown, or static
freshness.source_updated_atSource update date, if known
freshness.received_atReception date as indicated by the contract
freshness.expires_atValidity expiration date indicated, if known
availability.bikesKnown quantity or unknown value
pickup.enabled and pickup.stateInformation about picking up a bike at the station

The time of your call is not automatically the observation time. A result received at 10:00 may contain a source updated at 9:45. Do not display “updated now” based solely on your interface's reception time.

Plan for distinct display states

Data receivedDisplay to expect
Known quantity and fresh dataObserved quantity and temporal indication
Quantity equal to zeroNo bike observed, with its temporal context
Quantity nullUnknown availability
Stale state or expired validityOld data; suggest a refresh
pickup.enabled=falsePickup unavailable even if a counter is positive
Search errorTemporary availability unavailable, without converting to zero

Do not classify an unknown or static state as fresh. Station information may be stable while counters evolve quickly. Also retain warnings and attributions required by the response.

An example of normalization before rendering

The following function produces a presentation state from a station already validated against the ROOTE schema. It is not a complete response validator. Visible labels should come from your interface’s translation keys.

function availabilityView(station, now = Date.now()) {
  const freshness = station.freshness;
  const expiresAt = freshness.expires_at
    ? Date.parse(freshness.expires_at) : null;
  const expired = expiresAt !== null &&
    Number.isFinite(expiresAt) && expiresAt <= now;
  if (station.pickup.enabled === false ||
      station.pickup.state === 'unavailable_now') {
    return { state: 'pickup_unavailable', count: null };
  }
  if (expired || freshness.state === 'stale') {
    return { state: 'stale', count: null };
  }
  const count = station.availability.bikes;
  if (freshness.state !== 'fresh' || count === null ||
      !Number.isFinite(count) || count < 0) {
    return { state: 'unknown', count: null };
  }
  return {
    state: count === 0 ? 'empty' : 'observed', count,
    sourceUpdatedAt: freshness.source_updated_at,
    receivedAt: freshness.received_at,
    pickupState: station.pickup.state
  };
}

Even with an observed state, do not transform pickupState=unknown into confirmed pickup. The counter remains an observation. Display pickup context if your product helps the user choose a station.

Refresh without unnecessarily multiplying calls

Adapt refreshing to the published expiration, service conditions, and user behavior. Group identical requests, avoid background calls on an inactive page, and cancel those from a replaced search.

A local cache delay does not prove source freshness. After an error, you may keep a last dated observation if your interface explicitly presents it as old. Do not erase this distinction at the first successful retry if the source remains stale.

Understanding the link with GBFS

GBFS describes shared mobility services and their published states. A direct integration must interpret files, version, and feed timestamps. With a standardized API, use the API contract; do not add an assumed GBFS field missing from its response.

Choosing between GTFS, GTFS Realtime, and GBFS

Testing scenarios misleading the reader

Test a real zero, an unknown value, an expired validity, a disabled pickup, and an error after a valid result. Also verify display time zones. A positive counter must never produce 'bike reserved' and an unavailability must never produce an invented zero.

Handling empty responses and errors

Applying these rules in an AI assistant

User guide to finding a bike

For developersROOTE Mobility API

Mobility around a location.
Directly in your application.

  • Search
    around a location
  • Access
    mobility data
  • Integrate into
    your application

From the map to the data: find nearby mobility options and services with the ROOTE API.

Frequently asked questions

Does a positive quantity guarantee a bike when I arrive?

No. It describes an observation, which may change between the search and your arrival.

Can null be replaced by zero?

No. null indicates an unknown value; zero is a known quantity and carries a different meaning.

Should I refresh every few seconds?

Use validity indications, service limits, and your interface’s needs. An arbitrary frequency does not guarantee a fresher source.

Why not explore nearby?

Explore your neighbourhood with ROOTE and find the information available to prepare your journey.

Explore the ROOTE map ↗