Skip to content

Instantly share code, notes, and snippets.

@MomoDeve
Last active April 22, 2026 22:26
Show Gist options
  • Select an option

  • Save MomoDeve/a18053dea84dd28e320b8b2c489540eb to your computer and use it in GitHub Desktop.

Select an option

Save MomoDeve/a18053dea84dd28e320b8b2c489540eb to your computer and use it in GitHub Desktop.
Google Flights tfs URL Parameter Spec
syntax = "proto2";
package nomad.google_flights.research;
// Reverse-engineered Google Flights `tfs` URL parameter.
//
// The `tfs` query value is URL-safe base64, usually without padding, over this
// protobuf-style message. This is research, not an official Google contract.
// Field names are descriptive and based on controlled Google Flights URL diffs.
message GoogleFlightsTfs {
// Example: every captured search encoded query_mode = 28.
//
// Meaning is still unknown. Synthetic changes did not produce visible UI
// differences in testing; keep the value when generating compatible URLs.
optional uint32 query_mode = 1;
// Example: every captured search encoded query_context = 2.
//
// Meaning is still unknown. Synthetic changes did not produce visible UI
// differences in testing; keep the value when generating compatible URLs.
optional uint32 query_context = 2;
// One leg for one-way, two reversed legs for round-trip, and N legs for
// multi-city. Multi-city can encode, but does not reliably open with all legs
// visible in the Google Flights UI.
repeated FlightLeg legs = 3;
// One entry per traveler.
repeated PassengerType passengers = 8;
optional Cabin cabin = 9;
// Maximum displayed price amount.
//
// Example:
// price up to 4000 CAD: max_price_amount = 4000
//
// Currency is not encoded in this field. In tested URLs, changing gl=CA vs
// gl=US changed the displayed currency context while leaving this amount as
// the numeric ceiling.
optional uint32 max_price_amount = 12;
// Omitted for no bag filter.
//
// Google-generated carry-on samples currently observed:
// 1 adult + 1 carry-on: field_2 = 1, field_3 = 0
// 2 adults + 2 carry-on: field_2 = 2, field_3 = 0
// 3 adults + 3 carry-on: field_2 = 3, field_3 = 0
//
// Current best hypothesis: field_2 tracks selected carry-on count in natural
// URLs, while field_3 stays 0 for these samples.
//
// Caution: synthetic probes with alternate combinations (for example
// field_2 = 1/2 and field_3 = 0/1) did not always produce distinct visible
// UI states, so carry-on vs checked semantics are still unresolved.
optional BaggageFilter baggage = 13;
// Example: every captured search encoded display_flag = 1.
//
// Meaning is still unknown. Synthetic changes did not produce visible UI
// differences in testing; keep the value when generating compatible URLs.
optional uint32 display_flag = 14;
// Example: all_results_flag.sentinel = 18446744073709551615.
optional AllResultsFlag all_results_flag = 16;
// Example:
// "Hide separate & self-transfer tickets" enabled: true
// disabled: field omitted
optional bool hide_separate_self_transfer_tickets = 17;
optional TripType trip_type = 19;
}
message FlightLeg {
// YYYY-MM-DD.
optional string departure_date = 2;
// Optional maximum stops filter.
//
// Presence matters because zero is meaningful:
// omitted = no max-stops filter
// 0 = nonstop only
// 1 = 1 stop or fewer
// 2 = 2 stops or fewer
optional uint32 max_stops = 5;
// Airline IATA codes, e.g. "AC" or "NH".
repeated string include_airlines = 6;
// Airline IATA codes to exclude.
repeated string exclude_airlines = 7;
// Time filters use whole-hour 24-hour buckets.
//
// Start fields match the visible start hour. End fields are the final
// included hour bucket, so a visible upper bound of 20:00 encodes as 19.
optional uint32 departure_start_hour = 8;
optional uint32 departure_end_hour_inclusive = 9;
optional uint32 arrival_start_hour = 10;
optional uint32 arrival_end_hour_inclusive = 11;
// Maximum total leg duration in minutes.
//
// Example:
// "under 16h" = 960
optional uint32 max_duration_minutes = 12;
optional Place origin = 13;
optional Place destination = 14;
// Connection airport IATA codes.
//
// Examples:
// only Amsterdam as connection: include_connection_airports = "AMS"
// Amsterdam + Calgary as connections: "AMS", "YYC"
// exclude Amsterdam connection: exclude_connection_airports = "AMS"
repeated string include_connection_airports = 15;
repeated string exclude_connection_airports = 16;
// Maximum layover duration in minutes.
//
// Example:
// 13h30 = 810
optional uint32 max_layover_minutes = 18;
// "Less emissions" filter.
//
// Example:
// checkbox enabled: packed payload containing value 1
//
// Modeled as packed numeric flags because the wire type is length-delimited,
// not a plain varint bool.
repeated uint32 emissions_flags = 19 [packed = true];
}
message Place {
// Examples:
// 1 = exact airport, paired with a three-letter IATA code in entity_id
// (YVR, CDG, YTZ, HND)
// 2/3 = Google city/entity ids in /m/... form
//
// The difference between city entity types 2 and 3 is not clear enough to
// encode as a stable enum.
optional uint32 entity_type = 1;
// Either an IATA code like "YVR" or a Google entity id like "/m/07dfk".
optional string entity_id = 2;
}
message BaggageFilter {
// Observed in natural Google-generated carry-on URLs:
// field_2 = carry-on count (1, 2, 3 seen so far)
// field_3 = 0
//
// UI note from testing: carry-on count cannot exceed adult count.
//
// These are still research names, currently unknown
optional uint32 field_2 = 2;
optional uint32 field_3 = 3;
}
message AllResultsFlag {
// Example: 18446744073709551615.
//
// This looks like an all-results or on-demand calculation sentinel.
optional uint64 sentinel = 1;
}
// Companion `tfu` query parameter captured alongside some `tfs` URLs.
//
// It did not control passenger count, cabin, stop filters, airline filters,
// time windows, or bag count in our current samples. Keep it separate from the
// `tfs` contract until its role is clearer.
message GoogleFlightsTfu {
optional TfuState state = 2;
}
message TfuState {
// Examples:
//
// tfu=EgYIABAAGAA:
// sort = 0
// field_2 = 0
// field_3 = 0
//
// tfu=EgoIABAAGAAgASgI:
// sort = 0
// field_2 = 0
// field_3 = 0
// field_4 = 1
// field_5 = 8
//
// cheapest mode with top-flights sort:
// sort = 1
// field_4 = 2
// field_5 = 19
//
// Sort examples:
// top flights: sort = 1
// price: sort = 2
// departure time: sort = 3
// arrival time: sort = 4
// duration: sort = 5
// emissions: sort = 6
optional SortMode sort = 1;
optional uint32 field_2 = 2;
optional uint32 field_3 = 3;
optional SearchMode search_mode = 4;
// Companion state that changes in natural Google-generated Best/Cheapest
// URLs, but does not appear to control the visible mode when changed alone.
//
// Examples:
// Google-generated best/default mode: field_5 = 8
// Google-generated cheapest mode: field_5 = 19
//
// Probes showed field_4 controls the visible Best/Cheapest mode:
// field_4 = 2, field_5 = 8 -> cheapest
// field_4 = 1, field_5 = 19 -> best
//
// Synthetic changes beyond those examples did not produce visible UI
// differences in testing. Preserve Google-generated values when possible.
optional uint32 field_5 = 5;
}
enum PassengerType {
PASSENGER_TYPE_UNKNOWN = 0;
PASSENGER_ADULT = 1;
PASSENGER_CHILD = 2;
PASSENGER_INFANT_ON_LAP = 3;
PASSENGER_INFANT_IN_SEAT = 4;
}
enum Cabin {
CABIN_UNKNOWN = 0;
CABIN_ECONOMY = 1;
CABIN_PREMIUM_ECONOMY = 2;
CABIN_BUSINESS = 3;
CABIN_FIRST = 4;
}
enum TripType {
TRIP_TYPE_UNKNOWN = 0;
TRIP_ROUND_TRIP = 1;
TRIP_ONE_WAY = 2;
TRIP_MULTI_CITY = 3;
}
enum SortMode {
SORT_DEFAULT = 0;
SORT_TOP_FLIGHTS = 1;
SORT_PRICE = 2;
SORT_DEPARTURE_TIME = 3;
SORT_ARRIVAL_TIME = 4;
SORT_DURATION = 5;
SORT_EMISSIONS = 6;
}
enum SearchMode {
SEARCH_MODE_UNKNOWN = 0;
SEARCH_MODE_BEST = 1;
SEARCH_MODE_CHEAPEST = 2;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment