Nov 2, 2026 / Gary Tokman

How to get NYC subway arrivals in JavaScript without parsing GTFS-realtime

Trains near Union Sq
Subway arrival board
Live
Updated --:--

In short

  • Call mta.subway.arrivals({ stopId, route }) with mta-js and you get arrivals with the route, stop name, destination, direction text, and minutes. The hosted API does the work you would otherwise do yourself: decoding the protobuf feed, picking the right feed for each line, and mapping north and south directions.
  • Install: npm i mta-js. One call: mta.subway.arrivals({ stopId: "L08", route: "L", limit: 5 }).
  • Destination to direction: mta.subway.direction() turns "Union Sq" into a feed direction. Free option: mta-js is MIT licensed and can call MTA feeds directly.

What the raw MTA feed asks of you

The demo above is live: it calls mta.subway.arrivalBoard() near Union Sq, shown in "Find arrivals near a location" below.

The MTA publishes subway realtime data as GTFS-realtime, a protobuf format with NYCT-specific extensions. The MTA developer resources page describes the feeds and the protobuf files you need to read the extensions. Building directly on that means handling four things:

Decoding

The feed is binary protobuf, so you need the GTFS-realtime definitions plus the NYCT extension file.

Choosing a feed

Subway data is split by line group. Open source projects map routes to separate feed URLs, for example one for the L, one for A, C, and E, one for B, D, F, and M, and one for N, Q, R, and W.

Directions

Stops carry north and south directions, and those labels do not always match the geography. The L is the usual surprise: north runs into Manhattan and south runs into Brooklyn.

Freshness

The MTA subway reference says the feed is generated every 30 seconds, so you need caching and sensible polling.

Get arrivals with mta-js

Each arrival includes route metadata, the stop, a direction, an ISO arrivalTime, minutes, and a realtime flag. Display fields such as displayDirection and destination can be missing, so keep a fallback like the one above.

subway-arrivals.ts
import { MTA } from "mta-js"
const mta = new MTA({ apiKey: process.env.MTA_API_KEY })
// Next L trains at Bedford Av
const arrivals = await mta.subway.arrivals({
stopId: "L08",
route: "L",
limit: 5,
})
for (const arrival of arrivals) {
const where = arrival.displayDirection ?? arrival.destination ?? "unknown direction"
console.log(
`${arrival.route.shortName} ${where} from ${arrival.stop.displayName ?? arrival.stop.name}: ${arrival.minutes} min`,
)
}

Turn a destination into a direction

Riders think in destinations, not north and south. mta.subway.direction() resolves a destination against GTFS route order:

subway-direction.ts
const toward = await mta.subway.direction({
route: "L",
fromStopId: "L06",
destination: "Union Sq",
})
// Returns a typed direction, the destination stop, and a label such as "8 Av"
const arrivals = await mta.subway.arrivals({
stopId: "L06",
route: "L",
direction: toward.direction,
limit: 5,
})

Find arrivals near a location

Log board once to see the response shape before you build a UI around it.

nearby-arrivals.ts
const board = await mta.subway.arrivalBoard({
lat: 40.7359,
lon: -73.9906,
limitStations: 3,
limitArrivals: 4,
})

The same call over HTTP

The REST path is GET /api/v1/subway/arrivals. Send your key in an x-api-key header or as a bearer token. See the API reference for parameters and the subway arrivals guide.

Self-host or use the hosted API

mta-js is open source under the MIT license, and you can call MTA realtime feeds directly with it. Choose the hosted API when you want managed realtime endpoints, route-aware stop lookups, caching, and stable response shapes without running GTFS imports yourself. Plans start at $19 per month with a 7-day free trial. See pricing.

FAQ

Install mta-js and call mta.subway.arrivals({ stopId, route }). You get arrivals with minutes, stop names, and direction text, with no protobuf decoding on your side.

GTFS-realtime is a protobuf format for transit trip updates, vehicle positions, and alerts. The MTA publishes subway data in it, with NYCT-specific extensions.

The MTA subway reference says the feed is generated every 30 seconds. Polling more often than that does not produce fresher subway predictions.

NYC subway feeds use north and south even on east-west lines. mta-js accepts east and west aliases for the L and maps them to the feed directions.

Yes. mta-js is MIT licensed and can call MTA feeds directly. The hosted API is a paid service with a 7-day free trial.

Yes. Connect an MCP client to https://www.mtaapi.dev/mcp with your API key, and the subway_arrivals tool returns the same arrivals as mta.subway.arrivals.

Get an API key and fetch your first arrivals in a few minutes.