Skip to content

Models Reference

Core Models

FlightSearchFilters

The main model for configuring flight searches.

from fli.models import (
    FlightSearchFilters, FlightSegment, Airport,
    SeatType, MaxStops, SortBy, TripType, PassengerInfo
)

# Create flight segments for round trip
flight_segments = [
    FlightSegment(
        departure_airport=[[Airport.JFK, 0]],
        arrival_airport=[[Airport.LAX, 0]],
        travel_date="2026-06-01",
    ),
    FlightSegment(
        departure_airport=[[Airport.LAX, 0]],
        arrival_airport=[[Airport.JFK, 0]],
        travel_date="2026-06-15",
    )
]

filters = FlightSearchFilters(
    trip_type=TripType.ROUND_TRIP,
    passenger_info=PassengerInfo(adults=1),
    flight_segments=flight_segments,
    stops=MaxStops.NON_STOP,
    seat_type=SeatType.ECONOMY,
    sort_by=SortBy.CHEAPEST
)

Validation Rules: - Flight segments must have different departure and arrival airports - Travel dates cannot be in the past - For round trips, exactly two flight segments are required - Passenger counts must be valid (at least one adult)

fli.models.google_flights.FlightSearchFilters

Bases: BaseModel

Complete set of filters for flight search.

This model matches required Google Flights' API structure.

airlines: list[Airline] | None = None class-attribute instance-attribute

bags: BagsFilter | None = None class-attribute instance-attribute

emissions: EmissionsFilter = EmissionsFilter.ALL class-attribute instance-attribute

exclude_basic_economy: bool = False class-attribute instance-attribute

flight_segments: list[FlightSegment] instance-attribute

layover_restrictions: LayoverRestrictions | None = None class-attribute instance-attribute

max_duration: PositiveInt | None = None class-attribute instance-attribute

passenger_info: PassengerInfo instance-attribute

price_limit: PriceLimit | None = None class-attribute instance-attribute

seat_type: SeatType = SeatType.ECONOMY class-attribute instance-attribute

show_all_results: bool = True class-attribute instance-attribute

sort_by: SortBy = SortBy.BEST class-attribute instance-attribute

stops: MaxStops = MaxStops.ANY class-attribute instance-attribute

trip_type: TripType = TripType.ONE_WAY class-attribute instance-attribute

encode() -> str

URL encode the formatted filters for API request.

Source code in fli/models/google_flights/flights.py
def encode(self) -> str:
    """URL encode the formatted filters for API request."""
    formatted_filters = self.format()
    # First convert the formatted filters to a JSON string
    formatted_json = json.dumps(formatted_filters, separators=(",", ":"))
    # Then wrap it in a list with null
    wrapped_filters = [None, formatted_json]
    # Finally, encode the whole thing
    return urllib.parse.quote(json.dumps(wrapped_filters, separators=(",", ":")))

format() -> list

Format filters into Google Flights API structure.

This method converts the FlightSearchFilters model into the specific nested list/dict structure required by Google Flights' API.

The output format matches Google Flights' internal API structure, with careful handling of nested arrays and proper serialization of enums and model objects.

RETURNS DESCRIPTION
list

A formatted list structure ready for the Google Flights API request

TYPE: list

Source code in fli/models/google_flights/flights.py
def format(self) -> list:
    """Format filters into Google Flights API structure.

    This method converts the FlightSearchFilters model into the specific nested list/dict
    structure required by Google Flights' API.

    The output format matches Google Flights' internal API structure, with careful handling
    of nested arrays and proper serialization of enums and model objects.

    Returns:
        list: A formatted list structure ready for the Google Flights API request

    """

    def serialize(obj):
        if isinstance(obj, Airport) or isinstance(obj, Airline):
            return obj.name.removeprefix("_")
        if isinstance(obj, Enum):
            return obj.value
        if isinstance(obj, list):
            return [serialize(item) for item in obj]
        if isinstance(obj, dict):
            return {key: serialize(value) for key, value in obj.items()}
        if isinstance(obj, BaseModel):
            return serialize(obj.dict(exclude_none=True))
        return obj

    # Format flight segments
    formatted_segments = []
    for segment in self.flight_segments:
        # Format airport codes with correct nesting
        segment_filters = [
            [
                [
                    [serialize(airport[0]), serialize(airport[1])]
                    for airport in segment.departure_airport
                ]
            ],
            [
                [
                    [serialize(airport[0]), serialize(airport[1])]
                    for airport in segment.arrival_airport
                ]
            ],
        ]

        # Time restrictions
        if segment.time_restrictions:
            time_filters = [
                segment.time_restrictions.earliest_departure,
                segment.time_restrictions.latest_departure,
                segment.time_restrictions.earliest_arrival,
                segment.time_restrictions.latest_arrival,
            ]
        else:
            time_filters = None

        # Airlines
        airlines_filters = None
        if self.airlines:
            sorted_airlines = sorted(self.airlines, key=lambda x: x.value)
            airlines_filters = [serialize(airline) for airline in sorted_airlines]

        # Layover restrictions
        layover_airports = (
            [serialize(a) for a in self.layover_restrictions.airports]
            if self.layover_restrictions and self.layover_restrictions.airports
            else None
        )
        layover_duration = (
            self.layover_restrictions.max_duration if self.layover_restrictions else None
        )

        # Selected flight (to fetch return/next-leg flights)
        selected_flights = None
        is_multi_leg = self.trip_type in (TripType.ROUND_TRIP, TripType.MULTI_CITY)
        if is_multi_leg and segment.selected_flight is not None:
            selected_flights = [
                [
                    serialize(leg.departure_airport),
                    serialize(leg.departure_datetime.strftime("%Y-%m-%d")),
                    serialize(leg.arrival_airport),
                    None,
                    serialize(leg.airline),
                    serialize(leg.flight_number),
                ]
                for leg in segment.selected_flight.legs
            ]

        # Emissions filter
        emissions_filter = (
            [self.emissions.value] if self.emissions != EmissionsFilter.ALL else None
        )

        segment_formatted = [
            segment_filters[0],  # departure airport
            segment_filters[1],  # arrival airport
            time_filters,  # time restrictions
            serialize(self.stops.value),  # stops
            airlines_filters,  # airlines
            None,  # unknown: accepts [] but 400s on scalars; seemingly no effect
            segment.travel_date,  # travel date
            [self.max_duration] if self.max_duration else None,  # max duration
            selected_flights,  # selected flight (to fetch return flights)
            layover_airports,  # layover airports
            None,  # unknown: accepts [] but 400s on scalars; seemingly no effect
            None,  # seemingly no effect: accepts any value (0-3, bool) without changing results
            layover_duration,  # layover duration
            emissions_filter,  # emissions filter: [1]=less emissions
            3,  # seemingly no effect: accepts any value (0-5, None) without changing results
        ]
        formatted_segments.append(segment_formatted)

    # Bags filter
    bags_filter = [self.bags.checked_bags, int(self.bags.carry_on)] if self.bags else None

    # The browser uses a wrapper nesting where outer[1] = [[], [main], ...fields...]
    # with self-transfer at wrapper[6] and basic economy at wrapper[15].
    # However, the wrapper format returns empty results through our API client
    # (likely requires browser cookies/headers). We use a flat format instead
    # which the API accepts. NOTE: Self-transfer cannot be toggled in the flat format.
    #
    # Main settings (filters[1]) index map:
    #   0:  unknown - seemingly no effect (tested 0-3, [], "en")
    #   1:  unknown - seemingly no effect (tested "USD"/"EUR"/"GBP"/"JPY");
    #       currency appears to be determined by IP/locale
    #   2:  trip type
    #   3:  unknown - seemingly no effect (tested 0-3)
    #   4:  unknown - seemingly no effect as [] or None; 400s on scalars
    #   5:  seat/cabin type
    #   6:  passenger counts [adults, children, infants_lap, infants_seat]
    #   7:  price limit [None, max_price]
    #   8:  unknown - seemingly no effect (tested 0-3, arrays)
    #   9:  unknown - seemingly no effect (tested 0-3, arrays)
    #   10: bags filter [checked_bags, carry_on]
    #   11: unknown - seemingly no effect (tested 0-3, arrays)
    #   12: unknown - seemingly no effect (tested 0-3, arrays)
    #   13: flight segments
    #   14-16: unknown - seemingly no effect
    #   17: unknown - seemingly no effect (hardcoded to 1)
    #   18-27: unknown - seemingly no effect
    #   28: exclude basic economy (0=allow, 1=exclude)
    #
    filters = [
        [],  # outer[0]
        [
            None,  # [0] seemingly no effect
            None,  # [1] seemingly no effect (not currency)
            serialize(self.trip_type.value),
            None,  # [3] seemingly no effect
            [],  # [4] seemingly no effect
            serialize(self.seat_type.value),
            [
                self.passenger_info.adults,
                self.passenger_info.children,
                self.passenger_info.infants_on_lap,
                self.passenger_info.infants_in_seat,
            ],
            [None, self.price_limit.max_price] if self.price_limit else None,
            None,  # [8] seemingly no effect
            None,  # [9] seemingly no effect
            bags_filter,  # [10] bags filter [checked_bags, carry_on]
            None,  # [11] seemingly no effect
            None,  # [12] seemingly no effect
            formatted_segments,
            None,  # [14] seemingly no effect
            None,  # [15] seemingly no effect
            None,  # [16] seemingly no effect
            1,  # [17] seemingly no effect (hardcoded to 1)
            None,  # [18] seemingly no effect
            None,  # [19] seemingly no effect
            None,  # [20] seemingly no effect
            None,  # [21] seemingly no effect
            None,  # [22] seemingly no effect
            None,  # [23] seemingly no effect
            None,  # [24] seemingly no effect
            None,  # [25] seemingly no effect
            None,  # [26] seemingly no effect
            None,  # [27] seemingly no effect
            1 if self.exclude_basic_economy else 0,
        ],
        serialize(self.sort_by.value),  # outer[2] sort mode
        1 if self.show_all_results else 0,  # outer[3] 0=~30, 1=all results
        0,  # outer[4] seemingly no effect
        1,  # outer[5] seemingly no effect
    ]

    return filters

FlightResult

Represents a flight search result with complete details.

fli.models.google_flights.FlightResult

Bases: BaseModel

Complete flight search result with pricing and timing.

currency: str | None = None class-attribute instance-attribute

duration: PositiveInt instance-attribute

legs: list[FlightLeg] instance-attribute

price: NonNegativeFloat instance-attribute

stops: NonNegativeInt instance-attribute

FlightLeg

Represents a single flight segment with airline and timing details.

fli.models.google_flights.FlightLeg

Bases: BaseModel

A single flight leg (segment) with airline and timing details.

airline: Airline instance-attribute

arrival_airport: Airport instance-attribute

arrival_datetime: datetime instance-attribute

departure_airport: Airport instance-attribute

departure_datetime: datetime instance-attribute

duration: PositiveInt instance-attribute

flight_number: str instance-attribute

Enums

SeatType

Available cabin classes for flights.

fli.models.google_flights.SeatType

Bases: Enum

Available cabin classes for flights.

BUSINESS = 3 class-attribute instance-attribute

ECONOMY = 1 class-attribute instance-attribute

FIRST = 4 class-attribute instance-attribute

PREMIUM_ECONOMY = 2 class-attribute instance-attribute

MaxStops

Maximum number of stops allowed in flight search.

fli.models.google_flights.MaxStops

Bases: Enum

Maximum number of stops allowed in flight search.

ANY = 0 class-attribute instance-attribute

NON_STOP = 1 class-attribute instance-attribute

ONE_STOP_OR_FEWER = 2 class-attribute instance-attribute

TWO_OR_FEWER_STOPS = 3 class-attribute instance-attribute

SortBy

Available sorting options for flight results.

fli.models.google_flights.SortBy

Bases: Enum

Available sorting options for flight results.

Maps to the top-level sort_mode value in the Google Flights API payload.

ARRIVAL_TIME = 4 class-attribute instance-attribute

BEST = 1 class-attribute instance-attribute

CHEAPEST = 2 class-attribute instance-attribute

DEPARTURE_TIME = 3 class-attribute instance-attribute

DURATION = 5 class-attribute instance-attribute

EMISSIONS = 6 class-attribute instance-attribute

TOP_FLIGHTS = 0 class-attribute instance-attribute

TripType

Type of trip for flight search.

fli.models.google_flights.TripType

Bases: Enum

Type of flight journey.

MULTI_CITY = 3 class-attribute instance-attribute

ONE_WAY = 2 class-attribute instance-attribute

ROUND_TRIP = 1 class-attribute instance-attribute

Support Models

PassengerInfo

Configuration for passenger counts.

fli.models.google_flights.PassengerInfo

Bases: BaseModel

Passenger configuration for flight search.

adults: NonNegativeInt = 1 class-attribute instance-attribute

children: NonNegativeInt = 0 class-attribute instance-attribute

infants_in_seat: NonNegativeInt = 0 class-attribute instance-attribute

infants_on_lap: NonNegativeInt = 0 class-attribute instance-attribute

TimeRestrictions

Time constraints for flight departure and arrival.

fli.models.google_flights.TimeRestrictions

Bases: BaseModel

Time constraints for flight departure and arrival in local time.

All times are in hours from midnight (e.g., 20 = 8:00 PM).

earliest_arrival: NonNegativeInt | None = None class-attribute instance-attribute

earliest_departure: NonNegativeInt | None = None class-attribute instance-attribute

latest_arrival: PositiveInt | None = None class-attribute instance-attribute

latest_departure: PositiveInt | None = None class-attribute instance-attribute

validate_latest_times(v: PositiveInt | None, info: ValidationInfo) -> PositiveInt | None classmethod

Validate and adjust the latest time restrictions.

Source code in fli/models/google_flights/base.py
@field_validator("latest_departure", "latest_arrival")
@classmethod
def validate_latest_times(
    cls, v: PositiveInt | None, info: ValidationInfo
) -> PositiveInt | None:
    """Validate and adjust the latest time restrictions."""
    if v is None:
        return v

    # Get "departure" or "arrival" from field name
    field_prefix = "earliest_" + info.field_name[7:]
    earliest = info.data.get(field_prefix)

    # Swap values to ensure that `from` is always before `to`
    if earliest is not None and earliest > v:
        info.data[field_prefix] = v
        return earliest
    return v

PriceLimit

Price constraints for flight search.

fli.models.google_flights.PriceLimit

Bases: BaseModel

Maximum price constraint for flight search.

currency: Currency | None = Currency.USD class-attribute instance-attribute

max_price: PositiveInt instance-attribute