Best Practices
When working with the V3 API, we recommend several best practices.
Skip to any section:
V3 API Documentation
Read the latest endpoint and resource documentation about available fields, filters, and includes.
Rate Limiting
Use of the V3 API is subject to certain rate limits:
- Requests made without an API key are subject to a limit of 20 requests per minute.
- Requests made with a valid API key are limited to a default of 1,000 requests per minute. In the V3 API Portal, you can view your current rate limit and, if needed, request a rate limit increase.
See the sections on Caching and Sparse Fieldsets for ways to fetch less data and avoid hitting rate limits.
All V3 API responses include HTTP headers, which show your rate limit status:
Header | Description | ||
---|---|---|---|
Header | x-ratelimit-limit | Description | The maximum number of requests you’re allowed to make per time window |
Header | x-ratelimit-remaining | Description | The number of requests remaining in the current time window |
Header | x-ratelimit-reset | Description | The time at which the current rate limit time window ends in UTC epoch seconds |
Request a V3 API Key
Requests made with a valid API key are limited to a default of 1,000 requests per minute.
Caching
The V3 API supports caching via the `Last-Modified`
response and `If-Modified-Since`
request headers. Each response contains a `Last-Modified`
header, specifying the last time that data was updated. If, on subsequent requests, your client passes an `If-Modified-Since`
header with that value and the data hasn't changed, you'll receive a 304 Not Modified status code. This cached response won't count against your API key limit. Another advantage of using this header is that you won't receive an update if you hit a server that was updated in the past.
Note: This only works for the root data type; included data isn't currently tracked by the `Last-Modified`
header.
Streaming
Often when working with predictions or other realtime data, it’s desirable to get new data as it changes. Rather than using polling for this purpose, the V3 API supports streaming new data in real time.
Compression
The V3 API supports gzip compression via the `Accept-Encoding`
header. If your HTTP client doesn't do this transparently, you can pass `Accept-Encoding: gzip`
as a request header, and the response will be compressed. This can result in ~10x reduction in data size.
Example: The full list of routes as of October 2024 is reduced from 86 KB without compression to 7.1 KB with compression.
Sparse Fieldsets
Each type of data supports a query parameter `fields[type]`
, which limits the returned attributes (reference).
Example: https://api-v3.mbta.com/routes/?fields%5Broute%5D=short_name,long_name returns only the names for the routes. If you know what fields you need, this is another good way to reduce the amount of data you receive. This also works for included data types: https://api-v3.mbta.com/route_patterns?filter[route]=CR-Providence&include=representative_trip&fields[trip]=headsign .
Nested Includes
JSON:API can include
multiple levels of relationships by connecting the relationship names with dots (reference).
Example: The response for https://api-v3.mbta.com/routes/Red?include=route_patterns.representative_trip.shape will include the requested route, its route patterns, the representative trips of those patterns, and the shapes of those trips.
This works to any depth of relationships, and it can save you the need to make N+1 requests in many situations.
Updates
The realtime data can update very frequently, even using `If-Modified-Since`
headers to avoid stale data. Predictions update every ~12 seconds, and vehicles update more than once per second. You may want to include some logic in your clients to prevent relative times from bouncing (say between "3 minutes away" and "4 minutes away") if that would be confusing to your users. If you're displaying predictions at that level of granularity, you can also reduce the frequency of updates accordingly.
Alerts
Displaying alerts is one of the trickiest features to get correct. Service disruptions can affect large sets of riders, and you as the client developer are in the best position to know where they might be trying to ride.
If you don’t know where along a route a rider is traveling, filtering only by their origin stop (see example) is likely to miss relevant alerts at other stops along the line. In that case, you should also query for alerts on the entire route (see example) and incorporate them.
The same recommendation applies to implementations where the filtering of the alerts to show happens on the client side. You want to add the alerts where any informed entity includes the relevant route. Even if an alert is about a specific stop, it may affect your riders.
When you do have the information about the rider's entire trip (where they are leaving from and where they are going), you still should consider the alerts on the stops along their route. For example, there might be an alert about shuttles replacing regular service somewhere in the middle of their trip. You can query for multiple stops by comma-separating them in the filter (see example).
You can also use the activity field of the Informed Entity to better filter to those alerts.
- For stops where the rider boards a vehicle (even transfers), filter for the "BOARD" activity.
- For stops where the rider gets off a vehicle (even transfers), filter for the "EXIT" activity.
- For stops the rider travels through, filter for the "RIDE" activity.
- If the user requests accessible trips, filter for the "USING_WHEELCHAIR" activity.
The documentation for AlertResource has more detail on other types of activities.
Alerts with a severity of 1 are informational. They can receive a more muted visual treatment, if possible, compared with more severe alerts.
Predictions
Displaying realtime predictions is a popular use of the API, and there are some complexities involved in interpreting and displaying this data correctly.
Predictions may have an associated `schedule`
, which represents the stop as it was originally scheduled to occur. The prediction can be thought of as a realtime update to the schedule; the `schedule_relationship`
attribute indicates the nature of the update. Its possible values, and their meanings, are listed in the PredictionResource section of the API documentation.
Note: Predictions may not have a `schedule`
, indicating we know a vehicle is running on the route but cannot match it to a scheduled trip.
In general, schedules should be ignored when displaying realtime predictions for rapid transit routes (`route_type`
0 and 1). Schedules are useful for time periods beyond when we have realtime predictions available.
Predictions and schedules may include an `arrival_time`
, a `departure_time`
, both, or (only for predictions) neither. These are the rules for when a time is present:
- The departure time is present if, and only if, it's possible for riders to board the associated
`vehicle`
at the associated`stop`
. A null departure time is typically seen at the last stop on a trip. - The arrival time is present if, and only if, it's possible for riders to alight from the associated
`vehicle`
at the associated`stop`
. A null arrival time is typically seen at the first stop on a trip. - Commuter Rail predictions with neither a departure time nor arrival time often have a status field with their boarding status. Depending on your use case, these predictions may be discarded, or the status may be displayed verbatim to the user (see the “Status Field” section below).
- Predictions with no arrival time, departure time, nor status indicate the vehicle will not make the scheduled stop. The
`schedule_relationship`
field may explain why.
In general, we recommend not displaying predictions with null departure times, since riders will not be able to board the vehicle. If both arrival and departure time are present, the arrival time is likely to be more useful to riders.
Predictions may have a free-text `status`
field, which is synonymous with the GTFS Realtime`boarding_status`
field. If present, we recommend displaying this directly to riders in place of the arrival or departure time, since it may indicate a situation that affects the accuracy of the prediction (for example, a train stopped unexpectedly between stations). Status strings are English-language and have no maximum length, though they are typically kept short for display on signage.
The most popular way to display prediction data for a given stop is a countdown of how many minutes away the next few vehicles are, with destinations (for example, "Ashmont" or "Braintree" for the Red Line). These are the rules we follow on the MBTA website and station signs.
The destination component is the `headsign`
attribute of the `trip`
associated with the `vehicle` of the prediction. The countdown component is determined by following these rules in order, stopping at the first applicable “display” or “do not display” instruction:
- If
`status`
is non-null:- Display this value as-is.
- If
`departure_time`
is null:- Do not display this prediction, since riders won’t be able to board the vehicle.
- Calculate the number of seconds until the vehicle reaches the stop, by subtracting the current time from the arrival time (if available) or the departure time (if not); call this value "seconds."
- If seconds < 0:
- Do not display this prediction, since the vehicle has already left the stop.
- If seconds <= 90, and the
`status`
of the associated`vehicle`
is "STOPPED_AT", and the vehicle’s`stop`
is the same as the prediction’s`stop`
:- Display "Boarding" (abbreviated as "BRD").
- If seconds is <= 30:
- Display "Arriving" (abbreviated as "ARR").
- If seconds is <= 60:
- Display "Approaching" (abbreviated as "1 min").
- Round the seconds value to the nearest whole number of minutes, rounding up if exactly in-between; call this value "minutes."
- If minutes > 20:
- Display “20+ minutes” (abbreviated as “20+ min”).
- Display the number of minutes followed by "minutes" (abbreviated as "min"). For example:
- Up to 89 seconds: "1 minute" or "1 min"
- 90 to 149 seconds: "2 minutes" or "2 min"
- 150 to 209 seconds: "3 minutes" or "3 min"
V3 API Documentation
Read the latest endpoint and resource documentation about available fields, filters, and includes.
Request a V3 API Key
Requests made with a valid API key are limited to a default of 1,000 requests per minute.