Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/samgutentag/bcycle-map/llms.txt

Use this file to discover all available pages before exploring further.

The /recent endpoint returns a pre-computed typical hourly availability profile for a single station. The worker reads a profile object written by the compute-typicals GitHub Action, so no parquet parsing happens at request time — the response stays well within the 10 ms CPU budget. The profile always contains exactly 24 hourly entries (hour 0–23 in the system’s local timezone). When at least 21 days of history are available (daysCovered >= 21), the worker selects the per-day-of-week slice that matches today; otherwise it returns the all-days aggregate. The response field isDowFiltered tells callers which mode is active.

Endpoint

GET /api/systems/:systemId/stations/:stationId/recent

Path parameters

systemId
string
required
The unique system identifier matching a registered network.Example: bcycle_santabarbara
stationId
string
required
The opaque station identifier from the GBFS feed.Example: station_012

Response

Returns the typical profile with Cache-Control: max-age=300. The endpoint always returns 200 OK — if no profile exists yet (new system or station) it returns an empty 24-hour shape with daysCovered: 0.
stationId
string
required
Echo of the requested station identifier.
hours
array
required
Exactly 24 entries, one per hour of the day (0–23) in the system’s local timezone. Values are averages over the historical data window used for this profile.
currentHour
number
required
The current local hour (0–23) in the system’s timezone at the time of the request. Used by the frontend to highlight the active bar in the chart.
currentDow
number
required
The current day of week (0 = Sunday … 6 = Saturday) in the system’s timezone. Used to select the day-of-week slice when isDowFiltered is true.
daysCovered
number
required
Total number of calendar days of polling history that were used to build this profile. 0 when no profile file exists yet.
isDowFiltered
boolean
required
true when daysCovered >= 21 and the hours array reflects the typical pattern for the current day of week. false when all days are averaged together.
label
string
required
Human-readable label for the profile mode, suitable for display in the chart header.
  • "Typical Monday" (or any weekday name) when isDowFiltered is true.
  • "Typical (all days)" when there is not enough data to filter by day of week.
  • "Typical (no history yet)" when daysCovered is 0.
timezone
string
required
The IANA timezone string used to compute currentHour and currentDow, sourced from the system’s KV latest blob.

Example request

curl https://bcycle-map-read-api.developer-95b.workers.dev/api/systems/bcycle_santabarbara/stations/station_012/recent

Example response

{
  "stationId": "station_012",
  "hours": [
    { "hour": 0,  "bikes": 6, "docks": 5, "samples": 42 },
    { "hour": 1,  "bikes": 7, "docks": 4, "samples": 42 },
    { "hour": 8,  "bikes": 3, "docks": 8, "samples": 42 },
    { "hour": 12, "bikes": 2, "docks": 9, "samples": 42 },
    { "hour": 17, "bikes": 1, "docks": 10, "samples": 42 },
    { "hour": 23, "bikes": 5, "docks": 6, "samples": 42 }
  ],
  "currentHour": 14,
  "currentDow": 1,
  "daysCovered": 28,
  "isDowFiltered": true,
  "label": "Typical Monday",
  "timezone": "America/Los_Angeles"
}

Empty profile (no history)

When no pre-computed profile exists for the station the worker returns:
{
  "stationId": "station_012",
  "hours": [
    { "hour": 0, "bikes": 0, "docks": 0, "samples": 0 },
    ...
    { "hour": 23, "bikes": 0, "docks": 0, "samples": 0 }
  ],
  "currentHour": 14,
  "currentDow": 1,
  "daysCovered": 0,
  "isDowFiltered": false,
  "label": "Typical (no history yet)",
  "timezone": "America/Los_Angeles"
}
Profile objects are pre-computed and stored at gbfs/{systemId}/typicals/{stationId}.json in R2 by the compute-typicals GitHub Action. The worker never parses raw parquet at request time, keeping CPU usage well under the Worker 10 ms budget. Profiles are refreshed on a schedule; the max-age=300 cache means the browser re-fetches at most once every 5 minutes.
Day-of-week filtering activates once daysCovered >= 21. Below that threshold there are fewer than 3 observations per weekday — not enough for a stable per-day pattern — so the all-days aggregate is used instead.

Build docs developers (and LLMs) love