Bus positions
Our archive of the MTA's live bus positions, about one report per bus every 30 seconds, since October 2026.
- Rows
- 7,822,245
- Size
- 126 MB
- Covers
- 2026-10-02 onward
- Updated
- Daily
- Source
- MTA bus GTFS-realtime vehicle positions
- License
- CC BY 4.0
https://data.transitlab.nyc/v1/bus_vehicle_positions/year=YYYY/month=MM/YYYY-MM-DD.parquetRows and size from catalog.json, 2026-10-06.
What a row is
Section titled “What a row is”One change to one bus. We fetch the MTA’s bus position feed twice a minute and keep a row only when something changes: a bus appears, reports a new position, or drops out of the feed. That keeps the table far smaller than every snapshot would be, with nothing lost.
Reading change events
Section titled “Reading change events”Every row has an event. The same scheme is used by bus predictions, service alerts and the elevator tables.
event | Meaning |
|---|---|
baseline | The bus as seen in the first snapshot of each UTC hour. Every hour starts with a full set, so you can read any hour on its own. |
added | The bus appeared in the feed. |
changed | It reported something new (here: a new vehicle_ts). |
removed | It dropped out of the feed. The row repeats its last known state. |
To rebuild the feed as it stood at a moment, take each bus’s latest row at or before that time and drop buses whose latest row is removed. For speeds and paths, use vehicle_ts (when the bus reported), not observed_at (when we fetched it).
Query it
Section titled “Query it”-- Where were M15 buses between 8 and 9 am on 2026-10-06?SELECT vehicle_id, vehicle_ts, lat, lon, direction_idFROM transitlab.bus_vehicle_positionsWHERE route_id = 'M15' AND vehicle_ts >= TIMESTAMP '2026-10-06 12:00:00' -- 8 am in New York AND vehicle_ts < TIMESTAMP '2026-10-06 13:00:00' AND event <> 'removed'QUALIFY row_number() OVER (PARTITION BY vehicle_id, vehicle_ts ORDER BY observed_at) = 1ORDER BY vehicle_id, vehicle_ts;Each day is its own file, so one day reads fast straight from its URL:
SELECT route_id, count(DISTINCT vehicle_id) AS busesFROM 'https://data.transitlab.nyc/v1/bus_vehicle_positions/year=2026/month=10/2026-10-06.parquet'GROUP BY route_idORDER BY buses DESC;Columns
Section titled “Columns”row_idvarcharStable ID of the row. If a row appears twice, keep one.
observed_attimestampWhen we fetched the feed snapshot this row came from (UTC).
eventvarcharbaseline, added, changed or removed. See reading change events.
vehicle_idvarcharBus ID as the feed gives it, e.g. MTA NYCT_8721.
vehicle_tstimestampWhen the bus reported this position (UTC).
trip_idvarcharGTFS trip.
route_idvarcharMTA route.
direction_idintegerGTFS direction, 0 or 1.
start_datevarcharTrip start date, YYYYMMDD.
latdoubleLatitude.
londoubleLongitude.
bearingdoubleHeading in degrees.
stop_idvarcharStop the bus is at or heading to.
Good to know
Section titled “Good to know”- A report can repeat at the top of each hour, once as
baselineand once as the change before it. TheQUALIFYline above keeps one per report. - Files are split by the UTC day of
observed_at. - Bus speeds explains how we turn positions into speeds; bus route speeds has the result.