Live NYC subway arrival times as JSON, straight from the MTA's real-time GTFS feeds. One GET request — that's the whole integration.

No API key No auth Free

Base URL: https://py-nycmta.web.app

curl "https://py-nycmta.web.app/arrivals/F/F24?direction=N&count=3"

There is no sign-up, token, or key of any kind: every endpoint is public and CORS-enabled, so you can call it from a terminal, a server, or directly from browser JavaScript on any site.

Try it live


      

Endpoints

GET/health

Liveness check. Always returns 200 with {"status": "ok"}.

GET/arrivals/{line}/{stop}

Upcoming arrivals for one subway line at one stop, sorted soonest-first.

ParameterInDescription
line path One of the 22 supported lines, e.g. F or 6. Case-insensitive. Unknown lines return 400.
stop path 3-character base GTFS stop ID without a direction suffix, e.g. F24 (7 Av). Full list on the stations page. Unknown or mismatched stop IDs return 200 with an empty arrivals list, not an error.
direction query N (northbound), S (southbound), or both. Default both. Anything else returns 422.
count query Optional. Limit the number of arrivals returned; must be an integer > 0, otherwise 422. Omit for all upcoming arrivals.

Response

FieldTypeDescription
linestringThe requested line, uppercased.
stopstringThe stop ID exactly as you sent it.
directionstringThe direction filter that was applied.
arrivals[]arrayUpcoming arrivals, sorted by arrival time. Empty if no trains are due.
arrivals[].train_idstringLine identifier of the arriving train.
arrivals[].minutes_awayintegerWhole minutes until arrival. The most reliable field for countdowns.
arrivals[].directionstring"N" or "S".
arrivals[].arrival_timestringNaive ISO 8601 timestamp, e.g. 2026-06-09T14:32:00. No timezone offset (UTC on the hosted API) — prefer minutes_away.
arrivals[].statusstringHuman-readable countdown, e.g. "4 mins".

Example

{
  "line": "F",
  "stop": "F24",
  "direction": "N",
  "arrivals": [
    {
      "train_id": "F",
      "minutes_away": 4,
      "direction": "N",
      "arrival_time": "2026-06-09T14:32:00",
      "status": "4 mins"
    },
    {
      "train_id": "F",
      "minutes_away": 11,
      "direction": "N",
      "arrival_time": "2026-06-09T14:39:00",
      "status": "11 mins"
    }
  ]
}

Errors

StatusWhenBody
400 Unknown line {"detail": "<message listing valid trains>"}. Note: the message may mention S and SIR, but those are not queryable here.
422 Invalid direction or count Standard FastAPI validation error body.
502 Upstream MTA feed failure or timeout {"detail": "Failed to fetch MTA feed"}. Usually transient — retry after a moment.
200 Unknown stop ID, stop not on that line, or simply no trains due A normal response with "arrivals": []not a 404. Validate stop IDs against the stations list if you need to distinguish.

No rate limits are enforced — please poll courteously (the underlying MTA feeds update roughly every 30 seconds, so there's nothing to gain from hammering). Responses are live and uncached; expect a typical latency of 1–3 seconds while the MTA feed is fetched.

Supported lines

123 456 7 ACE BDFM G JZ L NQRW

All 22 lettered and numbered lines are supported. The S shuttles (42 St, Franklin Av, Rockaway Park) and the Staten Island Railway are not available through this API.

Examples

curl

# Next 3 northbound F trains at 7 Av
curl "https://py-nycmta.web.app/arrivals/F/F24?direction=N&count=3"

# Everything due at Times Sq-42 St on the N, both directions
curl "https://py-nycmta.web.app/arrivals/N/R16"

Python

import httpx

resp = httpx.get(
    "https://py-nycmta.web.app/arrivals/F/F24",
    params={"direction": "N", "count": 3},
)
resp.raise_for_status()

for arrival in resp.json()["arrivals"]:
    print(f"{arrival['train_id']} train in {arrival['status']}")

JavaScript

const res = await fetch(
  "https://py-nycmta.web.app/arrivals/F/F24?direction=N&count=3"
);
if (!res.ok) throw new Error(`HTTP ${res.status}`);

const { arrivals } = await res.json();
arrivals.forEach((a) => console.log(`${a.train_id} train in ${a.status}`));

Works from any origin — CORS is enabled for GET across the whole API.