MBTA Boston: Bus & Train Arrivals, Timetables, Alerts
Pricing
from $1.40 / 1,000 transit row returneds
MBTA Boston: Bus & Train Arrivals, Timetables, Alerts
Next departures at any MBTA stop or station as rows: minutes away, headsign, direction, live flag and delay against the timetable. Slots the source does not predict are filled from the schedule. Other modes: timetable by date, service alerts, live vehicles, stop and route dictionaries.
Pricing
from $1.40 / 1,000 transit row returneds
Rating
0.0
(0)
Developer
Samat Makatov
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
2 days ago
Last modified
Categories
Share
When does the next bus or train leave this stop, and is the line disrupted right now? This actor answers both as
plain rows from Boston's official transit API (api-v3.mbta.com): minutes away, headsign, direction, live-or-scheduled
flag, delay against the timetable, vehicle, stop and route ids. Every slot the agency does not predict — late night, a
trip two hours out, a stop without realtime coverage — is filled from the published timetable, so a run at 03:00 still
writes rows instead of an empty dataset. Six modes share one input: arrivals, timetable, alerts, vehicles, stops and
routes. No API key, no proxy, no browser.
Use cases
- Stop or platform display: poll
arrivalsfor one station and one direction every minute and showminutesAway,headsignand the live flag on a screen in a lobby, café or office. - Station departure board for one mode: South Station carries commuter rail, subway and buses — filter
Modes of transport to
commuter-railand you get the board a rail passenger wants, withserviceDateand track direction. - "What leaves near me" for an app or an agent: give a latitude and longitude, get every stop within walking
distance with a real
distanceMetersand the next departures from each. - Service-status dashboard:
alertswrites every disruption in effect with effect, cause, severity, the routes and stops affected and the active window; add Changed in the last N hours plus Only rows not seen before for a daily watch that never reports the same alert twice. - Accessibility and step-free routing: filter alerts to
ELEVATOR_CLOSURE,ESCALATOR_CLOSURE,ACCESS_ISSUEandSTATION_ISSUEto see which lifts are out before sending someone to a station. - Timetable export and analysis:
schedulewrites every scheduled trip at a stop for one service date — trip id, stop sequence, local and UTC times, pick-up and drop-off flags — ready for a spreadsheet or a punctuality study. - Building the ids first:
stopsandroutesare the two dictionaries; run them once, keep the ids, and feed them into every other mode.
Input
Every field is optional. Values shown in the Console form as examples are prefills, not defaults — only behaviour settings have defaults, so no run is silently narrowed to a place you did not ask for.
| Field | Type | Default | Allowed values / notes |
|---|---|---|---|
mode | string | arrivals | arrivals, schedule, alerts, vehicles, stops, routes — see Modes |
stopQuery | string | — (prefill Park Street) | A stop or station name as riders say it. Resolved against the source's own stop list, stations first; the id picked is written to the log and to SUMMARY |
stopIds | string[] | — | Source ids instead of a name: place-pktrm, place-sstat, 70075, 1936. A station id covers all its platforms |
routeIds | string[] | — | Red, Orange, Blue, Mattapan, Green-B…Green-E, bus numbers (1, 66, 111, SL1), commuter rail (CR-Fitchburg…). See Route ids |
routeTypes | string[] | — (all) | light-rail, subway, commuter-rail, bus, ferry |
latitude | number | — | −90…90. Give it together with longitude instead of naming a stop |
longitude | number | — | −180…180 (Boston is about −71) |
radiusMeters | integer | 600 | 50–5000. Real metres: the actor converts to the degrees the source wants, then measures each stop itself |
directionId | string | any | any, 0, 1. Each route names its own two directions — mode routes lists them |
minutesAhead | integer | 120 | 5–1440. Mode arrivals only: how far ahead departures may lie |
serviceDate | string | — (today in Boston) | YYYY-MM-DD. Used by schedule, and by arrivals for another day |
startTime | string | — (whole day) | HH:MM Boston local, mode schedule. Past midnight counts on: 25:00 is 1 a.m. |
includeScheduled | boolean | true | Mode arrivals: fill every slot without a prediction from the timetable and compute delaySeconds where both exist |
alertEffects | string[] | — (all) | See Alert effects |
alertSeverityMin | integer | — (all) | 0–10; 3 drops most information notices |
alertLifecycles | string[] | — (all) | NEW, ONGOING, ONGOING_UPCOMING, UPCOMING |
activeOnly | boolean | true | Only alerts whose active period covers this moment |
sinceHours | integer | — (no filter) | 1–720. Keep only alerts updated inside this many hours, counted in UTC |
onlyNew | boolean | false | Write only rows whose key was not seen in an earlier run of the same filters |
maxItems | integer | 50 | 1–2000 |
fields | string[] | — (all) | Keep only these fields, in this order |
apiKey | string (secret) | — | Optional. Your own free key from the agency raises the request limit from 20 to 1000 per minute; sent as a header, never logged, never written to a row |
Typos in the dictionaries are corrected and reported ("commuter rail" → commuter-rail, "Ornage" → Orange,
"timetable" → schedule). A value nothing matches ends the run with a message naming the field — the search is
never widened behind your back.
Reference
Modes
mode | What one row is | Needs | Typical fields |
|---|---|---|---|
arrivals | The next departure of one trip from one stop | a stop, a coordinate or a route | minutesAway, timeLocal, headsign, predicted, delaySeconds |
schedule | One scheduled trip at one stop on one service date | a stop, a coordinate or a route | serviceDate, timeLocal, tripId, stopSequence, pickUp |
alerts | One published disruption | nothing | header, effect, severity, routeIdsAffected, activePeriodStart |
vehicles | One vehicle in service with its position | nothing (filter by route) | latitude, longitude, currentStatus, bearing |
stops | One stop or station | nothing (filter by route or coordinate) | stopId, stopName, municipality, wheelchairBoarding |
routes | One route or line | nothing | routeId, routeName, directionNames, routeColor |
arrivals works like this: the live predictions come first; if fewer rows than you asked for are predicted and
Fill gaps from the timetable is on, the published timetable for the same stops and window is merged in, marked
source: "schedule"; where a prediction and a timetable entry describe the same trip at the same stop, the row keeps
the live time and gains scheduledTime and delaySeconds. If the window holds nothing at all — after the last train
of the night — the run writes the first departures of the next service day rather than nothing.
Modes of transport
routeTypes value | routeType | Covers |
|---|---|---|
light-rail | 0 | Green Line branches, Mattapan trolley |
subway | 1 | Red, Orange and Blue lines |
commuter-rail | 2 | All CR-… lines |
bus | 3 | Numbered bus routes and the Silver Line (SL1…) |
ferry | 4 | Harbor ferry routes (Boat-…) |
Route ids
Rapid transit uses colour names: Red, Orange, Blue, Mattapan, Green-B, Green-C, Green-D, Green-E.
Buses use their number as the id (1, 39, 66, 111) and the Silver Line uses SL1…SL5. Commuter rail lines are
CR- plus the line name: CR-Fitchburg, CR-Providence, CR-Newburyport, CR-Worcester, CR-Fairmount, and so on
(13 lines at the time of writing). Ferries start with Boat-. Run mode routes for the current list with names,
colours and direction names; give routeTypes to get one mode's lines only.
Stop ids
Station ids start with place- (place-pktrm = Park Street, place-sstat = South Station, place-north = North
Station, place-portr = Porter). Subway platforms are four-digit numbers (70075), bus poles are shorter numbers
(1936), commuter rail platforms look like FR-0034-02. A station id in stopIds covers every platform under it, and
the rows then carry both the platform (stopId) and the station (parentStationId). Run mode stops with a route or
a coordinate to get the ids for your area.
Alert effects
DELAY, SHUTTLE, DETOUR, SUSPENSION, CANCELLATION, STATION_CLOSURE, STOP_CLOSURE, STOP_MOVE,
TRACK_CHANGE, SCHEDULE_CHANGE, SERVICE_CHANGE, STATION_ISSUE, ACCESS_ISSUE, ELEVATOR_CLOSURE,
ESCALATOR_CLOSURE, SNOW_ROUTE, NO_SERVICE, POLICY_CHANGE, NOTICE, SUMMARY.
Causes seen in the live feed include UNKNOWN_CAUSE, CONSTRUCTION, TRAFFIC, MAINTENANCE, WEATHER,
SEVERE_WEATHER, ACCIDENT, POLICE_ACTIVITY, MEDICAL_EMERGENCY. Lifecycles are NEW, ONGOING,
ONGOING_UPCOMING and UPCOMING. Severity runs 0–10: 1 is an information notice, 3–5 a real disruption, 7 and above a
line-wide event. Effects are filtered on the rows, so an effect nobody published today simply yields fewer rows.
Vehicle status values
INCOMING_AT, STOPPED_AT, IN_TRANSIT_TO; occupancy, when the agency reports it, is one of
MANY_SEATS_AVAILABLE, FEW_SEATS_AVAILABLE, STANDING_ROOM_ONLY, CRUSHED_STANDING_ROOM_ONLY, FULL,
NO_DATA_AVAILABLE.
Examples
Next Red Line trains at Park Street (a stop display)
{ "mode": "arrivals", "stopQuery": "Park Street", "routeIds": ["Red"], "maxItems": 15 }
Commuter rail departures from South Station (one mode at a multi-mode station)
{ "mode": "arrivals", "stopQuery": "South Station", "routeTypes": ["commuter-rail"], "maxItems": 20 }
Buses near a coordinate in downtown Boston
{ "mode": "arrivals", "latitude": 42.3601, "longitude": -71.0589, "radiusMeters": 500, "routeTypes": ["bus"], "maxItems": 20 }
Every disruption in effect right now
{ "mode": "alerts", "maxItems": 30 }
Lift and escalator outages for step-free routing
{ "mode": "alerts", "alertEffects": ["ELEVATOR_CLOSURE", "ESCALATOR_CLOSURE", "ACCESS_ISSUE", "STATION_ISSUE"], "maxItems": 20 }
Today's full commuter rail timetable at Porter
{ "mode": "schedule", "stopQuery": "Porter", "routeIds": ["CR-Fitchburg"], "maxItems": 40 }
The two dictionaries: Orange Line stops, then the commuter rail lines
{ "mode": "stops", "routeIds": ["Orange"], "maxItems": 30 }
{ "mode": "routes", "routeTypes": ["commuter-rail"], "maxItems": 25 }
Output
One real row from the cloud run Cu0CI8DmcadgyYTFB (input: the first example above), 27 September 2026, 18:20 Boston:
{"rowType": "arrival","found": true,"stopId": "70076","stopName": "Park Street","parentStationId": "place-pktrm","parentStationName": "Park Street","municipality": "Boston","distanceMeters": null,"latitude": 42.3563946,"longitude": -71.0624242,"routeId": "Red","routeName": "Red Line","routeShortName": null,"routeType": 1,"routeTypeName": "subway","routeColor": "DA291C","directionId": 1,"directionName": "North","headsign": "Alewife","tripId": "77916490","tripName": null,"vehicleId": "R-548BD870","vehicleLabel": "1920","arrivalTime": "2026-09-27T22:19:27.000Z","departureTime": "2026-09-27T22:20:34.000Z","time": "2026-09-27T22:20:34.000Z","timeLocal": "2026-09-27T18:20:34-04:00","scheduledTime": "2026-09-27T22:15:00.000Z","delaySeconds": 334,"serviceDate": "2026-09-27","minutesAway": 10,"predicted": true,"source": "prediction","status": null,"scheduleRelationship": null,"stopSequence": 150,"lastTrip": false,"url": "https://www.mbta.com/stops/place-pktrm","fetchedAt": "2026-09-27T22:10:11.262Z"}
An alert row from the same suite (run yM0xAAggcS8hPg1Dn), shortened:
{"rowType": "alert","alertId": "1035169","header": "Route 44 is experiencing delays of about 20 minutes due to traffic.","effect": "DELAY","cause": "TRAFFIC","severity": 5,"lifecycle": "NEW","serviceEffect": "Route 44 delay","active": true,"activePeriodStart": "2026-09-27T21:59:28.000Z","activePeriodEnd": "2026-09-28T00:10:00.000Z","updatedAt": "2026-09-27T21:59:28.000Z","routeIdsAffected": ["44"],"stopIdsAffected": [],"routeTypesAffected": ["bus"],"activities": ["BOARD","EXIT","RIDE"],"url": "https://www.mbta.com/schedules/44/alerts","fetchedAt": "2026-09-27T22:10:15.343Z"}
Fields
| Field | Type | Meaning |
|---|---|---|
rowType | string | arrival, schedule, alert, vehicle, stop, route or notFound |
found | boolean | false only on the single row a run writes when nothing matched |
stopId, stopName | string | The platform or stop the row happens at, and its name |
parentStationId, parentStationName | string | The station the platform belongs to |
municipality | string | Town the stop is in |
distanceMeters | number | Distance from your coordinate, measured by the actor (coordinate searches only) |
latitude, longitude | number | Stop position — for a vehicle row, the vehicle's own position |
routeId, routeName, routeShortName | string | The route: id used by the filters, long name, number |
routeType, routeTypeName | number, string | 0–4 and its name (subway, bus, …) |
routeColor | string | The line's colour as a hex triplet, for a map or a badge |
directionId, directionName | number, string | 0 or 1 and the name that route gives it (South, Inbound, Alewife) |
headsign | string | What the vehicle shows — the destination of this trip |
tripId, tripName | string | Trip identity; commuter rail also numbers its trains |
vehicleId, vehicleLabel | string | The vehicle serving the trip, when the agency tracks it |
arrivalTime, departureTime | string | ISO 8601 UTC; either may be empty at the first or last stop of a trip |
time | string | The time the row is sorted and counted by: departure if there is one, else arrival (UTC) |
timeLocal | string | The same moment with Boston's own offset, exactly as the agency publishes it |
scheduledTime | string | The timetable time for this trip and stop (UTC), when it exists |
delaySeconds | number | time − scheduledTime; positive = late, negative = early |
serviceDate | string | The service day the trip is counted under — a 00:40 train belongs to the previous date |
minutesAway | number | Whole minutes from the start of the run; empty for a timetable row already in the past |
predicted | boolean | true = live prediction, false = published timetable |
source | string | prediction or schedule |
status, scheduleRelationship | string | The agency's own words about the trip (ADDED, CANCELLED, …) when it says anything |
stopSequence | number | Position of this stop in the trip |
pickUp, dropOff, timepoint | boolean | Timetable rows: whether the trip boards, alights and keeps time here |
lastTrip | boolean | Prediction rows: the last trip of the night on that route |
alertId, header, shortHeader, description | string | Alert identity and the rider-facing text |
effect, cause, severity, lifecycle, serviceEffect, timeframe, banner | — | The alert classified: see Alert effects |
active, activePeriodStart, activePeriodEnd | boolean, string | Whether it is in effect now and the window (UTC) |
createdAt, updatedAt | string | When the agency published and last touched it (UTC) |
routeIdsAffected, stopIdsAffected, routeTypesAffected, activities | array | What the alert names as affected |
currentStatus, currentStopSequence, bearing, speed, occupancyStatus, revenue | — | Vehicle rows: what it is doing and where it points |
locationType, locationTypeName, platformName, platformCode, address, description | — | Stop rows: what kind of place it is |
wheelchairBoarding | number | 0 unknown, 1 accessible, 2 not accessible |
matchedRouteIds | array | Stop rows: which of your route filters this stop serves |
directionNames, directionDestinations, routeTextColor, routeDescription, fareClass, sortOrder | — | Route rows |
url | string | The agency page for the station, line or alert — for a human to open |
fetchedAt | string | When the run read the source, ISO 8601 UTC |
All timestamps are UTC (…Z) except timeLocal, which keeps Boston's offset on purpose. Dataset views:
Next arrivals, Service alerts, Live vehicles and Stops & routes. A run also writes a SUMMARY record to
the key-value store with the resolved stop, the filters, how many rows were live and how many came from the timetable,
the request count and any warnings.
If nothing matches — a name no stop carries, an alert filter nothing satisfies — the dataset gets exactly one row with
found: false and a message saying why, never a silent empty success.
Use it from code / agents
curl -X POST "https://api.apify.com/v2/acts/yadroo~mbta-arrivals/run-sync-get-dataset-items?token=$APIFY_TOKEN" \-H "Content-Type: application/json" \-d '{"mode":"arrivals","stopQuery":"Park Street","routeIds":["Red"],"maxItems":15}'
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: process.env.APIFY_TOKEN });const run = await client.actor('yadroo/mbta-arrivals').call({ mode: 'arrivals', stopQuery: 'South Station', routeTypes: ['commuter-rail'], maxItems: 20 });const { items } = await client.dataset(run.defaultDatasetId).listItems();
from apify_client import ApifyClientclient = ApifyClient(os.environ["APIFY_TOKEN"])run = client.actor("yadroo/mbta-arrivals").call(run_input={"mode": "alerts", "alertSeverityMin": 3, "maxItems": 30})items = client.dataset(run["defaultDatasetId"]).list_items().items
MCP: add https://mcp.apify.com to Claude / Cursor / any MCP client and call the yadroo/mbta-arrivals tool with the
same JSON input.
A display should poll a narrow window (minutesAhead 30, maxItems 10) rather than the whole day — it is faster and
cheaper. Trim the row to what you render with fields, e.g.
["stopName", "routeName", "headsign", "minutesAway", "predicted"]. For a watch, schedule the run and set onlyNew:
the keys are remembered per filter combination, so the same alert is never paid for twice.
Pricing
Pay per event: $0.001 per run start plus $0.002 per dataset row on the free tier. The row price falls with your
Apify plan — Bronze $0.0018, Silver $0.0016, Gold and above $0.0014 — while the start event stays $0.001. Every run is
charged the start event, including runs that write a single found: false row.
| Run | Rows | Free tier | Gold and above |
|---|---|---|---|
| Stop display, one direction | 10 | $0.001 + $0.020 = $0.021 | $0.001 + $0.014 = $0.015 |
Station board (the default maxItems) | 50 | $0.001 + $0.100 = $0.101 | $0.001 + $0.070 = $0.071 |
| Timetable export for a stop | 300 | $0.001 + $0.600 = $0.601 | $0.001 + $0.420 = $0.421 |
A typical run takes a few seconds at 256 MB, so platform compute is a fraction of a cent on top.
Limits & FAQ
- Rate limit. Anonymous callers get 20 requests a minute from the source, and the actor paces itself under that; a run that has to page the whole stop list can therefore take a minute. Paste your own free key from the agency into Your own API key and the limit becomes 1000 a minute. The key is sent as a header, never logged and never written to a row.
- Predictions exist only where the agency predicts. Realtime covers trips that are being tracked; a stop with no
vehicle on the way, or the middle of the night, has none. That is exactly why the timetable fallback exists and why
most rows outside rush hour say
source: "schedule". - Vehicle positions are empty overnight. Roughly 02:00–04:30 Boston time nothing is moving, so mode
vehicleslegitimately returns the onefound: falserow. Usearrivalsif you need something to show at that hour. - The published timetable covers roughly the current quarter. A
serviceDatefar in the past or future has no trips, and the run says so rather than returning an empty dataset. - Alert filters can legitimately return nothing. A quiet hour with
alertSeverityMin: 7is a network running well. - Ambiguous names. "Harvard" is a station and also several bus stops; the actor picks the station and writes the id
it used into the log and
SUMMARY. UsestopIdswhen you need to be certain. - Service days, not calendar days. A trip at 00:40 belongs to the previous service date and the timetable counts it
as 24:40.
serviceDatealways shows the agency's own day. - What is not here. No fare or trip-planning data, no historical performance archive, no vehicle occupancy where the agency does not publish it, and no pages from the agency's website — the actor reads only the public JSON API.
- Source and attribution. Data comes from the MBTA's public V3 API and is governed by the MassDOT Developers License Agreement; attribute the agency when you republish it, and do not present the output as an official service. Alert text is the agency's wording and is passed through unchanged.
Made by Yadroo. Sibling actors: open-meteo-weather, osm-geocode, nasa-eonet-events, public-holidays, rss-to-json.