GetBulkTripOptions method

Compared to the GetTripOptions method, the GetBulkTripOptions method has the following advantages:

  • Allows for more efficient population of the Google cache, by using fewer requests and by better aligning with upstream APIs from operators which may be used by partners to acquire the data themselves.
  • Makes it easier for partners with a predetermined set of available trips to provide their data without detailed itinerary inspection.
  • For more sophisticated partners, still provides them with a list of known itineraries to maximize matching of partner inventory and itineraries produced by the Google routing engine, thus showing partner prices on as many itineraries as possible.
  • Allows to ingest itineraries that Google is not yet serving, in order to potentially adjust the Google routing engine to better match partner inventory in the future.

GetBulkTripOptionsRequest

Proto Definition

// Request sent to partners.
message GetBulkTripOptionsRequest {
  // The MarketDates for which to return results.
  // Initially, only a single value will be supplied, representing one-way
  // trips.
  repeated MarketDate market_dates = 1;

  // List of itineraries matching market_dates that Google knows about and will
  // serve to end users. Partners should try to return results matching these
  // itineraries, as other itineraries will not be immediately useful other than
  // for analysis purposes.
  repeated ItineraryKey known_itineraries = 2;

  // If true, only the explicitly specified known_itineraries should be
  // returned. Not in use yet (always set to false).
  bool only_known_itineraries = 3;
}

message MarketDate {
  // ID of the stop where the traveler starts their journey.
  string origin_ticketing_stop_id = 1;

  // Stop ID of the ultimate journey destination of the traveler.
  string destination_ticketing_stop_id = 2;

  // Date of departure at the origin, in the timezone at the origin.
  .google.type.Date departure_date = 3;
}

// A full end-to-end journey for a traveler, including all vehicles used.
message ItineraryKey {
  // All segments together should form a single trip for which a ticket can be
  // booked.
  repeated ext.travel.transport.partner.SegmentKey segment_keys = 1;
}

JSON Sample

{
  "market_dates": [
    {
      "origin_ticketing_stop_id": "ZRH-1234",
      "destination_ticketing_stop_id": "WOL-2455",
      "departure_date": {
        "year": 2023,
        "month": 5,
        "day": 15
      }
    }
  ],
  "known_itineraries": [
    {
      "segment_keys": [
        {
          "ticketing_trip_id": "123456",
          "from_ticketing_stop_time_id": "ZRH-1234",
          "to_ticketing_stop_time_id": "LUZ-1235",
          "service_date": {
            "year": 2023,
            "month": 5,
            "day": 15
          },
          "boarding_time": {
            "year": 2023,
            "month": 5,
            "day": 15,
            "hours": 14,
            "minutes": 25,
            "seconds": 0,
            "nanos": 0,
            "utc_offset": "0s"
          },
          "arrival_time": {
            "year": 2023,
            "month": 5,
            "day": 15,
            "hours": 16,
            "minutes": 25,
            "seconds": 0,
            "nanos": 0,
            "utc_offset": "0s"
          }
        },
        {
          "ticketing_trip_id": "988833",
          "from_ticketing_stop_time_id": "LUZ-1235",
          "to_ticketing_stop_time_id": "WOL-2455",
          "service_date": {
            "year": 2023,
            "month": 5,
            "day": 15
          },
          "boarding_time": {
            "year": 2023,
            "month": 5,
            "day": 15,
            "hours": 18,
            "minutes": 13,
            "seconds": 0,
            "nanos": 0,
            "utc_offset": "0s"
          },
          "arrival_time": {
            "year": 2023,
            "month": 5,
            "day": 15,
            "hours": 20,
            "minutes": 13,
            "seconds": 0,
            "nanos": 0,
            "utc_offset": "0s"
          }
        }
      ]
    },
    {
      "segment_keys": [
        {
          "ticketing_trip_id": "98765",
          "from_ticketing_stop_time_id": "ZRH-1234",
          "to_ticketing_stop_time_id": "ZUG-1235",
          "service_date": {
            "year": 2023,
            "month": 5,
            "day": 15
          },
          "boarding_time": {
            "year": 2023,
            "month": 5,
            "day": 15,
            "hours": 14,
            "minutes": 30,
            "seconds": 0,
            "nanos": 0,
            "utc_offset": "0s"
          },
          "arrival_time": {
            "year": 2023,
            "month": 5,
            "day": 15,
            "hours": 16,
            "minutes": 30,
            "seconds": 0,
            "nanos": 0,
            "utc_offset": "0s"
          }
        },
        {
          "ticketing_trip_id": "43210",
          "from_ticketing_stop_time_id": "ZUG-1235",
          "to_ticketing_stop_time_id": "WOL-2455",
          "service_date": {
            "year": 2023,
            "month": 5,
            "day": 15
          },
          "boarding_time": {
            "year": 2023,
            "month": 5,
            "day": 15,
            "hours": 18,
            "minutes": 13,
            "seconds": 0,
            "nanos": 0,
            "utc_offset": "0s"
          },
          "arrival_time": {
            "year": 2023,
            "month": 5,
            "day": 15,
            "hours": 20,
            "minutes": 13,
            "seconds": 0,
            "nanos": 0,
            "utc_offset": "0s"
          }
        }
      ]
    }
  ],
  "only_known_itineraries": false
}

GetBulkTripOptionsResponse

Proto Definition

// Response received from partners.
message GetBulkTripOptionsResponse {
  oneof response {
    BulkTripOptionsResult bulk_trip_options_result = 1;
    BulkTripOptionsError bulk_trip_options_error = 2;
  }
}

// A message representing a successful response.
message BulkTripOptionsResult {
  // One response per entry in known_itineraries in the request, plus any
  // additional ones partners choose to return if only_known_itineraries is
  // false.
  repeated ItineraryResponse itinerary_responses = 1;
}

// A message for errors affecting the whole request.
message BulkTripOptionsError {
  enum BulkTripOptionsErrorType {
    // An unexpected error happened during retrieval of this trip.
    // This indicates a bug that needs to be fixed by the partner.
    // This error is retriable.
    // Always returns HTTP 500 status code in response.
    INTERNAL_ERROR = 1;
  }

  BulkTripOptionsErrorType error_type = 1;

  // This is not user-visible and is for logging and debugging purposes only.
  string error_message = 2;
}

// A message container for an itinerary with a set of prices for that itinerary.
message ItineraryResponse {
  // A single itinerary that this response relates to.
  ItineraryKey itinerary = 1;

  oneof response {
    // A set of prices for this itinerary.
    TripOptionSet trip_option_set = 2;

    // Error information about the retrieval of this itinerary.
    TripOptionsError trip_options_error = 3;
  }
}

// A container for multiple trip options.
message TripOptionSet {
  // To reduce the amount of data transferred, segments.segment_key can be empty
  // inside these trip_options, since the segment_key is already present in
  // ItineraryResponse.itinerary.segment_keys. This requires the number of
  // segments in to each TripOption to match exactly the number of segments
  // inside ItineraryResponse.itinerary. We then assume that the n-th
  // trip_options.segments.segment_key matches the n-th itinerary.segment_key.
  repeated TripOption trip_options = 1;
}


// A travel option for users covering the whole end-to-end journey with the
// partner.
message TripOption {
  // A series of Segments that cover the whole trip option, one for each
  // SegmentKey in the request.
  repeated Segment segments = 1;

  // This is the lowest standard fare of all seats with the specified segments.
  // This field is optional if the series of segments is unavailable.
  // This "standard fare" should be the lowest seat price where no age or other
  // booking restrictions apply. The currency should be the local currency of
  // the origin of the first segment.
  Fare lowest_standard_fare = 2;

  // The availability status for bookings of this trip option at the
  // lowest_standard_fare.
  Availability availability = 3;

  // This is used to receive optional extra data via the Travel Transport API
  // and pass it into the ticketing deep link.
  // Booking links should still work without the booking token, but the token
  // can be used to ensure price consistency between Google Search results and
  // the partner booking page.
  string booking_token = 4 [features.field_presence = EXPLICIT];
}

// A segment traveled on a single vehicle in a given Service Class.
message Segment {
  SegmentKey segment_key = 1;

  // The service class used on this segment.
  ServiceClass service_class = 2;
}

JSON Sample

{
  "bulk_trip_options_result": {
    "itinerary_responses": [
      {
        "itinerary": {
          "segment_keys": [
            {
              "ticketing_trip_id": "123456",
              "from_ticketing_stop_time_id": "ZRH-1234",
              "to_ticketing_stop_time_id": "LUZ-1235",
              "service_date": {
                "year": 2023,
                "month": 5,
                "day": 15
              },
              "boarding_time": {
                "year": 2023,
                "month": 5,
                "day": 15,
                "hours": 14,
                "minutes": 25,
                "seconds": 0,
                "nanos": 0,
                "utc_offset": "0s"
              },
              "arrival_time": {
                "year": 2023,
                "month": 5,
                "day": 15,
                "hours": 16,
                "minutes": 25,
                "seconds": 0,
                "nanos": 0,
                "utc_offset": "0s"
              }
            },
            {
              "ticketing_trip_id": "988833",
              "from_ticketing_stop_time_id": "LUZ-1235",
              "to_ticketing_stop_time_id": "WOL-2455",
              "service_date": {
                "year": 2023,
                "month": 5,
                "day": 15
              },
              "boarding_time": {
                "year": 2023,
                "month": 5,
                "day": 15,
                "hours": 18,
                "minutes": 13,
                "seconds": 0,
                "nanos": 0,
                "utc_offset": "0s"
              },
              "arrival_time": {
                "year": 2023,
                "month": 5,
                "day": 15,
                "hours": 20,
                "minutes": 13,
                "seconds": 0,
                "nanos": 0,
                "utc_offset": "0s"
              }
            }
          ]
        },
        "trip_option_set": {
          "trip_options": [
            {
              "segments": [
                {
                  "service_class": {
                    "type": "SECOND_CLASS"
                  }
                },
                {
                  "service_class": {
                    "type": "SECOND_CLASS"
                  }
                }
              ],
              "lowest_standard_fare": {
                "total_amount": {
                  "units": 10,
                  "nanos": 0,
                  "currency_code": "CHF"
                },
                "line_items": [
                  {
                    "line_item_type": "BASE_FARE",
                    "amount": {
                      "units": 9,
                      "nanos": 750000000,
                      "currency_code": "CHF"
                    }
                  },
                  {
                    "line_item_type": "SERVICE_CHARGE",
                    "amount": {
                      "units": 0,
                      "nanos": 250000000,
                      "currency_code": "CHF"
                    }
                  }
                ]
              },
              "availability": {
                "available": {
                  "available_seat_count": 44,
                  "total_seat_count": 200
                }
              }
            },
            {
              "segments": [
                {
                  "service_class": {
                    "type": "FIRST_CLASS"
                  }
                },
                {
                  "service_class": {
                    "type": "FIRST_CLASS"
                  }
                }
              ],
              "lowest_standard_fare": {
                "total_amount": {
                  "units": 15,
                  "nanos": 0,
                  "currency_code": "CHF"
                },
                "line_items": [
                  {
                    "line_item_type": "BASE_FARE",
                    "amount": {
                      "units": 13,
                      "nanos": 950000000,
                      "currency_code": "CHF"
                    }
                  },
                  {
                    "line_item_type": "SERVICE_CHARGE",
                    "amount": {
                      "units": 1,
                      "nanos": 50000000,
                      "currency_code": "CHF"
                    }
                  }
                ]
              },
              "availability": {
                "available": {
                  "available_seat_count": 10,
                  "total_seat_count": 100
                }
              }
            }
          ]
        }
      },
      {
        "itinerary": {
          "segment_keys": [
            {
              "ticketing_trip_id": "98765",
              "from_ticketing_stop_time_id": "ZRH-1234",
              "to_ticketing_stop_time_id": "ZUG-1235",
              "service_date": {
                "year": 2023,
                "month": 5,
                "day": 15
              },
              "boarding_time": {
                "year": 2023,
                "month": 5,
                "day": 15,
                "hours": 14,
                "minutes": 30,
                "seconds": 0,
                "nanos": 0,
                "utc_offset": "0s"
              },
              "arrival_time": {
                "year": 2023,
                "month": 5,
                "day": 15,
                "hours": 16,
                "minutes": 30,
                "seconds": 0,
                "nanos": 0,
                "utc_offset": "0s"
              }
            },
            {
              "ticketing_trip_id": "43210",
              "from_ticketing_stop_time_id": "ZUG-1235",
              "to_ticketing_stop_time_id": "WOL-2455",
              "service_date": {
                "year": 2023,
                "month": 5,
                "day": 15
              },
              "boarding_time": {
                "year": 2023,
                "month": 5,
                "day": 15,
                "hours": 18,
                "minutes": 13,
                "seconds": 0,
                "nanos": 0,
                "utc_offset": "0s"
              },
              "arrival_time": {
                "year": 2023,
                "month": 5,
                "day": 15,
                "hours": 20,
                "minutes": 13,
                "seconds": 0,
                "nanos": 0,
                "utc_offset": "0s"
              }
            }
          ]
        },
        "trip_option_set": {
          "trip_options": [
            {
              "segments": [
                {
                  "service_class": {
                    "type": "SECOND_CLASS"
                  }
                },
                {
                  "service_class": {
                    "type": "SECOND_CLASS"
                  }
                }
              ],
              "lowest_standard_fare": {
                "total_amount": {
                  "units": 9,
                  "nanos": 0,
                  "currency_code": "CHF"
                },
                "line_items": [
                  {
                    "line_item_type": "BASE_FARE",
                    "amount": {
                      "units": 8,
                      "nanos": 750000000,
                      "currency_code": "CHF"
                    }
                  },
                  {
                    "line_item_type": "SERVICE_CHARGE",
                    "amount": {
                      "units": 0,
                      "nanos": 250000000,
                      "currency_code": "CHF"
                    }
                  }
                ]
              },
              "availability": {
                "available": {
                  "available_seat_count": 30,
                  "total_seat_count": 150
                }
              }
            },
            {
              "segments": [
                {
                  "service_class": {
                    "type": "FIRST_CLASS"
                  }
                },
                {
                  "service_class": {
                    "type": "FIRST_CLASS"
                  }
                }
              ],
              "lowest_standard_fare": {
                "total_amount": {
                  "units": 14,
                  "nanos": 0,
                  "currency_code": "CHF"
                },
                "line_items": [
                  {
                    "line_item_type": "BASE_FARE",
                    "amount": {
                      "units": 12,
                      "nanos": 950000000,
                      "currency_code": "CHF"
                    }
                  },
                  {
                    "line_item_type": "SERVICE_CHARGE",
                    "amount": {
                      "units": 1,
                      "nanos": 50000000,
                      "currency_code": "CHF"
                    }
                  }
                ]
              },
              "availability": {
                "available": {
                  "available_seat_count": 15,
                  "total_seat_count": 60
                }
              }
            }
          ]
        }
      }
    ]
  }
}

JSON Error Sample

{
  "bulk_trip_options_error": {
    "error_type": "INTERNAL_ERROR",
    "error_message": "Unexpected RoutingException during trip lookup."
  }
}

JSON Partial Error

{
  "bulk_trip_options_result": {
    "itinerary_responses": [
      {
        "itinerary": {
          "segment_keys": [
            {
              "ticketing_trip_id": "123456",
              "from_ticketing_stop_time_id": "ZRH-1234",
              "to_ticketing_stop_time_id": "LUZ-1235",
              "service_date": {
                "year": 2023,
                "month": 5,
                "day": 15
              },
              "boarding_time": {
                "year": 2023,
                "month": 5,
                "day": 15,
                "hours": 14,
                "minutes": 25,
                "seconds": 0,
                "nanos": 0,
                "utc_offset": "0s"
              },
              "arrival_time": {
                "year": 2023,
                "month": 5,
                "day": 15,
                "hours": 16,
                "minutes": 25,
                "seconds": 0,
                "nanos": 0,
                "utc_offset": "0s"
              }
            },
            {
              "ticketing_trip_id": "988833",
              "from_ticketing_stop_time_id": "LUZ-1235",
              "to_ticketing_stop_time_id": "WOL-2455",
              "service_date": {
                "year": 2023,
                "month": 5,
                "day": 15
              },
              "boarding_time": {
                "year": 2023,
                "month": 5,
                "day": 15,
                "hours": 18,
                "minutes": 13,
                "seconds": 0,
                "nanos": 0,
                "utc_offset": "0s"
              },
              "arrival_time": {
                "year": 2023,
                "month": 5,
                "day": 15,
                "hours": 20,
                "minutes": 13,
                "seconds": 0,
                "nanos": 0,
                "utc_offset": "0s"
              }
            }
          ]
        },
        "trip_option_set": {
          "trip_options": [
            {
              "segments": [
                {
                  "service_class": {
                    "type": "SECOND_CLASS"
                  }
                },
                {
                  "service_class": {
                    "type": "SECOND_CLASS"
                  }
                }
              ],
              "lowest_standard_fare": {
                "total_amount": {
                  "units": 10,
                  "nanos": 0,
                  "currency_code": "CHF"
                },
                "line_items": [
                  {
                    "line_item_type": "BASE_FARE",
                    "amount": {
                      "units": 9,
                      "nanos": 750000000,
                      "currency_code": "CHF"
                    }
                  },
                  {
                    "line_item_type": "SERVICE_CHARGE",
                    "amount": {
                      "units": 0,
                      "nanos": 250000000,
                      "currency_code": "CHF"
                    }
                  }
                ]
              },
              "availability": {
                "available": {
                  "available_seat_count": 44,
                  "total_seat_count": 200
                }
              }
            },
            {
              "segments": [
                {
                  "service_class": {
                    "type": "FIRST_CLASS"
                  }
                },
                {
                  "service_class": {
                    "type": "FIRST_CLASS"
                  }
                }
              ],
              "lowest_standard_fare": {
                "total_amount": {
                  "units": 15,
                  "nanos": 0,
                  "currency_code": "CHF"
                },
                "line_items": [
                  {
                    "line_item_type": "BASE_FARE",
                    "amount": {
                      "units": 13,
                      "nanos": 950000000,
                      "currency_code": "CHF"
                    }
                  },
                  {
                    "line_item_type": "SERVICE_CHARGE",
                    "amount": {
                      "units": 1,
                      "nanos": 50000000,
                      "currency_code": "CHF"
                    }
                  }
                ]
              },
              "availability": {
                "available": {
                  "available_seat_count": 10,
                  "total_seat_count": 100
                }
              }
            }
          ]
        }
      },
      {
        "itinerary": {
          "segment_keys": [
            {
              "ticketing_trip_id": "98765",
              "from_ticketing_stop_time_id": "ZRH-1234",
              "to_ticketing_stop_time_id": "ZUG-1235",
              "service_date": {
                "year": 2023,
                "month": 5,
                "day": 15
              },
              "boarding_time": {
                "year": 2023,
                "month": 5,
                "day": 15,
                "hours": 14,
                "minutes": 30,
                "seconds": 0,
                "nanos": 0,
                "utc_offset": "0s"
              },
              "arrival_time": {
                "year": 2023,
                "month": 5,
                "day": 15,
                "hours": 16,
                "minutes": 30,
                "seconds": 0,
                "nanos": 0,
                "utc_offset": "0s"
              }
            },
            {
              "ticketing_trip_id": "43210",
              "from_ticketing_stop_time_id": "ZUG-1235",
              "to_ticketing_stop_time_id": "WOL-2455",
              "service_date": {
                "year": 2023,
                "month": 5,
                "day": 15
              },
              "boarding_time": {
                "year": 2023,
                "month": 5,
                "day": 15,
                "hours": 18,
                "minutes": 13,
                "seconds": 0,
                "nanos": 0,
                "utc_offset": "0s"
              },
              "arrival_time": {
                "year": 2023,
                "month": 5,
                "day": 15,
                "hours": 20,
                "minutes": 13,
                "seconds": 0,
                "nanos": 0,
                "utc_offset": "0s"
              }
            }
          ]
        },
        "trip_options_error": {
          "error_type": "SEGMENT_KEY_NOT_FOUND",
          "error_message": "No matching segments found, no departures at 14:30"
        }
      }
    ]
  }
}

Typical scenarios

We imagine the following scenarios with particular request shapes for the API:

  • For less technically sophisticated partners, only providing a market_date. These partners can use a market_date based query to their upstream data and return all results mapped to our data format. Google can filter the results to only the expected itineraries.

  • For more technically sophisticated partners, providing both a market_date and known_itineraries. The partner can do a detailed lookup in internal databases to be able to price exactly the expected itineraries. All the requested known_itineraries will be shown to users, so partners can maximize the number of itineraries that show their price and booking link. Partners still have the option of returning additional itineraries in order to help Google better match their own (promoted) inventory.

Error reporting

Errors should be reported for each trip individually. Therefore, the preferred response is a BulkTripOptionsResult (HTTP 200), indicating per-trip errors in bulk_trip_options_result.itinerary_responses[i].trip_options_error. If the entire request failed unexpectedly, use bulk_trip_options_error with BulkTripOptionsErrorType.INTERNAL_ERROR (HTTP 500).

For more details see TripOptionsErrorType

For status code return in HTTP responses, refer to Status codes & error handling.

Remarks

MarketDate.departure_date specifies a calendar day. To better match operator APIs, this will use the timezone at the origin. However, SegmentKey will continue to specify boarding and arrival times using UTC for consistency with GetTripOptions.