Best Practices for GTFS Publishers

Last Updated: April 21, 2023.

This document is for publishers of GTFS data and developers of real-time API services.

See the Help page for assistance using this Web service.

Words of Wisdom

Be Simple, Be Efficient, And Be Inviting...

-- The Webmaster

The purpose of specifications is to allow software to place reliable expectations on data so applications can be disigned for the best possible user experience. Nonsense is compliant usability is what matters. Be consistent with naming and identifiers across datasets and don't second guess design.

What's New

  • June 20, 2023. Advanced Guide for GTFS Extensions published and available below.
  • June 15, 2023. Support for the new GTFS Extensions (apart from completion of Flex v2, which is in progress). Fares v2, GTFS-Booking, GTFS-Eligibilities, MBTA-Facilities, and Deep Link Ticketing for fares, passes, media, rider categories, retail venders, customer service locations, and purchase deep links.
  • April 21, 2023. Support for multi-agency GTFS-R alert files.
  • April 21, 2023. Normal update of datasets with unrealistically long date ranges.
  • April 1, 2023. Minor fixes in display of agency information.
  • September 16, 2019. GTFS-R module update for improved performance.
  • April 29, 2019. System map images desktop and mobile optimized for the device.
  • April 11, 2019. Coercion to user friendly timetables where calendar.txt entries overlap or don't include all trips for the service period. To see the full effect of this change, include current schedules in your update. (Note: coercion is based on continuous service days and service_id.)
  • April 11, 2017. Separate display of arrival and departure times. The Arrival/Departure heading is tabbed and arrival and departure times are displayed separately if the time block is not equal. Specify arrival times for the new feature.
  • April 11, 2017. The parser is updated not to coerce a maximum of two trip_headsigns per schedule trip block when direction_id is used. The change avoids possibly misleading results when only the first trip_headsign is used. Use trip_headsign to model direction names identical to official publications. If trip_headsign changes along a trip, use stop_headsign to indicate the change. See GTFS Best Practices, Direction Names below.
  • February 7, 2017. See GTFS Best Practices, Other Best Practices #2 (for February 7, 2017).

Advanced Guide to GTFS Extensions (June 20, 2023)


Tired of oversimplified, incomplete, or ambiguous examples that leave important details unaswered?

Use this Guide to help get a clear understanding of fares, passes, media, rider categories, eligibilities, retail outlets, customer service locations, and deep purchase links.

Grow your data to grow your transit service.


Use the fare_attributes.txt and fare_rules.txt to publish your boarding fares. Fares v1 does a fine job, but not for publishing fare structures or additional fare options. To do that, use Fares v2.

Single Agency Wide Fares

To indicate single ride fares payable when boarding use the Fares v1 fare_attributes.txt and fare_rules.txt. The files are the legacy Fares v1. Fares v2 builds upon Fares v1.

After creating your v1 boarding fares, use Fares v2 to add rider categories, fare products, or eligibilities.

Fares fragmented by route, should create entries in fare_rules.txt that populate fare_id and route_id. The fare_id then references fare attributes in fare_attributes.txt.

Here's an example.


fare_id, price, currency_type, payment_method, transfers, transfer_duration
standard_fare,2.50, USD,0,,7200

The example shows a standard fare of $2.50 with unlimited transfers for 2 hours.

Fares Fragmented by Route

If fares differ among routes, use fare_attributes.txt and fare_rules.txt to identify a fare and associate it with a route_id.

First, add each fares that varies by route to the fare_attributes.txt file.


fare_id, price, currency_type, payment_method, transfers, transfer_duration
standard_fare,2.50, USD,0,,7200
brt_fare,3.25, USD,0,,7200
streetcar_fare,2.00, USD,1,,
ferry_fare,14.00, USD,1,,
rail_fare,24.00, USD,1,,

Now that the fare can be referenced by fare_id, you can key that fare_id to a route_id using entries in fare_rules.txt.


fare_id, route_id, origin_id, destination_id, contains_id
standard_fare, route_1,,,
brt_fare,3.25, brt_route_2,,,
streetcar_fare, streetcar,,,
ferry_fare, ferry,,,

The fare is now assigned to the corresponding route_id.

Origin / Destination Fare Tables

Fares v1 is still the best solution for origin/destination fares. Use the fare_attributes.txt and fare_rules.txt files to describe origin, destination, or contains (travel within) fare structure. Create one entry in each file for each combination of origin, destination, or contains travel within fares (include route_id if the fare is per route). Use stops.zone_id to identify your origin, destination, or contains (travel within) stops or stations. (Note: defining and assigning friendly names to zones is discussed later in Fare Zones.)


fare_id, price, currency_type, payment_method, transfers, transfer_duration
origin_id_1_to_2, 2.50,USD,0,,
origin_id_1_to_3, 2.75,USD,0,,

Now create the fare_rules.txt entries to associate the fare_id with the itinerary.


fare_id, route_id, origin_id, destination_id, contains_id
origin_id_1_to_2,, origin_zone_1,dest_zone_1,
origin_id_1_to_3,, origin_zone_1,dest_zone_3,

Use contains_id without origin_id or destination_id to express travel within a zone.

How to associate these identifiers with their well known public name is discussed later under Fare Zones


Use the fare_products.txt file to publish your agency-wide or per route passes with price, media, eligibility, activation, and other details.

Agency Wide Passes

To publish, create entries in fare_products.txt with the attributes of your passes. The specification supports most types of passes, whether per route, within a time frame, or for categories of riders.

Here's a typical example. The product name is unambiguous and attribute details are populated. Populating all attributes can grow trust in your data.


fare_product_id, fare_product_name, amount, currency, rider_category_id, fare_media_id, fare_media_group_id, bundle_amount, duration_start, duration_amount, duration_unit, duration_type, offset_amount, offset_unit, cap_required,service_id, timeframe_group_id, timeframe_type
local_1_day_fare,1-Day Pass (Local),5.50,USD,,,,,,1,3,1,,,0,,,
local_7_day_fare,7-Day Pass (Local),22.00,USD,,,,,,7,3,1,,,0,,,
local_31_day_fare,31-Day Pass (Local),84.00,USD,,,,,,31,3,1,,,0,,,
brt_7_day_fare,7-Day Pass (Bus Rapid Transit),24.00,USD,,,,,,7,3,1,,,0,,,
ferry_1_day_fare,1-Day Pass (Ferry),32.00,USD,,,,,,1,3,1,,,0,,,
intercity_1_day_fare,1-Day Pass (Intercity),12.00,USD,,,,,,1,3,1,,,0,,,

The fare_product.txt file contains all fare products referenced in the fileset apart from Fares v1 fare_attributes.txt or fare_rules.txt.

Read on for how to publish per route passes with rider categories and associated fare media.

Agency Passes Per Route

The fare_leg_rules.txt file is new to Fares v2. The file makes it possible to easily sum total fares for a journey with many trips. Simply search the file from_* and to_* identifiers.

The file is important because it keys together attributes of Fares v2, GTFS-Eligibilities, and some parts of Flex v2. Applications can query this file, to get fare and other information about all trips. The fare_leg_rules.txt is the only file for this purpose.

Another important attribute of Fares v2 is the addition of network_id to routes.txt and fare_leg_rules.txt. The network_id is an arbitrary identifier used to segment and classify GTFS attributes.


route_id, agency_id, route_short_name, route_long_name, route_desc, route_type, route_url, route_color, route_text_color, route_sort_order, network_id, continuous_pickup, continuous_drop_off
1,1,"short name...", "long name...","route description...",3,,,,,network_local_bus,,
BRT,1,"BRT", "Bus Rapid Transit","From... to...",3,,,,,network_brt,,
FRY,1,"F1", "Ferry name","From... to...",4,,,,,network_ferry,,
INTERCTY,1,"IC1...","Inter-city","From... to...",3,,,,,network_intercity,,

Note: The netword_id is required to associate one or more routes with a fare product, as shown above.

Now, add the entry in fare_leg_rules.txt to key network_id to fare_product_id.

The fare_leg_rules.txt does have a suprise, however. A row becomes unique with concantonation of several columns. Default values must be included when creating your network_id entry. Remember to ensure the default values for these attributes are the same or you'll face unexpected results.

The attributes that create a semantically unique row:

network_id, from_area_id, contains_area_set, to_area_id, is_symmetrical, from_timeframe_group_id, to_timeframe_group_id, min_distance, max_distance, distance_type, service_id

Add entries to fare_leg_rules.txt to key network_id with a fare_product_id or group of routes. You can then use the network_id to locate fare products for any route.


network_id, fare_product_id, fare_leg_name, from_area_id, contains_area_set, to_area_id, is_symmetrical, from_timeframe_group_id, to_timeframe_group_id, min_distance, max_distance, distance_type, service_id, leg_group_id, timeframe_type, eligibile_cap_id, filter_fare_product_id, rule_priority
network_local_bus, local_1_day_fare,,,,,,,,
network_local_bus, local_7_day_fare,,,,,,,,
network_local_bus, local_31_day_fare,,,,,,,,
network_brt, brt_7_day_fare,,,,,,,,
network_ferry, ferry_1_day_fare,,,,,,,,
network_intercity, intercity_1_day_fare,,,,,,,,

Each route can now be associated with one or more fare products.

Read on to learn how to add rider categories and media containers to your fare products.

Rider Categories

Use the rider_categories.txt and fare_rider_categories.txt files to create the rider categories of your fare structure. Associate the corresponding rider_category_id with a Fares v1 fare_rules.fare_id or Fares v2 fare_products.fare_product_id. Rider categories can be associated with any Fares v1 or Fares v2 fare products.

For Single Ride Fares (Fares v1)

The fare_rider_categories.txt file is an early follow-on to Fares v1. It keys the fare_rules.fare_id to a rider category. You can express fare structures using fare_id, rider_category_id, and price.


fare_id, rider_category_id, price
standard_fare, general_public, $2.50,
standard_fare, youth, $1.75,
standard_fare, senior, $1.50,
standard_fare, disabled, $1.250,

The second file rider_categories.txt defines the attributes of a rider_category_id. The column names of the file differ between the Fares v1 follow-on and Fares v2. Fares v2 uses rider_category_id, rider_category_name, min_age, max_age, eligibility_url in place of rider_category_id, and rider_category_description. If not publishing any Fares v2 content, Fares v1 format can be expected.

Note the backward compatibility, includes support for a single agency-wide fare structure. Simply include the price column as in fare_rider_categories.txt above.

In the example above, the standard_fare becomes a typical fare structure. With multiple entries in fare_rules.txt and fare_attributes.txt you can associate all Fares v1 fares with a rider category.

Fare Products (Fares v2)

Use the fare_products.rider_category_id to indicate a fare product is restricted to a rider category. Simply add the rider_category_id to the fare_products.txt entry. To do that, first define the rider category in rider_categories.txt, then update fare_products.txt.

The example below shows rider_categories using a table having all the GTFS extension columns.


rider_category_id, rider_category_name, min_age, max_age, eligibility_url, rider_category_desc, trip_purpose, trip_purpose_urn_set_id, eligibility, eligibility_urn_set_id, eligibility_authentication, retistration_desc, registration_url, registration_phone, appeals_url, appeal_phone, compliance_urn_set_id
general_public, 'General Public',,,,,,,,,,,,,,
senior, "Senior (Min 65)",65,,,,,,,,,,,,,,
youth, "Youth (3-17)",3,17,,,,,,,,,,,,,,

*Note: Columns differ in Fares v2 above from the Fares v1 follow-on. Use Fares v2 if publishing Fares v2 content.

The table above defines our new rider categories with each having a unique rider_category_id.

Note the rider_category_id has been added to several files by other GTFS extensions and is available is numerous GTFS contexts.


fare_product_id, fare_product_name, amount, currency, rider_category_id, fare_media_id, fare_media_group_id, bundle_amount, duration_start, duration_amount, duration_unit, duration_type, offset_amount, offset_unit, cap_required,service_id, timeframe_group_id, timeframe_type
local_1_day_fare,1-Day Pass (Local),5.50,USD, general_public,,,,,1,3,1,,,0,,,
local_1_day_fare_youth,1-Day Pass (Local)(Youth),2.75,USD, youth,,,,,1,3,1,,,0,,,
local_1_day_fare_senior,1-Day Pass (Local)(Senior),2.50,USD, senior,,,,,1,3,1,,,0,,,
local_1_day_fare_disabled,1-Day Pass (Local)(Disabled),2.25,USD, disabled,,,,,1,3,1,,,0,,,
local_7_day_fare,7-Day Pass (Local),22.00,USD, general_public,,,,,,7,3,1,,,0,,,

The fare products above are now published with the corresponding rider category restrictions.

Read on to see how to add fare media containers to your data for easier ticketing.


Use the fare_media.txt file to indicate which fare media can be used with which fare products. The file supports common attributes, such as initial and minimum purchase amounts, types of validation, activation details, and purchase URLs.

The fare_media.txt file associates your fare product with any type of fare media, such as paper ticket, plastic card, or software app. The media can also be used for onboard ticketing.

To get started, first define your fare media then associate it with fare products.

Create Fare Media

To get started with adding your fare media, first define your media in fare_media.txt. Then, set fare_products.fare_media_id to fare_products.fare_media_group_id indicate which fare media or fare media group can be used with the fare product.


fare_media_id, fare_media_name, fare_media_type, rider_category_id, fare_media_validation_method, fare_media_info_url, fare_media_url, minimum_initial_purchase, fare_media_price, currency
charlie, "Charlie Card", 2, general_public, 1,,, $20.00, $2.00,USD
charlieApp, "Charlie Purchase And Boarding App (QR)", 4, general_public, 3,,, $0.00, $0.00,USD
charlieSMS, "Charlie SMS Pass", 5, general_public, 4,,, $0.00, $0.00,USD
charlieEmail, "Charlie Email Pass", 6, general_public, 4,,, $0.00, $0.00,USD

The first entry above is for the Charlie Card with a minimum purchase of $20.00 and a $2.00 media card purchase price. The media can be used by the general public to tap and pay when boarding.

Media types include none (cash), paper ticket, transit card, debit/credit card, mobile app, token, SMS, or email. The validation methods include none, tap, swipe, scan (QR), or visual.

Always include the info and purchase URLs to leverage IT infrastructure to improve customer experience and sales.

Purchase Deep Link Extensions

The System appends query parameters to agency_fare_url, fare_media_url, fare_media_info_url and other GTFS URLs to support backend processing.

Parameters include:

  • intent with value [purchase|info].
  • GTFS fare_product_id, fare_media_id, fare_id, agency_id, route_id, trip_id, or stop_id if known from context.

If the request includes an intent=purchase assume the user intend to purchase and desires checkout with the fare product. Forward the user to your checkout flow with the associated product.

Configure your backend to complete the customer's intent with minimal effort and no confusion.

Add Media to Fare Products

Use the fare_media_id attribute in fare_products.txt to show which product is supported by which media. If the fare product is supported by more than one media, create a fare_media_group_id and pair it with each fare_media_id the product supports. Add the entries to fare_media_groupts.txt as shown below. The file is just a one to many list to associate fare products with one or more fare media.



Now, to mark all "by day" passes as supported by all the "Charlie" ticketing methods, add "passes_by_day" as the fare_media_group_id to the entries in fare_products.txt for each "by day" pass. The file now looks like the following:


fare_product_id, fare_product_name, amount, currency, rider_category_id, fare_media_id, fare_media_group_id, bundle_amount, duration_start, duration_amount, duration_unit, duration_type, offset_amount, offset_unit, cap_required,service_id, timeframe_group_id, timeframe_type
local_1_day_fare,1-Day Pass (Local),5.50,USD, general_public,,passes_by_day,,,1,3,1,,,0,,,
local_1_day_fare_youth,1-Day Pass (Local)(Youth),2.75,USD, youth,,passes_by_day,,,1,3,1,,,0,,,
local_1_day_fare_senior,1-Day Pass (Local)(Senior),2.50,USD, senior,,passes_by_day,,,1,3,1,,,0,,,
local_1_day_fare_disabled,1-Day Pass (Local)(Disabled),2.25,USD, disabled,,passes_by_day,,,1,3,1,,,0,,,
local_7_day_fare,7-Day Pass (Local),22.00,USD, general_public,,passes_by_day,,,,7,3,1,,,0,,,

Each fare product can now be displayed with an associated media product with price, activation, and other details.

Fare Containers (Deprecated)

The fare_containers.txt file is a follow-on to Fares v1 that is renamed fare_media.txt in Fares v2.

New implementations should always use fare_media.txt as adopted in the Fares v2 specification.

We support the fare_containers.txt method for backward compatibility.


fare_container_id, fare_container_name, minimum_initial_purchase, amount, currency, rider_category_id
charlie, "Charlie Card", $20.00, $2.00,USD, general_public,
charlieApp, "Charlie Purchase And Boarding App (QR)", $0.00, $0.00,USD, general_public
charlieSMS, "Charlie SMS Pass", $0.00, $0.00,USD, general_public
charlieEmail, "Charlie Email Pass", $0.00, $0.00,USD, general_public

The entries have the same result as fare_media.txt, less the Fares v2 features. Today, publish using the fare_media.txt format.

Fare Zones

Use the farezone_attriburtes.txt to name your Fares v1 zones. The file is an early follow-on to Fares v1 that keys stops.zone_id to a user friendly name.

The file was not included in Fares v2. The areas.txt and stop_areas.txt serve the same purpose but do not distinguish fare zones from service areas.

For Fares v1 content, use farezone_attributes.txt. The system will check farezone_attributes.txt, then areas.txt, then stop_areas.txt to resolve a zone_id to a friendly name.

The zone_id should match values in stops.zone_id.


southmetro, "Southern Metro"
peninsula, "Peninusula"
southcounty, "South County"
theisland, "The Island"

The zone_ids in your stops.txt file now have well known names.

Read on to see how to add service areas to your data.

Service Areas

Use the areas.txt file to display services areas to passengers. The file is an early follow-on to Fares v1 that is also included in Fares v2 except area_wkt becomes area_name. Fares v2 content should use area_name.

Use areas.txt to define user friendly names of zones or service areas.

The area_id must match the stops.zone_id when used to name fare zones using stops.txt.


southmetro, "Southern Metro"
peninsula, "Peninusula"
southcounty, "South County"
theisland, "The Island"

The entries above associate a friendly name with the area_ids to communicate fare or service areas.

Service Area From stop_ids

The stop_areas.txt file lets you define zone or service areas from groups of stops.stop_ids. The area_id associated with each stop_id refers to areas.area_id to resolve the area name.


soutmetro, 1000
soutmetro, 1001
peninsula, 2001
peninsula, 2002
southcounty, 3001
southcounty, 3002
theisland, 40001

Note that if the stop_id refers to a station with location_type=1 all stops that define the station as parent are included in the stop_id group.

The area_sets.txt file lets you create groups of areas to represent your service or fare zones. Note the area_ids must reference areas defined in areas.txt.

Using area sets lets you define groups of area_ids to name as a single service or fare zone.



The area_set_id can now be used to reference multiple area_ids in areas.txt to identify service regions.

Customer Service Locations

Use the facilities.txt file to show passengers your ticket sales and customer service locations. List your transit center addresses here.

The facilities.txt file is used by the Massachusetts Bay Transit Authority (MBTA). Names, addresses, and customer service hours are displayed as one line entries keyed by facility_type with value fare-media-assistance-facility. Use double quotes to escape entries having a comma.

The facilities.facility_type and facilities.facility_short_name or facilities.facility_long_name are required.


facility_id, facility_code, facility_class, facility_type, stop_id, facility_short_name, facility_long_name, facility_desc, facility_lat, facility_lon, wheelchair_facility
transit_center_downtown,,, fare-media-assistance-facility,,"Downtown Transit Center, 1 Main Street, City, State ZIP, Looby 8am - 8pm, Ticket Window 8am - 5pm",,,,,1
transit_center_uptown,,, fare-media-assistance-facility,,"Uptown Transit Center, 255 N. Main Street, City, State ZIP, Lobby 8am - 8pm, Ticket Window 8am - 5pm",,,,,1

The entries display facility_long_name or facility_short_name to users trying to buy tickets. Notice the wheelchair_facility attribute. Populate with '1' if the facility is wheelchair accessible or '2' otherwise.

Remember to use fare-media-assistance-facility as facility_type and include exact address with lobby and/or customer service hours.

You can see the MBTA table specification here.

Read on to learn how to publish your third party ticket vending retailers.


Use the facilities.txt file with facility_type set to ticket-vending-retailer to display a list of your retail vendors.

The file is the same one used for Customer Service Locations above. The value of facility-type is changed from fare-media-assistance-facility to ticket-vending-retailer to indicate a third party ticker vender.

The facilities.facility_type and facilities.facility_short_name or facilities.facility_long_name is required. The wheelchair_facililty, facility_lat, facility_lon attributes should be populated if correct values are known.


facility_id, facility_code, facility_class, facility_type, stop_id, facility_short_name, facility_long_name, facility_desc, facility_lat, facility_lon, wheelchair_facility
walgreens,,, ticket-vending-retailer,,"Downtown Walgreens, 53 Main Street, City, State ZIP and other participating locations.",,,,,1
cvs_pharmacy,,, ticket-vending-retailer,,"Uptown CVS/Pharamacy, 153 Broadway, City, State ZIP and other participating locations.",,,,,1
target_stores,,, ticket-vending-retailer,,"Target, 500 Market St., City, State ZIP and other participating locations.",,,,,1

The entries display retail outlets to customers seeking to purchase.

Use well known text with exact address as a single line. Double quote field values that include a comma.


Use the bookings.txt file to display the booking policy for your on demand transit service.

The booking rules specification includes most aspects of booking, including how far in advance, deadlines, phone numbers, information URLs, and instructions during travel.

The file is part of the Flex v2 specification and allows you to provide additional instruction when stop_times.pickup_type=2 or stop_times.drop_off_type=2. The pickup and drop off type indicates coordination with the driver is required.


booking_rule_id, booking_type, prior_notice_duration_min, prior_notice_duration_max, prior_notice_last_day, prior_notice_last_time, prior_notice_start_day, prior_notice_start_time, prior_notice_service_id, message, pickup_message, drop_off_message, phone_number, info_url, booking_url
1, 0,,,,,,,,,,,1-234-567-8910,
2, 1,10,2880,,,,,,"Listen for your requested drop off location",,,1-234-567-8910,
3, 2,,,1,17:00:00,7,08:00:00,,"Listen for your requested drop off location",,,1-234-567-8910,

The first entry indicates real-time booking with no deadline before travel--with phone number and information URL.

The second entry indicates up to the same day booking with advance notice no later than 10 minutes and no earlier than 48 hours before travel--with instructions, phone number, and information URLs.

The third entry indicates booking up to the day prior to travel. Booking is no later than 1 day before by 5PM and no earlier than 7 days at 8AM--with instructions, phone number, and information URLs.

The text of the Flex v2 specification can be found here.


Use the urns.txt and urn_set_ids.txt files with rider_categories.txt to display your rider eligibility restrictions.

urn is short for "uniform resource name". It's a simple way of creating one to many human readable lists.

GTFS-Eligibilities adds the rider_category_id to routes.txt, trips.txt, and stop_times to clearly display eligibility restrictions for routes, trips, and stops.

Supported restrictions include age, gender, disability, trip purpose, assistance levels required, registration, appeals, and custom (human readable words) purpose, eligibility, and compliance urns.

To apply restrictions to routes, trips, or stops add rider_category_id to the desired entries and set eligibility_urn_set_id, trip_purpose_urn_set_id or compliance_urn_set_id rider_categories.txt to the desired list.


rider_category_id, rider_category_name, min_age, max_age, eligibility_url, rider_category_desc, trip_purpose, trip_purpose_urn_set_id, eligibility, eligibility_urn_set_id, eligibility_authentication, retistration_desc, registration_url, registration_phone, appeals_url, appeal_phone, compliance_urn_set_id
general_public, 'General Public',,,,,,,,,,,,,,
senior, "Senior (Min 65)",65,,,,,,use_personal,,,,,,,,
youth, "Youth (3-17)",3,17,,,,,,,,,,,,,,

Notice the trip_purpose_urn_set_id points to a one to many entry in urn_set_ids.txt. The results is whatever humane readable list desirable. The same pattern of creating lists for different contexts applies to eligibility_urn_set_id and compliance_urn_set_id.


urn, urn_name, urn_desc, urn_url
doctor_appt,Medical Purposes,,
personal,Personal Use,,
disabled,Disabled, "Medicare Card holder",,
senior,Senior,"Age 67+",,
youth,Youth,"Age 8 to 17",,

The table above is a list of eligibility and trip purpose restrictions. This is the list of items grouped by identifier in urn_set_ids.txt shown below.

Group restrictions using a mnemonic friendly identifier for different categories of interest, such as for medical purposes or personal use. You can group any of the items in your urns.txt file for each category of interest.


urn_set_id, urn, reverse_test

Now you can select which group of items to apply to which category of interest for each rider. In this example, simply update trip_purose_set_id=[use_personal|use_medical] or eligibility_set_id=discounted to restrict the associated riders.

Purchase Deep Links

GTFS Basic Tips & Guidance (Updated April 11, 2019)

Here's a quick start to avoid common mistakes.

  1. Changing Data URL. Keep your latest data at the same HTTP(S) URL (using the same file name). For example, use and NOT Keeping the data URL the same means less confusion and is required for automatic updating. (Avoid FTP because many software libraries are not updated for current encryption.)
  2. Confusing Direction Names. The trip_headsign is used as the direction name. Too many can be confusing. Use direction names identical to official publications. If a trip headsign changes along a trip, use stop_headsign. It's designed for that purpose.
  3. Non-Unique route_ids Across Service Updates. The route_id is designated as the unique identifier for routes and should not change to denote a change in service. Publish data that supports tracking route schedules by keeping route_ids the same. If you have legacy software, update it.

    See Support Tracking Route Schedules (GTFS Best Practices) below for tips if you're locked into changing route_ids.

  4. GTFS Not a Snapshot of Current Service. Always maintaining a snapshot ensures a seamless transition between iterations regardless of how applications treat data caching. If you use GTFS-R, the static URL must always point to the data in use by the real-time system.
  5. Misconfigured Server. When the content-length and last-modified headers don't exist, are incorrect, or fluctuate, updates to the data are not processed timely and unnecessary downloads can result. When automated processes regenerate data, the resulting file size is unlikely to be size identical after compression. Avoid scheduled processes that regenerate the file even when the data is unchanged.
  6. Non-Compliant Data Files. Do not publish files with subfolders of datasets or partial agency data. Remember the purpose of the specification is to create data that can be fed directly into software. Your .zip file should contain at least specification files that completely describe each agency in agency.txt. Otherwise, your data is non-compliant and it's worth additional time to make it usable.
  7. Separate route_ids For Each Direction. Tempting simplicity but it disrupts user interfaces designed for GTFS. Use the route_id, route_short_name, and route_long_name to identify routes and trip_headsign to identify direction. User interfaces were designed expecting this pattern, disrupting it is counter productive.
  8. Stop Codes Improperly Added Or Excluded. If your system uses public facing stop codes, include stop_code in stops.txt even if they are used as stop_id and don't append them to stop_name. The stop_code flags whether your system uses public stop codes and triggers useful features. If you use them, they're worth including.
  9. Real-Time Vendor Without Public API. If you contract out for real-time services, be sure your agreement includes access to real-time data through a publicly available API, i.e. an Open API. Otherwise, real-time information will be unavailable in familiar software.

GTFS Best Practices (Updated April 11, 2019)

Reproduce Printed Schedules

Use the same calendars, routes, direction names, and times in your data as you use in print. Identical print and data publications promote trust and use of your data. For an unsure public, it's a relief to find identical information.

Arrival and Departure Times

If arrival and departure times differ, specify them both. The public is interested in arrivals and not just departures.

Don't leave departure_time or arrival_time empty or times will be interpolated and results may differ among publications.

Calendars and Timetables

Use the preferred calendaring method to best reproduce official publications. If schedules do not vary by day too much, use calendar.txt with modifications in calendar_dates.txt. If schedules are highly variable by day, use only calendar_dates.txt.

If you use calendar_dates.txt only, try to keep days of the week consistent within each service date range. The result is a user friendlier experience with fewer calender entries to choose from.

Direction Names

The trip_headsign is used as the direction name for a group of trips. Use the same names as official publications to create familiar direction names and time blocks.

Too many trip_headsigns can cause confusion because arrival and departure times get listed in separate time blocks. It's difficult to identify the next arrival or departure when times are listed under separate directions.

If trip headsigns vary along a trip, use stop_headsign. That's it's purpose.

We no longer coerce trip blocks into two trip_headsigns when direction_id is used. The change avoids possibly misleading results when only the first trip_headsign is used.

If you use direction_id, limit trip_headsigns to reflect official publications and use stop_headsign to reflect changes along a trip.

Use Timepoints

Make the major stops that appear in print publications an exact timepoint so the publication can be reproduced without a tomb of stops. See the reference publication for timepoint guidance.

Support Tracking Route Schedules

If you change your route_ids, your data does not support tracking route schedules. The little utility left is a small return on your investment and you could benefit greatly by keeping them the same.

Remember, your data describes things in the real world and if they haven't changed neither should their identifiers.

If route_ids change, we identify routes between updates by route_id patterns or the route name on a per dataset basis using settings we assign. For example, 2-BMT-sj2-1 may be recognized as 2-BMT-sj2-. The route short name or complete route name may also be used. The route_id with the latest service period is used to interoperate with other systems.

If you're using legacy software that changes route_ids, update it if you can. Otherwise, your data works as a common pattern and not as a rule (very bad). If you must change route_ids, pay special attention to keep route_short_name and route_long_name the same.

Other Best Practices

  1. Ensure the data URL includes the last-modified and content-length headers in GET and HEAD requests. One of these, preferrably both, is required for updates to be detected and posted automatically.

    Note: If you want to restict access, use HTTPS with basic authentication. This allows restricted access while supporting automatic updates.

  2. Avoid frequent, last minute updates to a new file by posting the update only when it's done. Up-to-the-second or to-the-minute update detection is not feasible due to a small number of misbehaving servers. This means the absolute latest file is not guaranteed to be processed if it differ by only a few minutes.

    During the update process never allow the last-modified date of the old file to be later than the new file. If this happens, the new file may be ignored.

    Finally, don't edit your file while it's at the static URL. If bad data gets processed, nonsense can become widespread and not automatically undoable.

  3. Keep the GTFS data file valid and completely describing each agency in agency.txt. This means your static URL should point to the merged data file. Include only specification files and not subfolders of datasets or partial agency descriptions. Remember the whole purpose of the GTFS specification is to publish data that can be fed directly into software. Anything else means your data may be unusable and the time you spent compiling it wasted.

    If the GTFS data file is invalid or at any time doesn't describe current service, nonsense may result and your data may be excluded from this Service.

  4. If your system uses public facing stop codes, always include them as stop_code and not stop_id or appended to stop_name. We have no way of knowing if stop_id is stop_code parsimonious or exactly which part of stop_name is really stop_code. The presense of stop_code activates many useful features and is worth including.
  5. Follow good naming practices so route_short_name, route_long_name, and trip_headsign match their well known equivalents. If routes are published by a short name and a long name, always include trip_headsign so the public can tell which direction you're talking about.
  6. If you are coordinating GTFS for a metro area, don't merge unnecessarily if the result is exceedingly large. Processing can be resource expensive and unnecessary if only a few updates are needed. A single page with a list of download links is resource efficient and update flexible.

GTFS-Realtime Best Practices (Updated April 11, 2019)

Best practices follow logically from the specification but may not be obvious. Many are critical to avoiding nonsense.

GTFS And GTFS-R Always In Sync

Remember GTFS and GTFS-R are designed to work together and should always be a snapshot of your service. All data used by GTFS-R must always be contained in the dataset pointed to at the static GTFS URL.


If your static GTFS URL at any time does not contain data used by GTFS-R, expect real-time to stop working.

Always post new data in a merged dataset at your static URL that uses the same identifiers as your GTFS-R. Please remember that maintaining a snapshot is the only way software can track your schedules and work as a rule (GTFS is a cacheless specification).

If you run GTFS-R and publish a non-merged dataset early, the update will not be processed until the earliest date in calendar.txt or calendar_dates.txt of the new dataset (this is necessary to maintain GTFS and GTFS-R synchronization).

We have updated our system to track changing route_ids, but any such system must rely on heuristics and will therefore not work as a rule. If you run GTFS-R, update your software to generate data that does not change route_ids (and is therefore suitable for general application use).

Please remember to support the General Transit Feed Specication (GTFS).

Model your data as intended by the specficiation to include all trips in each calendar.txt entry with any modifications in calendar_dates.txt. If trips vary day to day, use the calendar dates only method.

Using the specification as intended means less work and a better user experience. See the GTFS documentation for the recommended approach.

Update .pb Files In A Background Process

The .pb files should be updated at an interval by a background service or process that is fully decoupled from GET or POST requests for the .pb file.

Updating .pb files in a separate process eliminates any latency issues. The service scales with high volume since no additional execution time is required per request.

The smaller the update interval the better the user experience, especially for vehicle locations. Use the smallest interval possible given your hardware resources.

For example, updating vehicle positions every 3-5 seconds allows users to watch and move from a sheltered area to the stop just in time.

Populate All Attributes

In general, populate all attributes if data is available. Code paths increase exponentially with each optional attribute and GTFS-R has many. You can promote quality software and the best possible user experience by simply populating all attributes.

Specifically, TripDescriptor should always include trip_id where available. Otherwise, scheduled times and direction may not be discernable and user experience will suffer in the extreme. In VehiclePositions, include stop_id, current_stop_sequence, and current_status so stop predictions and trip inquiries can resolve to vehicles to the extent possible. Also, include bearing for visual directional cues.

Post TripUpdate for Vehicles

If you follow best practices, treat a vehicle assignment as a trip update so predictions can be coupled to the vehicle even when it is "on time". It's also a pleasure to see the system is up and working and the trip is "on time".

Don't Drop Past Events Too Soon

Don't drop past StopTimeEvents too soon. If so, users could see a trip status showing vehicle departure as scheduled 15 minutes ago and the vehicle just leaving the station.

Recognize 'Reply-To' Address

If your GTFS-R includes email auto-replies, be sure it responds to the 'Reply-To' address header if present. Anti-abuse configurations for email servers often reject email sent from a different domain, which means relayed requests can't be 'From' the user and responses must be sent to 'Reply-To'. If your system responds to the Reply-To address if present, mention it so feature support is known.

Publish Helpful Info

Publish other information that might be helpful to consuming applications, such as alert sources and types. It can be difficult to monitor publications to figure out what information is published where so it can be properly configured into a user interface. Also consider publishing information about the update frequency of vehicle locations and trip updates, non-abusive request frequencies, or any special or additional meaning attached to attributes.


Successful services always make sense. Fine tune your system. Watch and compare to confirm it never displays nonsense or contradicts itself. Final testing is where work pays of the most.

Don't rely on the public to discover an outage. By that time there may be thousands of frustrated users. Maintain a notification system so you can discover outages and promptly restore service.

GTFS Extensions (April 21, 2023

To Publish More Info...

To publish more than what's in the specification, maximize application support by doing it the same way others do.

At present, most efforts to go beyond the basic specification focus on:

  • Stop attributes
  • Fare info
  • Route types, and
  • On demand travel

Many GTFS publishing services implement GTFS-Fares, GTFS-Flex, GTFS-ContinousStops and other extensions for stops, pathways, and facilities. Do not break the specification by publishing separate GTFS files for your on demand service. The specification is updated to describe on demand service--use it to your advantage. You can see the new files and attributes and official documentation for GTFS-Flex.

We've attempted to fully support these extensions but new ones arise. As of now, GTFS-Flex and GTFS-Fares have partial support with complete support in progress. Periodally we evaluate support of all bona fide GTFS extensions. If you've implemented a non-standard solution, you are encouraged to migrate to these new popular formats to describe your services.

The dominate classification for additional route types is the Hierarchical Vehicle Type (HVT) codes from the European TPEG. You can see a list of the codes and their support by Google here. However, new route_types will create backward incompatibilies into the future. Future support at RideSchedules depends on the extent to which the codes are incoporated into publications.

Example: Senior, Youth, Disabled Fares

To include fares for different types of passengers, include a rider_categories.txt file and fare_rider_categories.txt in addition to the fare attributes and fare rules.

The rider_categories.txt files assigns a unique integer to a string public name.


The fare_rider_categories.txt file keys the rider_category_id to a fare_id and a separate price.


The above example results in:

$1.00 upon boarding (includes 1 transfer), Senior $0.35, Disabled $0.35, and K-12 $0.70.

Note: Always specify route_id in fare_rules.txt so the fare can be associated with a route. At present, that means an entry in fare_rules.txt for each route_id.

Other Files and Attributes of Note

Disambiguting the location of a stop can be done with the cardinal_direction and cardinal_position fields in stop_attributes.txt or by using the vender specific method of direction and position in stops.txt. You can create results such as: "Stop located Fareside traffic direction South". The stop_address in stops.txt and stop_city in stop_attributes.txt is also available.

You can specify the name of fare zones in a way familiar to the public by including farezone_attributes.txt with zone_id keyed to a zone_name.

Passengers using GTFS frequently need non-GTFS information, such as system maps, rider guides, fare brochures, etc.

Note: As of this writing, the mobile segment currently has insufficient resources for system maps created from GTFS data but that's changing slowly.

Follow a few simple rules to keep your resources visible and up to date.

  • Keep URLs to the up to date resources the same. If you publish early, use an "Upcoming" or "Planned" URL (which can also be kept the same) before the effective date.
  • Optimize your resources. Some freeware reduces the size of .png and .jpg images significantly (jpegoptim and optipng are popular). If you publish PDFs, consider the optimize features in your editing software.

Other Real-Time Design Suggestions


    GTFS-R is recommended as the emerging standard,
    but for RESTful services, here's some
    sample API requests using URL naming schemes
    with JSON/XML response

/* stop arrivals/departures */[route_id|route_short_name]&stop=[stop_id|stop_code]&response=json|xml

/* trip updates */[trip_id]&stop=[stop_id|stop_code|stop_sequence]&response=json|xml

/* alerts */[agency_name|agency_id]&route=[route_id|route_short_name]&stop=[stop_id|stop_code]&response=json|xml

/* news */[agency_name|agency_id]&route=[route_id|route_short_name]&stop=[stop_id|stop_code]&response=json|xml

/* vehicle locations */[agency_id|agency_name]&route=[route_id|route_short_name]&direction=[direction_id|trip_headsign]&stop=[stop_id|stop_code|stop_sequence]&trip=[trip_id]&vehicle=[vehicle_id]&response=json|xml


Aside from GTFS-Realtime, there is generally no uniform request or response format for stop predictions and vehicle locations. SIRI has recently enjoyed implementation by a few major providers but most often developers still need to write separate code to interact with the service. If your system is in development, here's some time saving suggestions.

  1. Use GTFS-Realtime Or RESTful Services. GTFS-Realtime is recommended and appearing for some major transit providers, though it can be difficult with non-mainstream dependencies and low level byte manipulation (though this is getting better). If a standard real-time request response format that is write once for all from the developer's perspective ever comes into being this is likely it. That means more bang for your work.

    XML and JSON are simple, mainstream, and reliable APIs, but not write once for all when it comes to predictions and vehicle locations. SIRI ("Service Interface for Real Time Information") is a template adopted by the European Commission on Standardization that offers at least the possibility of write once for all. However, in practice most implementations require custom code to interact with the service. You can learn more about SIRI from the official home page at

    If you decide to develop your own API, be sure to include the DTD or Schema in the API documentation. Example responses are not enough. Developers need to know whether attributes are optional or required and whether they appear once or repeated as well as the data type and how errors are handled. Without this information, reponses cannot be parsed reliably. Do not use the HTTP layer to convey meaning within your API or software handling HTTP communications may not interact with your service as expected.

  2. Real-Time - GTFS Compatibility. Routes should map to route_id, stops to stop_id or stop_code and directions to direction_id or trip_headsign. If your real-time identifiers cannot be resolved from your GTFS data, your service will very likely not be implemented.
  3. Model Requests. Prediction requests should support per stop_id or stop_code and narrowing by route and direction. If you support vehicle locations, also by vehicle_id. Vehicle location requests should support narrowing by route, direction, and vehicle id with per stop inquiries handled by coordinates included in prediction responses. Alerts, notifications, advisories, news, etc. should support requests per stop, route, route type, and agency, if appropriate. Always support narrowing where possible for convenience and when data volumn is high.
  4. Model Responses. Minutes until arrival or departure is the most important response attribute for prediction requests. Epoch time in the local time zone as the date time format is the least ambiguous. Prediction responses should include attributes for route_id, trip_headsign, stop_id, stop_code, or vehicle_id and always vehicle coordinates if available. Responses to prediction requests based on vehicle id should return data for all future stops on the trip.

    Vehicle location responses should identify each vehicle by vehicle id, route, direction, and trip_id and include status and next stop information (early/late next stop X mins) and all available positional information, such as lat lons, speed, heading, etc.

  5. Include Human Readable Attribute Values Human readable values allow user request completion without additional queries or storage and can create a significant performance improvement.

    For example, if you specify next_stop_sequence or next_stop_id include next_stop_name. If you specify trip_id or direction_id, include route_short_name and trip_headsign.

  6. Email Auto-Replies: Popular anti-abuse configurations reject email messages not sent from the sender's domain. Auto-reply real-time requests must be sent from the senders domain and answered using the 'Reply To' email address. Be sure your system replies to 'Reply-To' if included in the request. Otherwise, the feature will not work and it will be turned off.
  7. Trip Updates. Trip updates give status information about an ongoing trip and may be particularly useful for long distance rail operators. We have seen few XML/JSON implementations, however. If you wish to support trip updates rather than predictions, GTFS-Realtime is the best choice. If you implement JSON/XML trip updates, requests must recognize the trip_id. Responses should include the same information as in the model vehicle locations response.
  8. RSS Advisories, News, Notifications, Etc. RSS (Really Simple Syndication) is convenient and effective for advisories, notifications, news, and other variable, non-GTFS information of interest to passengers. It enjoys wide-spread support in end user software and development APIs. We support immediate activation of RSS data sources. Use your GTFS identifiers with URL naming schemes to support convenient per route and per stop passenger inquiries. Of course, all requests should recognize identifiers in your GTFS data.

    Be sure your RSS files are indeed RSS! Frequently, software inserts custom tags and outputs non-compliant files. Select strict RSS as the output format. If your file is not valid RSS, do not publish it as such or it may be read incorrectly or not at all.

  9. X. X is often used as a convenient up to the minute alert and advisory service. It offers auto-updating lists immediately viewable on mobile devices and desktops with no quotas for developers. However, lists are chronological and can become confusing with size. Consider using X for up to the minute, late-breaking information and RSS for more item based status information. Together, they offer an inexpensive alternative to an API.

Known Issues (Updated April 21, 2023)

All behavior is as designed to the extent known.


  • Multi-agency GTFS-R Alerts without a limiting agency query parameter is now supported. If you notice your file not properly displaying, notify the Webmaster.

  • Static GTFS with date ranges covering many decades now auto-update normally. The displayed date ranges will roll a sensible date range without re-fetch until the data file expiration date.

submit 0 510afdd1bceff6a31b6f3b77967c26d6 d891fe2b62c0ed2bd9940011433064d9 en-us hlp_


We are happy to hear from you!

  • Was this information helpful?
  • ▲ Some input is required.

  • ▲ Invalid Reply-To Email address.

Submit Cancel

Please remember to include your email address if a reply is needed.