Skip to content
OneBusAwayPublic
forked from jamespfennell/gtfs

About

Go library for parsing GTFS static and realtime feeds

Resources

Stars

4 stars

Watchers

0 watching

Forks

 
 

Latest commit

 

History

233 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

gtfs

This is a Go package for parsing GTFS static and realtime feeds.

The static parser is pretty straightforward and simply maps fields in the GTFS static CSV files to associated Go types.

For realtime parsing this package offers two options. Option 1 is to use the parser and types generated by the protobuf compiler which are available in the proto subpackage. This saves you from having to compile the protobufs yourself.

Option 2 is to use the package's custom parser, which takes the protobuf output from option 1, does a bunch of post-processing, and then outputs the result in standard (non-proto) Go types. This post-processing does useful things like:

  • Converts some data fields to more idiomatic Go types - for example, times are converted from int64 Unix timestamps to time.Time values.

  • Makes the representation of trips and vehicles more explicit. Each trip has a (nullable) pointer to the associated vehicle in the feed, if it exists, and vice-versa.

  • Implements business logic to map data in various GTFS realtime extensions to regular GTFS realtime fields.

Backwards compatibility warning: this package is under active development and backwards incompatible changes are frequently made. We're eventually planning to release a v1.0.0 version, and after that all changes will be backwards compatible and consistent with semantic versioning.

Go version support: this package supports the current Go release and the two releases before it. When a new Go release comes out, the minimum version in go.mod moves up to match. The minimum is currently Go 1.25.

Examples

Parse the GTFS static feed for the New York City Subway:

resp, _ := http.Get("http://web.mta.info/developers/data/nyct/subway/google_transit.zip")
b, _ := io.ReadAll(resp.Body)
staticData, _ := gtfs.ParseStatic(b, gtfs.ParseStaticOptions{})
fmt.Printf("The New York City subway has %d routes and %d stations\n", len(staticData.Routes), len(staticData.Stops))

Stop times take most of the memory of a parsed feed. If you don't need arrival and departure times or shapes, ask the parser to leave them out:

staticData, _ := gtfs.ParseStatic(b, gtfs.ParseStaticOptions{
	SkipShapes: true,
	// Keep only the stops each trip visits, in trip.Stops.
	// Use gtfs.StopTimesNone to skip stop_times.txt entirely.
	StopTimes: gtfs.StopTimesStopsOnly,
})

Parse the GTFS realtime feed for the San Francisco Bay Area BART:

resp, _ := http.Get("http://api.bart.gov/gtfsrt/tripupdate.aspx")
b, _ := io.ReadAll(resp.Body)
realtimeData, _ := gtfs.ParseRealtime(b, &gtfs.ParseRealtimeOptions{})
fmt.Printf("The SF BART currently has %d trains running or scheduled\n", len(realtimeData.Trips))

Supported GTFS Schedule files

The table lists every file in the GTFS Schedule reference as revised on April 27, 2026, and whether the parser reads it. Please report gaps in this repository's issues.

File Name Supported Required by Spec Notes
agency.txt ✅ Required
stops.txt ✅ Conditionally Required Optional only when locations.geojson is present
routes.txt ✅ Required
trips.txt ✅ Required
stop_times.txt ✅ Required
calendar.txt ✅ Conditionally Required Surfaced as a Service. GTFS requires it unless calendar_dates.txt defines every date of service; the library treats it as optional
calendar_dates.txt ✅ Conditionally Required Surfaced as part of a Service. Optional to the library
fare_attributes.txt ✅ Optional GTFS-Fares V1 (#30)
fare_rules.txt ✅ Optional GTFS-Fares V1 (#30)
timeframes.txt ✅ Optional GTFS-Fares V2 (#31)
rider_categories.txt ✅ Optional GTFS-Fares V2 (#31)
fare_media.txt ✅ Optional GTFS-Fares V2 (#31)
fare_products.txt ✅ Optional GTFS-Fares V2 (#31)
fare_leg_rules.txt ✅ Optional GTFS-Fares V2 (#31)
fare_leg_join_rules.txt ✅ Optional GTFS-Fares V2 (#31)
fare_transfer_rules.txt ✅ Optional GTFS-Fares V2 (#31)
areas.txt ✅ Optional
stop_areas.txt ✅ Optional
networks.txt ✅ Conditionally Forbidden
route_networks.txt ✅ Conditionally Forbidden
location_groups.txt ✅ Optional
shapes.txt ✅ Optional
frequencies.txt ✅ Optional
transfers.txt ✅ Optional #17, #18, #19; see upgrading
pathways.txt ✅ Optional
levels.txt ✅ Conditionally Required
location_group_stops.txt ✅ Optional
locations.geojson ✅ Optional Polygon and MultiPolygon only
booking_rules.txt ✅ Optional
translations.txt ✅ Optional
feed_info.txt ✅ Conditionally Required
attributions.txt ✅ Optional

GTFS-Flex and upgrading

Feeds that use GTFS-Flex now parse into Static.Locations, Static.LocationGroups and Static.BookingRules, and their stop_times.txt rows appear on trips alongside ordinary ones. Code written against earlier versions should note:

  • A row's GTFS-Flex columns are on ScheduledStopTime.Flex, a *FlexStopTime that is nil when the row sets none of them. Accessors such as Location() and PickupBookingRule() return nil in that case, so callers need not check Flex first.
  • ScheduledStopTime.Stop may be nil. A flex row references a Flex.Location or a Flex.LocationGroup instead; exactly one of Stop, Location and LocationGroup is set.
  • A windowed row (one with Flex.StartPickupDropOffWindow/EndPickupDropOffWindow) has zero ArrivalTime and DepartureTime and ExactTimes == false. Interpolation leaves these rows alone.
  • Code that expects fixed-stop, fixed-time service should skip flex rows with IsFlex(), or only windowed rows with IsWindowed().
  • A stop_times.txt row whose stop_id is not in stops.txt is still skipped, but it now produces a StopTimeInvalidReference warning in Static.Warnings. Earlier versions dropped such rows silently.
  • A row whose pickup_booking_rule_id or drop_off_booking_rule_id does not resolve is kept with a nil booking rule and a StopTimeInvalidReference warning.
  • A locations.geojson that cannot be read as a FeatureCollection is ignored with a LocationsFileInvalid warning; a single unusable Feature is skipped with a LocationInvalidGeometry warning. Neither stops the rest of the feed from parsing.

stops.txt and upgrading

The parser reads location_type as GTFS defines it. Users upgrading from earlier versions should note:

  • A stop with a parent station is no longer read as StopType_Platform. It is read as its location_type, usually StopType_Stop. To tell a platform from a standalone stop, check stop.Parent != nil. StopType_Platform is deprecated and never produced.
  • A location_type that is not empty or 0 through 4 adds a StopInvalidLocationType warning. The stop is still kept as StopType_Stop, as before. Whitespace around the value is ignored, so 1 now parses as StopType_Station.

transfers.txt and upgrading

  • transfer_type values 4 and 5 (linked trips) now parse as TransferType_InSeat and TransferType_InSeatNotAllowed. Earlier versions read them as TransferType_Recommended.
  • A row whose transfer_type is not empty or 0 through 5 is skipped with a TransferInvalidType warning. Earlier versions read it as TransferType_Recommended. Whitespace around the value is ignored, so 2 now parses as TransferType_RequiresTime.
  • For linked trips, from_stop_id and to_stop_id are optional, so Transfer.From and Transfer.To may be nil. Check them before use.
  • A linked-trip row without a known from_trip_id and to_trip_id is skipped with a TransferMissingTrip or TransferUnknownReference warning.
  • A row whose from_stop_id or to_stop_id is not a stop or station (location_type 0 or 1), such as an entrance, is skipped with a TransferStopNotAllowed warning. For linked trips, only a stop (location_type 0) is allowed.
  • A row that sets both a trip and a route on the same side (from_ or to_) is skipped with a TransferTripNotOnRoute warning when the trip runs on a different route.
  • A row whose from_stop_id equals its to_stop_id is kept. Earlier versions dropped it.
  • A row whose from_route_id, to_route_id, from_trip_id or to_trip_id is not empty and not defined in routes.txt or trips.txt is skipped with a TransferUnknownReference warning. Earlier versions kept the row with a nil route or trip, which made the rule apply to every route or trip at the stop.
  • A row whose from_stop_id or to_stop_id is not defined in stops.txt is skipped with a TransferUnknownReference warning. Earlier versions skipped it with only a log line.
  • A row of transfer_type empty or 0 to 3 with an empty from_stop_id or to_stop_id is skipped with a TransferMissingStop warning. If the file has no from_stop_id or to_stop_id column, the first such row adds one MissingColumns warning for the file. Earlier versions skipped these rows with only a log line.

Performance

The package is designed to be about as fast as possible without resorting to unreadable code. A profiler for the package is in the performance directory.

Static parser

The static parser has been performance optimized somewhat significantly. It takes about 3 seconds to parse the GTFS static data for the NYC buses (45 megabytes compressed, 360 megabytes uncompressed). The rough breakdown is:

  • 30% of the time unzipping the archives (using the archive/zip standard library package).

  • 40% of the time parsing the CSV files into strings (using the encoding/csv standard library package)

  • 30% of the time in this package performing the conversions from strings into types like time.Duration and linking related entities.

Realtime parser

TBD

Regenerating the protobuf code

The proto subpackage is generated from the .proto files next to it. After editing a .proto file, install buf (CI uses v1.73.0) and run:

buf generate

CI fails if the committed code is out of date.

About

Go library for parsing GTFS static and realtime feeds

Resources

Stars

4 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages