Merged and Multi-Property GTFS Feeds

Applies to: Avail GTFS static feeds and GTFS-Realtime feeds
Audience: Transit agencies, trip planners and other GTFS data consumers

An Avail merged feed puts more than one set of schedule data into a single GTFS feed. This article explains how to recognize a merged feed, how its IDs are structured, how schedule versions hand over on the calendar, and how to match GTFS-Realtime data to the static feed.

In this article

Overview

A merged feed combines more than one set of schedule data, and a suffix on each affected ID tells the sets apart. There are two kinds of merge, and one feed can use both.

Kind What's combined How the sets relate in time ID suffix
Schedule change The current schedule and the next published schedule One after the other, with no shared dates -v<version>
Multi-agency Schedules for separate parts of one operation, such as different yards. Each part is published as its own agency At the same time, on the same dates -v<version>-a<agency_id>

With a schedule-change merge, the new schedule is in the feed before it starts. Trip planners can show it ahead of time, and there's no gap when it takes over. With a multi-agency merge, riders get one feed for the whole operation.

How to recognize a merged feed

Look at trip_id in trips.txt. If the IDs end in -v followed by a version, the feed is merged. If they also end in -a followed by a number, it's a multi-agency feed.

Check Merged feed Non-merged feed
trip_id t140-b1-sl1C-v64 or t266-b162B2-sl2-v4A-a1 t1A9-b4E85-sl2
service_id 28-v64, 3-v50-a2 2, 3
shape_id 27337_shp-v64 3800_shp
Rows in agency.txt One per agency in the merge One

ID structure

IDs that belong to one schedule version end in -v<version>. In a multi-agency feed, -a<agency_id> comes after that. Treat every ID as an opaque string: the suffix is part of the ID.

The version is hexadecimal. It goes up by one with each schedule publish, so v9 is followed by vA through vF, then v10. Read it as text or as base 16. A script that only accepts digits will miss a version like vC. In a multi-agency feed, each agency has its own version number.

Field Schedule-change merge Multi-agency merge
trip_id, service_id, block_id, shape_id -v<version> -v<version>-a<agency_id>
service_id in calendar.txt and calendar_dates.txt -v<version> -v<version>-a<agency_id>
stop_id, parent_station No suffix; shared by all versions -a<agency_id>; each agency has its own copy
route_id No suffix; shared by all versions Unique to one agency
agency.txt One row One row per agency
feed_info.txt One row covering every version One row for the whole feed

Same base ID, different trip. Apart from the suffix, a trip ID often stays the same from one version to the next, for example t219-bD-sl1C-v63 and t219-bD-sl1C-v64. These are different trips. A system that strips or ignores the suffix will merge them by mistake.

Stops in multi-agency feeds. A stop that two agencies both serve appears once for each agency. The copies have the same stop code, name and location but different IDs, such as 1005-a1 and 1005-a2. A trip planner may show both.

Calendar

In a schedule-change merge, the two versions never share a service date. The current version covers every date before the new schedule's start date, and the new version covers the start date onward. In a multi-agency merge, the agencies' schedules run side by side on the same dates.

merged-gtfs-calendar-changeover.png

Service days past midnight. Avail assigns each trip to a service day using the agency's own service-day boundary, not the calendar day. A trip that starts late and runs past midnight belongs to the service date it started on. At a changeover, late-night trips from the old version's last day keep the old version's IDs until they finish.

Service is defined either in calendar_dates.txt alone, which lists every service date, or in calendar.txt with exceptions in calendar_dates.txt. Which one a feed uses varies by agency. Both are valid GTFS.

GTFS-Realtime

Avail's realtime feeds use the same suffixed IDs as the static feed, so they match it directly. Join realtime to static on the full ID, suffix included.

  • Trip IDs: trip.trip_id in TripUpdates and VehiclePositions carries the full suffix of the version running that service day, for example t219-bD-sl1C-v64 or t2E4-b277C-sl3-v50-a2.
  • Stop IDs: in a multi-agency feed, stop references use the agency's stop ID, such as 750-a2.
  • Service day: start_date in TripUpdates is the service day, not the calendar date. It follows the agency's service-day boundary, so overnight trips keep their original date.
  • Vehicles: in a multi-agency feed, the vehicle ID carries the agency suffix (1800-a2) and the label riders see doesn't (1800).
  • Alerts: alerts target routes or a whole agency. In a multi-agency feed, each alert names the agency_id it applies to.

Each feed is served as protobuf from one endpoint. FeedLabel names the feed and FeedType names the message type. A multi-agency feed uses one label for all its agencies.

Feed type URL
Alerts http://gtfs-rt.myavail.cloud/GtfsProtoBuf?FeedLabel=<FeedLabel>&FeedType=Alerts
TripUpdates http://gtfs-rt.myavail.cloud/GtfsProtoBuf?FeedLabel=<FeedLabel>&FeedType=TripUpdates
VehiclePositions http://gtfs-rt.myavail.cloud/GtfsProtoBuf?FeedLabel=<FeedLabel>&FeedType=VehiclePositions

Guidance for consumers

  1. Keep IDs exactly as published. Never strip or parse the -v and -a suffixes.
  2. Filter trips by service date, using calendar.txt and calendar_dates.txt. A merged feed holds trips that don't run on a given day.
  3. Match realtime to static on the full trip_id and stop_id, and use start_date for the service day.
  4. If you check versions in code, accept hexadecimal (0–9 and A–F).

FAQ

Question Answer
Why does every trip seem to appear twice? In a schedule-change merge, the current and the future version of a trip are both in the feed. Each runs only on its own dates. Filter by service date.
Why is the feed larger than before? It holds more than one schedule set, and shapes are included once for each version.
Why is the new schedule already in the feed? That's the point of a schedule-change merge. Trip planners can show the new schedule before it starts.
A validator flags trips that look like duplicates. The IDs are different because of the suffix, and the versions don't share dates. They aren't duplicates.
Why does the same stop appear twice? In a multi-agency feed, each agency publishes its own copy of a stop they share. The copies have different IDs.
Why are there several agencies with the same name? Each part of the operation is published as its own agency in the merged feed.
Why is there no calendar.txt? Some feeds list every service date in calendar_dates.txt instead. Both forms are valid GTFS.
Was this article helpful?

Articles in this section

New to the Help Center?
Review the Help Center guide
Help Center Feedback
Have a suggestion for new content or how we can improve the Help Center? Let us know!