| ISO week | Dates | Days | Snapshots | Speed (mph) | Wait (min) | Reliab. (%) | Bunch / snap | 20+ gaps | 30+ gaps | Buses (avg) | Buses (peak) | Routes |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| No weekly rows yet. | ||||||||||||
Loading collection stats…
Every metric the live page reports is captured at the daily, weekly, and monthly level — nothing is computed only at view time. Specifically:
Where the data lives (all viewable on GitHub):
data/snapshots/YYYY-MM-DD.jsonl — raw 5-min collections, one JSON line per snapshot, ~1,500 vehicles each. Never deleted.data/daily/YYYY-MM-DD.json — full daily roll-up including the per-route, per-hour, and per-borough slices above.data/summary/weekly.json & monthly.json — system-level period rollups (the source for the table above).data/summary/weekly-routes.json & monthly-routes.json — per-route history keyed by route shortname.data/summary/latest.json — convenience file with current/last-week/last-month plus 12 weeks & 12 months of context.How collection actually runs: a GitHub Actions workflow fires once at the top of every hour from 6 AM to 11 PM ET. Inside each run, the collector polls the MTA API 18 times at 30-second intervals, producing dense, evenly spaced samples (~306 snapshots per day). The hourly cron is reliable in a way short-interval crons aren't, and the in-run loop gets us the 30-second cadence the speed math actually needs. Cost: $0 (public repos get unlimited Actions minutes).
Known limitations — read these numbers with appropriate skepticism:
If something looks wrong, the audit trail is the raw JSONL plus the daily JSON — both are public and dated. Open an issue, and the math is in collector/process.js and collector/rollup.js.
Every 5 minutes between 6 AM and 11 PM ET, a GitHub Action pulls every active bus from the MTA BusTime SIRI API and appends one JSON line to data/snapshots/YYYY-MM-DD.jsonl. Each line captures position, heading, route, direction, phase, and timestamp for ~1,500 vehicles.
Daily roll-up (process.js, runs 1 AM ET) captures every metric the live page shows, plus slices for later analysis:
Every input count is preserved in data/daily/YYYY-MM-DD.json — nothing is thrown away.
Weekly & monthly roll-ups (rollup.js): group days by ISO week (Mon–Sun) and calendar month. Take the simple mean of daily means for each metric; sum totals (snapshots, bunching events). Roll up the per-hour, per-borough, and per-route slices the same way and write them to data/summary/weekly.json, monthly.json, weekly-routes.json, and monthly-routes.json. Weekday-only and weekend-only sub-aggregates are also stored, so we can compare like-with-like across weeks.
Weeks with fewer than 7 days appear in the table with a partial-week marker (3/7 *) and are excluded from the "this week" headline cards. Change vs. last week = this week's mean minus last week's mean (one decimal). Good direction (up for speed/reliability/buses, down for wait/bunching/gaps) renders green.
See the full methodology for how each metric compares to MTA official numbers and the limits of this pipeline.