Technical overview

How It Works

Subway Intel publishes live NYC subway intelligence from MTA GTFS-Realtime feeds — arrivals, vehicle positions, service alerts, and derived delay signals — refreshed every 30 seconds.

By James Tannahill · Source on GitHub

Data ingestion

A FastAPI backend polls every MTA GTFS-Realtime feed (all NYC subway line groups) plus the camsys customer alerts JSON endpoint every 30 seconds. ETags and content hashes skip unchanged feeds to reduce parse cost.

MTA GTFS-RT (protobuf) + alerts JSON
  → parse_feed / parse_vehicle_positions / parse_alerts
  → LiveState (in-memory)
  → WebSocket broadcast + REST API + TimescaleDB writer

Views

ViewRouteDescription
Network Pulse#/pulsePer-line NOMINAL / DELAYED / DISRUPTED status, alerts, track diagram
My Stations#/stationsSaved stops with live m:ss countdowns and direction filter
Nearby#/nearbyGPS nearest stations; deep link ?near=STOP_ID
Delay Intel#/intelHeuristic delay signals from live trip deviation
Live Map#/mapMapbox GL JS + NYC DOT track GeoJSON + live vehicles
Plan#/planTrip planning with one-transfer fallback

Delay intelligence

Line health and Delay Intel signals are computed from live arrival headways, schedule deviation, and bunching detection — not from static MTA alert text alone. This surfaces congestion patterns before they appear in official customer alerts.

Users can confirm train presence via a HERE? feedback loop; per-route timing bias is stored and applied to displayed countdowns.

Public API

JSON REST + WebSocket on the same origin. No authentication required.

EndpointDescription
GET /api/stations/{stop_id}/arrivalsLive arrivals for a stop
GET /api/networkPer-line health summary
GET /api/delay-intelHeuristic delay signals
GET /api/commute?origin=&destination=Trip options with transfer routing
GET /api/stops/search?q=Station search
GET /api/stops/nearest?lat=&lon=Nearest stations
WS /wsFull live-state snapshot every poll cycle

Full machine-readable index: llms-full.txt

Stack