JSON feeds
The small JSON files the site's own pages draw from. Rebuilt every night; one fetch each.
Three pages on transitlab.nyc load their numbers from JSON files. You can load the same files. They are rebuilt every night, cached for five minutes, and small enough to fetch from a web page.
| Feed | Size | Drives |
|---|---|---|
/data/slow-zones.json | about 30 KB | Subway slow zones |
/data/bus-speeds.json | about 60 KB | Bus speeds |
/data/bus-lanes.json | about 1.7 MB | Bus lane map |
All three start with updated, the date the file was built (YYYY-MM-DD). Field names may grow; we won’t rename or remove one without notice here.
const zones = await fetch("https://transitlab.nyc/data/slow-zones.json").then((r) => r.json())console.log(zones.summary.active, "slow zones active")slow-zones.json
Section titled “slow-zones.json”Subway slow zones, the same numbers as the subway_slow_zones and subway_slow_zones_weekly tables, already summed up.
| Field | Type | What it is |
|---|---|---|
updated | date | When the file was built. |
dataThrough | date | Last weekday of train data in it. |
summary.active | number | Slow zones active in the last week of data. |
summary.riderHoursPerWeekday | number | Rider-hours lost each weekday, averaged over the last four full weeks. |
summary.trainHoursPerWeekday | number | The same in train-hours. |
weekly[] | list | One entry per week since January 2022. |
weekly[].week | date | Monday of the week. The first and last weeks are partial. |
weekly[].riderHours | number | Rider-hours lost to slow zones that week. |
weekly[].trainHours | number | Train-hours lost that week. |
active[] | list | Up to 30 active zones, costliest first. Each is a zone, below. |
biggest[] | list | The 25 costliest zones since 2022. |
A zone has:
| Field | Type | What it is |
|---|---|---|
segment | string | Readable name, e.g. Queensboro Plaza → 60 St tunnel. |
routes | string | Routes through it, space-separated, e.g. N W. |
start, end | date | First and last slow weekday. |
slowDays | number | Slow weekdays in the zone. |
baselineS | number | Usual seconds for the segment. |
extraS | number | Median extra seconds per train. |
extraPct | number | extraS as a percentage of baselineS. |
riders | number | Riders through the segment on a typical weekday. |
riderHoursLost | number | Total rider-hours lost over the zone. |
trainHoursLost | number | Total train-hours lost. |
bus-speeds.json
Section titled “bus-speeds.json”Bus speeds from two sources: the MTA’s monthly figures (history, a month or so late) and our own daily speeds (recent days).
| Field | Type | What it is |
|---|---|---|
updated | date | When the file was built. |
mtaThrough | month | Latest month the MTA has published (YYYY-MM). |
oursThrough | date | Latest day of our own speeds. |
periods.routes | list of months | The three months the route speeds cover. |
periods.routesYearAgo | list of months | The same months a year before. |
summary.citywideMph | number | Weekday citywide speed in mtaThrough. |
summary.citywideYearAgo | number | The same month a year before. |
summary.cbdLocalChange | number | Change in local bus speed inside the congestion zone, 2024 to 2025, in percent. |
summary.outsideLocalChange | number | The same outside the zone. |
summary.slowest | route | The slowest local or Select Bus route. |
monthly[] | list | Weekday speed by month since 2019: month, citywide and one field per borough (Bronx, Brooklyn, Manhattan, Queens, Staten Island). |
cbd[] | list | Local buses, weekdays 6 am to 8 pm, since 2023: month, inside and outside the congestion zone, in mph. |
daily[] | list | Our citywide speed by day: date, weekday (true or false), mph. |
routes[] | list | Every route, slowest first. Each is a route, below. |
A route has:
| Field | Type | What it is |
|---|---|---|
route | string | MTA route, e.g. M50. |
borough | string | Borough. |
type | string | MTA route type: LCL/LTD, SBS, EXP and so on. |
mph | number | Weekday speed over periods.routes. |
mphYearAgo | number or null | Over periods.routesYearAgo. |
changePct | number or null | Change from a year before, in percent. |
ours | number or null | Our speed over our last 14 weekdays. |
Speeds are miles over hours summed across routes, so busier routes count for more, as in the MTA’s own figures.
bus-lanes.json
Section titled “bus-lanes.json”Where buses lose time, for the bus lane map. Coordinates are [longitude, latitude], rounded to about a metre and simplified to within 8 m.
| Field | Type | What it is |
|---|---|---|
updated | date | When the file was built. |
period.start, period.end | month | The months of MTA data used. |
summary.busHoursLost | number | Bus-hours lost each weekday, all stretches. |
summary.withoutLane | number | Of those, on stretches without a bus lane. |
summary.withLane | number | On stretches with one. |
summary.routeMilesWithout | number | Route-miles without a bus lane. |
summary.routeMilesWith | number | Route-miles with one. |
streets[] | list | The 25 streets without bus lanes where buses lose the most time: street, borough, routes, lost (bus-hours a weekday), mph, lane (share covered, 0 to 1), riders, routeMiles. |
stretches[] | list | Every stretch between two timepoint stops. Short names keep the file small; see below. |
lanes[] | list of lines | NYC DOT bus lanes, each a list of [lon, lat] points. |
A stretch has:
| Field | What it is |
|---|---|
r | Route. |
a, b | Timepoint stops at each end. |
mph | Average speed, 7 am to 7 pm. |
best | Speed in the stretch’s fastest hour. |
delay | Minutes per bus over the best hour’s time. |
buses | Buses a weekday, 7 am to 7 pm. |
lost | Bus-hours lost a weekday. |
perMile | lost per mile. |
lane | Share of the stretch with a bus lane, 0 to 1. |
c | The stretch’s path, a list of [lon, lat] points. |
The same stretches, with full names and no shapes, are in the bus_lanes table.