Search API Reference¶
Flight Search¶
The main search functionality for finding specific flights.
SearchFlights¶
fli.search.flights.SearchFlights()
¶
Flight search via Google Flights' FlightsFrontendService API.
Public surface:
- :meth:
search— issue a GetShoppingResults call and return the parsed flights. - :meth:
get_booking_options— follow up with GetBookingResults to surface bookable fares for a selected itinerary. See the method docstring for the live-token limitation.
Concurrency
SearchFlights is not thread-safe. The instance caches the
shopping-session id from the most recent :meth:search call so
:meth:get_booking_options can derive the booking token; two
concurrent search calls on the same instance will race on that
cache and may cross-pollinate sessions between unrelated bookings.
:attr:sparse_passenger_mix has the same limitation — it reflects
only the most recently completed :meth:search call.
For multi-threaded or async server use, either (a) instantiate a
fresh SearchFlights per request, or (b) pass the
session_id returned by your own session bookkeeping into
:meth:get_booking_options explicitly (the kwarg overrides the
cached value).
Initialize the search client.
Source code in fli/search/flights.py
BASE_URL = 'https://www.google.com/_/FlightsFrontendUi/data/travel.frontend.flights.FlightsFrontendService/GetShoppingResults'
class-attribute
instance-attribute
¶
BOOKING_URL = 'https://www.google.com/_/FlightsFrontendUi/data/travel.frontend.flights.FlightsFrontendService/GetBookingResults'
class-attribute
instance-attribute
¶
DEFAULT_HEADERS = {'content-type': 'application/x-www-form-urlencoded;charset=UTF-8'}
class-attribute
instance-attribute
¶
client = get_client()
instance-attribute
¶
sparse_passenger_mix: bool
property
¶
Whether the sparse-passenger-mix warning fired on the most recent search().
True exactly when the empty result this instance last returned
(or raised out of) was consistent with Google's client-side pricing
gap for children/infants — at least one fetched page decoded to zero
rows before client-side filtering, and the party had a child or
infant — rather than the caller's own airline/price/duration/window
filter removing rows Google did inline. See
SPARSE_PASSENGER_MIX_WARNING. Reset to False at the start of
every :meth:search call, including ones that raise, so a stale
True from an earlier call never leaks into a later one.
Reflects only the last completed :meth:search call on this
instance and is not meant for instances shared across concurrent
searches — two overlapping :meth:search calls on one
SearchFlights would race on this attribute the same way they
already race on _last_session_id (see the class docstring's
Concurrency section). The MCP server and CLI each construct a fresh
SearchFlights per request, so this is safe there; both read this
attribute after :meth:search instead of recomputing the condition
themselves.
build_flight_booking_url(flight: FlightResult | tuple[FlightResult, ...], *, currency: str | None = None, language: str | None = None, country: str | None = None, seat_type: SeatType = SeatType.ECONOMY, passenger_info: PassengerInfo | None = None) -> str
¶
Build a Google Flights deep-link URL for a specific itinerary.
Constructs https://www.google.com/travel/flights/booking?tfs=… that
opens the booking page pre-loaded with the given itinerary — the
airline/OTA fare options and the "Continue" booking CTA included.
The tfs itinerary token is fully deterministic (built from the
flight's airports, dates and flight numbers); no session id or network
round-trip is required, so the same itinerary always yields the same
URL. This method never raises — on malformed input it falls back to the
generic Google Flights URL.
| PARAMETER | DESCRIPTION |
|---|---|
flight
|
A :class:
TYPE:
|
currency
|
ISO 4217 currency code appended as
TYPE:
|
language
|
BCP-47 language code appended as
TYPE:
|
country
|
ISO 3166-1 alpha-2 country code appended as
TYPE:
|
seat_type
|
Cabin class encoded into the |
passenger_info
|
Passenger mix encoded into the
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
str
|
A |
Source code in fli/search/flights.py
get_booking_options(flight: FlightResult | tuple[FlightResult, ...], filters: FlightSearchFilters, currency: str | None = None, language: str | None = None, country: str | None = None, booking_token: str | None = None, session_id: str | None = None) -> list[BookingOption]
¶
Fetch bookable fare options for a selected itinerary.
After a :meth:search call, the session id from Google's response
is cached on the client and used here automatically — no explicit
token plumbing is required by callers. The booking-call payload
carries the same selected_flight legs the caller used in their
round-trip search and a protobuf token constructed from the
cached session id + the chosen itinerary's identifiers.
| PARAMETER | DESCRIPTION |
|---|---|
flight
|
A :class:
TYPE:
|
filters
|
The same filters used in the preceding :meth:
TYPE:
|
currency
|
Optional ISO 4217 currency code passed to Google as
TYPE:
|
language
|
Optional BCP-47 language code (
TYPE:
|
country
|
Optional ISO 3166-1 alpha-2 country code (
TYPE:
|
booking_token
|
Explicit override for
TYPE:
|
session_id
|
Explicit override for the session id used to build
the token. Defaults to the session captured by the most
recent :meth:
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
list[BookingOption]
|
A list of :class: |
list[BookingOption]
|
returns no vendors. |
| RAISES | DESCRIPTION |
|---|---|
ValueError
|
No session id available — either pass it
explicitly or call :meth: |
Exception
|
HTTP request failure. |
Source code in fli/search/flights.py
405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 | |
search(filters: FlightSearchFilters, top_n: int = 5, currency: str | None = None, language: str | None = None, country: str | None = None) -> list[FlightResult | tuple[FlightResult, ...]] | None
¶
Search for flights using the given :class:FlightSearchFilters.
| PARAMETER | DESCRIPTION |
|---|---|
filters
|
Full search descriptor (airports, dates, preferences).
TYPE:
|
top_n
|
Number of outbound options to expand when chasing a
round-trip or multi-city itinerary. Must be between 1 and
10 (inclusive). A round trip costs
TYPE:
|
currency
|
Optional ISO 4217 currency code (
TYPE:
|
language
|
Optional BCP-47 language code (
TYPE:
|
country
|
Optional ISO 3166-1 alpha-2 country code (
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
list[FlightResult | tuple[FlightResult, ...]] | None
|
For one-way trips, a list of :class: |
list[FlightResult | tuple[FlightResult, ...]] | None
|
round-trip / multi-city, a list of tuples of |
list[FlightResult | tuple[FlightResult, ...]] | None
|
class: |
list[FlightResult | tuple[FlightResult, ...]] | None
|
when no results. |
| RAISES | DESCRIPTION |
|---|---|
ValueError
|
|
Exception
|
HTTP failure or unparseable response. |
Source code in fli/search/flights.py
216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 | |
FlightSearchFilters¶
A simplified interface for flight search parameters.
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
¶
airlines_exclude: list[Airline] | None = None
class-attribute
instance-attribute
¶
alliances: list[Alliance] | None = None
class-attribute
instance-attribute
¶
alliances_exclude: list[Alliance] | 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
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:
|
Source code in fli/models/google_flights/flights.py
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 | |
validate_segment_date_order() -> FlightSearchFilters
¶
Validate that each leg departs no earlier than the one before it.
Nothing else catches a return date that precedes the outbound date: it used to be rejected only when it happened to land in the past, so a backwards round trip in the future was passed straight to Google.
Source code in fli/models/google_flights/flights.py
Date Search¶
Search functionality for finding the cheapest dates to fly.
SearchDates¶
fli.search.dates.SearchDates()
¶
Date-based flight search implementation.
This class provides methods to search for flight prices across a date range, useful for finding the cheapest dates to fly.
Initialize the search client for date-based searches.
Source code in fli/search/dates.py
MAX_DAYS_PER_SEARCH = 61
class-attribute
instance-attribute
¶
client = get_client()
instance-attribute
¶
sparse_passenger_mix: bool
property
¶
Whether the sparse-passenger-mix warning fired on the most recent search().
True exactly when the empty sweep this instance last returned (or
raised out of) was consistent with Google's client-side pricing gap
for children/infants rather than the caller's own filters — see
_warn_if_sparse_passenger_mix. Reset to False at the start of
every :meth:search call, including ones that raise, so a stale
True from an earlier call never leaks into a later one.
Reflects only the last completed :meth:search call on this
instance and is not meant for instances shared across concurrent
searches — two overlapping :meth:search calls on one
SearchDates would race on this attribute. Not currently read by
the MCP server or CLI (unlike SearchFlights.sparse_passenger_mix);
exposed here for symmetry with the warning this class already logs.
__parse_currency(item: list[list] | list | None) -> str | None
staticmethod
¶
Parse the returned currency code from the API response.
Source code in fli/search/dates.py
__parse_date(item: list[list] | list | None, trip_type: TripType) -> tuple[datetime] | tuple[datetime, datetime]
staticmethod
¶
Parse date data from the API response.
| PARAMETER | DESCRIPTION |
|---|---|
item
|
Raw date data from the API response
TYPE:
|
trip_type
|
Trip type (one-way or round-trip)
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
tuple[datetime] | tuple[datetime, datetime]
|
Tuple of datetime objects |
Source code in fli/search/dates.py
__parse_price(item: list[list] | list | None) -> float | None
staticmethod
¶
Parse price data from the API response.
| PARAMETER | DESCRIPTION |
|---|---|
item
|
Raw price data from the API response
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
float | None
|
Float price value if valid, None if invalid or missing |
Source code in fli/search/dates.py
search(filters: DateSearchFilters, currency: str | None = None, language: str | None = None, country: str | None = None) -> list[DatePrice] | None
¶
Search for flight prices across a date range and search parameters.
| PARAMETER | DESCRIPTION |
|---|---|
filters
|
Search parameters including date range, airports, and preferences
TYPE:
|
currency
|
Optional ISO 4217 currency code (e.g.
TYPE:
|
language
|
Optional BCP-47 language code passed via the
TYPE:
|
country
|
Optional ISO 3166-1 alpha-2 country code passed via the
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
list[DatePrice] | None
|
List of DatePrice objects containing date and price pairs, or None if no results |
| RAISES | DESCRIPTION |
|---|---|
ValueError
|
The range covers more than :data: |
SearchClientError
|
Every date in the range failed to load, or
nothing priced because at least half the attempted dates
never loaded — see :meth: |
Notes
Every date in the range costs its own search-page fetch — the page
serves no calendar grid — so the range is capped at
:data:MAX_DATES_PER_SEARCH dates. Internally it is still split
into MAX_DAYS_PER_SEARCH-day chunk filters, but all dates are
priced by a single flat parallel map.
Source code in fli/search/dates.py
263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 | |
DatePrice¶
fli.search.dates.DatePrice
¶
Examples¶
Basic Flight Search¶
from fli.search import SearchFlights
from fli.models import Airport, SeatType, FlightSearchFilters, FlightSegment, PassengerInfo
# Create filters
filters = FlightSearchFilters(
passenger_info=PassengerInfo(adults=1),
flight_segments=[
FlightSegment(
departure_airport=[[Airport.JFK, 0]],
arrival_airport=[[Airport.LAX, 0]],
travel_date="2026-06-01",
)
],
seat_type=SeatType.ECONOMY
)
# Search flights
search = SearchFlights()
results = search.search(filters)
Date Range Search¶
from fli.search import SearchDates
from fli.models import DateSearchFilters, Airport, FlightSegment, PassengerInfo
# Create filters
filters = DateSearchFilters(
passenger_info=PassengerInfo(adults=1),
flight_segments=[
FlightSegment(
departure_airport=[[Airport.JFK, 0]],
arrival_airport=[[Airport.LAX, 0]],
travel_date="2026-06-01",
)
],
from_date="2026-06-01",
to_date="2026-06-30"
)
# Search dates
search = SearchDates()
results = search.search(filters)
Running These Examples¶
You can find complete, runnable versions of these examples in the examples/python/ directory:
# Run with uv (recommended)
uv run python examples/python/basic_one_way_search.py
uv run python examples/python/date_range_search.py
# Or install dependencies and run directly
pip install pydantic curl_cffi httpx
python examples/python/basic_one_way_search.py
For more advanced examples, see:
examples/python/complex_flight_search.py- Advanced filteringexamples/python/result_processing.py- Data analysisexamples/python/error_handling_with_retries.py- Robust error handling
HTTP Client¶
The underlying HTTP client used for API requests.
Client¶
fli.search.client.Client(calls_per_second: int = DEFAULT_CALLS_PER_SECOND)
¶
HTTP client with built-in rate limiting, retry and user agent impersonation functionality.
Sessions are kept per-thread because curl_cffi.requests.Session is
not thread-safe — concurrent post/get calls from different
threads each get their own libcurl handle. The shared
:class:TokenBucketRateLimiter enforces the global 10 req/sec budget
across all of them.
Initialise the shared rate limiter and per-thread session storage.
Source code in fli/search/client.py
DEFAULT_HEADERS = {'content-type': 'application/x-www-form-urlencoded;charset=UTF-8'}
class-attribute
instance-attribute
¶
__del__()
¶
Best-effort cleanup of the main-thread session (others die with their thread).
Source code in fli/search/client.py
get(url: str, **kwargs: Any) -> Response
¶
Make a rate-limited GET request with automatic retries.
Source code in fli/search/client.py
post(url: str, **kwargs: Any) -> Response
¶
Make a rate-limited POST request with automatic retries.