MBTA Boston: Bus & Train Arrivals, Timetables, Alerts avatar

MBTA Boston: Bus & Train Arrivals, Timetables, Alerts

Pricing

from $1.40 / 1,000 transit row returneds

Go to Apify Store
MBTA Boston: Bus & Train Arrivals, Timetables, Alerts

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

Samat Makatov

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

2 days ago

Last modified

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 arrivals for one station and one direction every minute and show minutesAway, headsign and 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-rail and you get the board a rail passenger wants, with serviceDate and 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 distanceMeters and the next departures from each.
  • Service-status dashboard: alerts writes 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_ISSUE and STATION_ISSUE to see which lifts are out before sending someone to a station.
  • Timetable export and analysis: schedule writes 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: stops and routes are 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.

FieldTypeDefaultAllowed values / notes
modestringarrivalsarrivals, schedule, alerts, vehicles, stops, routes — see Modes
stopQuerystring— (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
stopIdsstring[]—Source ids instead of a name: place-pktrm, place-sstat, 70075, 1936. A station id covers all its platforms
routeIdsstring[]—Red, Orange, Blue, Mattapan, Green-B…Green-E, bus numbers (1, 66, 111, SL1), commuter rail (CR-Fitchburg…). See Route ids
routeTypesstring[]— (all)light-rail, subway, commuter-rail, bus, ferry
latitudenumber—−90…90. Give it together with longitude instead of naming a stop
longitudenumber—−180…180 (Boston is about −71)
radiusMetersinteger60050–5000. Real metres: the actor converts to the degrees the source wants, then measures each stop itself
directionIdstringanyany, 0, 1. Each route names its own two directions — mode routes lists them
minutesAheadinteger1205–1440. Mode arrivals only: how far ahead departures may lie
serviceDatestring— (today in Boston)YYYY-MM-DD. Used by schedule, and by arrivals for another day
startTimestring— (whole day)HH:MM Boston local, mode schedule. Past midnight counts on: 25:00 is 1 a.m.
includeScheduledbooleantrueMode arrivals: fill every slot without a prediction from the timetable and compute delaySeconds where both exist
alertEffectsstring[]— (all)See Alert effects
alertSeverityMininteger— (all)0–10; 3 drops most information notices
alertLifecyclesstring[]— (all)NEW, ONGOING, ONGOING_UPCOMING, UPCOMING
activeOnlybooleantrueOnly alerts whose active period covers this moment
sinceHoursinteger— (no filter)1–720. Keep only alerts updated inside this many hours, counted in UTC
onlyNewbooleanfalseWrite only rows whose key was not seen in an earlier run of the same filters
maxItemsinteger501–2000
fieldsstring[]— (all)Keep only these fields, in this order
apiKeystring (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

modeWhat one row isNeedsTypical fields
arrivalsThe next departure of one trip from one stopa stop, a coordinate or a routeminutesAway, timeLocal, headsign, predicted, delaySeconds
scheduleOne scheduled trip at one stop on one service datea stop, a coordinate or a routeserviceDate, timeLocal, tripId, stopSequence, pickUp
alertsOne published disruptionnothingheader, effect, severity, routeIdsAffected, activePeriodStart
vehiclesOne vehicle in service with its positionnothing (filter by route)latitude, longitude, currentStatus, bearing
stopsOne stop or stationnothing (filter by route or coordinate)stopId, stopName, municipality, wheelchairBoarding
routesOne route or linenothingrouteId, 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 valuerouteTypeCovers
light-rail0Green Line branches, Mattapan trolley
subway1Red, Orange and Blue lines
commuter-rail2All CR-… lines
bus3Numbered bus routes and the Silver Line (SL1…)
ferry4Harbor 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

FieldTypeMeaning
rowTypestringarrival, schedule, alert, vehicle, stop, route or notFound
foundbooleanfalse only on the single row a run writes when nothing matched
stopId, stopNamestringThe platform or stop the row happens at, and its name
parentStationId, parentStationNamestringThe station the platform belongs to
municipalitystringTown the stop is in
distanceMetersnumberDistance from your coordinate, measured by the actor (coordinate searches only)
latitude, longitudenumberStop position — for a vehicle row, the vehicle's own position
routeId, routeName, routeShortNamestringThe route: id used by the filters, long name, number
routeType, routeTypeNamenumber, string0–4 and its name (subway, bus, …)
routeColorstringThe line's colour as a hex triplet, for a map or a badge
directionId, directionNamenumber, string0 or 1 and the name that route gives it (South, Inbound, Alewife)
headsignstringWhat the vehicle shows — the destination of this trip
tripId, tripNamestringTrip identity; commuter rail also numbers its trains
vehicleId, vehicleLabelstringThe vehicle serving the trip, when the agency tracks it
arrivalTime, departureTimestringISO 8601 UTC; either may be empty at the first or last stop of a trip
timestringThe time the row is sorted and counted by: departure if there is one, else arrival (UTC)
timeLocalstringThe same moment with Boston's own offset, exactly as the agency publishes it
scheduledTimestringThe timetable time for this trip and stop (UTC), when it exists
delaySecondsnumbertime − scheduledTime; positive = late, negative = early
serviceDatestringThe service day the trip is counted under — a 00:40 train belongs to the previous date
minutesAwaynumberWhole minutes from the start of the run; empty for a timetable row already in the past
predictedbooleantrue = live prediction, false = published timetable
sourcestringprediction or schedule
status, scheduleRelationshipstringThe agency's own words about the trip (ADDED, CANCELLED, …) when it says anything
stopSequencenumberPosition of this stop in the trip
pickUp, dropOff, timepointbooleanTimetable rows: whether the trip boards, alights and keeps time here
lastTripbooleanPrediction rows: the last trip of the night on that route
alertId, header, shortHeader, descriptionstringAlert identity and the rider-facing text
effect, cause, severity, lifecycle, serviceEffect, timeframe, banner—The alert classified: see Alert effects
active, activePeriodStart, activePeriodEndboolean, stringWhether it is in effect now and the window (UTC)
createdAt, updatedAtstringWhen the agency published and last touched it (UTC)
routeIdsAffected, stopIdsAffected, routeTypesAffected, activitiesarrayWhat 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
wheelchairBoardingnumber0 unknown, 1 accessible, 2 not accessible
matchedRouteIdsarrayStop rows: which of your route filters this stop serves
directionNames, directionDestinations, routeTextColor, routeDescription, fareClass, sortOrder—Route rows
urlstringThe agency page for the station, line or alert — for a human to open
fetchedAtstringWhen 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 ApifyClient
client = 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.

RunRowsFree tierGold and above
Stop display, one direction10$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 stop300$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 vehicles legitimately returns the one found: false row. Use arrivals if you need something to show at that hour.
  • The published timetable covers roughly the current quarter. A serviceDate far 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: 7 is 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. Use stopIds when 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. serviceDate always 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.