Skip to content
gobeyondptyPublic
forked from saraswatayu/swoop

About

Search Google Flights programmatically. Real prices, typed results, no API key.

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

 
 

Repository files navigation

swoop

PyPI Python License CI

Search Google Flights and Google Travel Hotels programmatically. Real prices, typed results, no API key.

from swoop import search

results = search("JFK", "LAX", "2026-06-15")
for option in results.results[:3]:
    print(f"${option.price} — {', '.join(option.legs[0].itinerary.airline_names)}")

Note

swoop is not affiliated with Google. It calls undocumented RPC endpoints that can change without notice.

swoop calls Google Flights' internal GetShoppingResults and GetBookingResults RPC endpoints, plus Google Travel Hotels' internal TravelFrontendUi batchexecute endpoints. Requests use TLS fingerprint impersonation via primp to match a real browser session. Responses are deeply nested lists (matching internal protobuf-style schemas) decoded into typed Python dataclasses.

Perch uses swoop in production to monitor booked flights for price drops, saving users an average of $247 per trip.

Landing Page · How I Built This


Install

pip install swoop-flights

# With CLI (adds `swoop` command)
pip install swoop-flights[cli]

CLI

swoop search JFK LAX 2026-06-15

# Search flights
swoop search JFK LAX 2026-06-15

# Nonstop, sorted by price
swoop search JFK LAX 2026-06-15 --nonstop --sort cheapest

# Roundtrip, business class
swoop search JFK LAX 2026-06-15 -r 2026-06-22 --cabin business

# Official multi-city search
swoop search --leg JFK LAX 2026-06-15 --leg LAX SFO 2026-06-18 --leg SFO SEA 2026-06-21

# Bookable fare for a known flight
swoop price JFK LAX --depart 2026-06-15 DL2300

# Show copy/paste price commands for displayed rows
swoop search JFK LAX 2026-06-15 --show-price-commands

# Search hotels
swoop hotels "New York" 2026-06-01 2026-06-03

# Filter/sort returned hotel cards
swoop hotels "New York" 2026-06-01 2026-06-03 --sort rating --min-rating 4 --max-total-price 500

# Server-side hotel filters captured from Google Hotels
swoop hotels "New York" 2026-06-01 2026-06-03 --amenity free-wifi --free-cancellation --property-type hostels

# Enrich broad hotel cards with pricing/review tokens
swoop hotels "New York" 2026-06-01 2026-06-03 --include-booking-tokens --token-enrichment-limit 3

# Script-stable pricing via selector
SELECTOR=$(swoop search JFK LAX 2026-06-15 -o json -q | jq -r '.results[0].selector')
swoop price --selector "$SELECTOR"
More CLI Examples
# Roundtrip shorthand pricing
swoop price JFK LAX --depart 2026-06-15 DL2300 --return 2026-06-22 DL2301

# Explicit leg pricing (supports 3+ legs)
swoop price --leg JFK LAX 2026-06-15 DL2300 --leg LAX SFO 2026-06-18 UA544 --leg SFO SEA 2026-06-21 AS331

# CSV for spreadsheets
swoop search JFK LAX 2026-06-15 -o csv -q > flights.csv

# Price CSV (one row per booking option, with seller and booking_url)
swoop price JFK LAX --depart 2026-06-15 DL2300 -o csv -q > fares.csv

# Search JSON for piping
swoop search JFK LAX 2026-06-15 -o json -q | jq '.results[0] | {selector, price, legs}'

# Filter by airline and time window
swoop search JFK LAX 2026-06-15 -a DL -a UA --depart-after 8 --depart-before 14

# Surface RPC debug logging on stderr (URLs, response sizes, retries)
swoop search JFK LAX 2026-06-15 --verbose

# Discover deals from an airport
swoop deals JFK

# Multi-origin (NYC) — single call, mixed origins
swoop deals JFK,LGA,EWR

# Parallel per-origin fetch, deduped by trip identity
swoop deals JFK,LGA,EWR --per-origin

# Europe-only summer deals, 5-10 day trips, under $700, 40%+ off
swoop deals JFK --region europe \
    --depart-window 2026-06-01,2026-08-31 \
    --trip-length 5-10 --max-price 700 --min-discount 40

# Delta-only deals
swoop deals JFK -a DL

# Hotel provider prices and reviews from a booking token
TOKEN=$(swoop hotels "HI New York City Hostel" 2026-06-01 2026-06-03 -o json -q | jq -r '.hotels[0].booking_token')
swoop hotel-prices "$TOKEN" --query "HI New York City Hostel" --check-in 2026-06-01 --check-out 2026-06-03
swoop hotel-reviews "$TOKEN"

Run swoop search --help or swoop hotels --help for all options.

Tip

Search shows shopping totals for browsing. Use --show-price-commands for copy/paste swoop price --selector ... commands in human output, or use selector from JSON with swoop price --selector ... in scripts.

Shell Completion

# bash (~/.bashrc)
eval "$(_SWOOP_COMPLETE=bash_source swoop)"

# zsh (~/.zshrc)
eval "$(_SWOOP_COMPLETE=zsh_source swoop)"

# fish (~/.config/fish/config.fish)
_SWOOP_COMPLETE=fish_source swoop | source

After reloading your shell, swoop <TAB>, swoop search --<TAB>, and -o <TAB> will autocomplete.

Python API

One-Way Search

from swoop import search

results = search("SFO", "JFK", "2026-06-15")

for option in results.results[:3]:
    print(f"${option.price}")
    for leg in option.legs:
        itinerary = leg.itinerary
        if itinerary is None:
            continue
        print(f"  {leg.origin} -> {leg.destination}")
        print(f"  {itinerary.airline_names}, {itinerary.stop_count} stops")
        print(f"  {itinerary.travel_time} min")

print(results.is_complete)

search() and search_legs() return shopping totals. Use check_price(), price_legs(), or price_selector() when you need the bookable fare for one chosen itinerary.

Quick discovery for any multi-bound trip contains first-bound details and an estimated whole-trip price, with option.is_resolved=False. Use expand_legs=True (CLI --expand-legs) for exact outbound/return or open-jaw combinations before calling price_selector(). Expansion requires follow-up requests and returns a bounded set controlled by max_results, beam_width, and time_budget; result.is_complete=False indicates incomplete exploration; result.truncation_reasons explains which limit or failure reduced coverage. Searches request the wider Google list by default (show_all_results=True). Use search_next_leg(option.selector) to choose any discovered flight and retrieve all compatible next-bound choices without expanding other outbounds. Repeat until is_resolved=True, then price that exact selector. The CLI supports swoop next --selector SEL. Wider coverage does not guarantee every airline or flight is listed by Google. The discovery price is never an outbound-only fare and must not be added to a return price.

Each price-check call costs ~2 RPCs (one search, one booking lookup) — including one-way trips, so PriceResult.booking_options is populated and booking_url is available. For high-volume scoring of many fares, expect rate-limit pressure to scale accordingly; increase retries or pace your calls.

More Examples

Price Check for a Specific Flight

from swoop import check_price

result = check_price("DL2300", origin="JFK", destination="LAX", date="2026-06-15")
if result:
    print(f"${result.price}")

result = check_price(
    "DL2300", origin="JFK", destination="LAX", date="2026-06-15",
    return_flight_number="DL2301", return_date="2026-06-22",
)
if result:
    print(f"${result.price} roundtrip — {result.fare_brand}")
    for leg in result.resolved_legs:
        print(f"  {leg.flight_summary} {leg.origin}->{leg.destination} ({leg.selection})")

Price a Chosen Search Result by Selector

from swoop import price_selector, search

results = search("JFK", "LAX", "2026-06-15")
option = results.results[0]

price = price_selector(option.selector)
if price:
    print(f"${price.price} — {price.fare_brand}")

Leg-Based Search and Pricing

from swoop import SearchLeg, SelectedLeg, price_legs, search_legs

# Search with explicit legs (official entrypoint for multi-city)
results = search_legs([
    SearchLeg(date="2026-06-15", from_airport="JFK", to_airport="LAX"),
    SearchLeg(date="2026-06-18", from_airport="LAX", to_airport="SFO"),
    SearchLeg(date="2026-06-21", from_airport="SFO", to_airport="SEA"),
])

for option in results.results:
    print(option.selector, option.price)
    for leg in option.legs:
        print(f"  {leg.origin}->{leg.destination}")

# Price with explicit legs
result = price_legs([
    SelectedLeg(flight_number="DL2300", origin="JFK", destination="LAX", date="2026-06-15"),
    SelectedLeg(flight_number="UA544", origin="LAX", destination="SFO", date="2026-06-18"),
    SelectedLeg(flight_number="AS331", origin="SFO", destination="SEA", date="2026-06-21"),
])

Roundtrip Search

results = search("SFO", "JFK", "2026-06-15", return_date="2026-06-22")
for option in results.results:
    print(option.price)  # roundtrip total

Cabin Class and Filters

from swoop import search, SORT_CHEAPEST

results = search(
    "LAX", "NRT", "2026-06-15",
    cabin="business",       # economy, premium-economy, business, first
    max_stops=0,            # nonstop only
    sort=SORT_CHEAPEST,     # cheapest first
    airlines=["NH", "JL"],  # filter to specific carriers
    earliest_departure=8,   # depart after 8am
    latest_departure=14,    # depart before 2pm
)

Hotel Search, Prices, and Reviews

from swoop import hotel_prices, hotel_reviews, hotels

result = hotels("HI New York City Hostel", "2026-06-01", "2026-06-03")
hotel = result.hotels[0]

print(hotel.name, hotel.price, hotel.total_price, hotel.rating)

if hotel.booking_token:
    priced = hotel_prices(
        hotel.booking_token,
        query=hotel.name,
        check_in="2026-06-01",
        check_out="2026-06-03",
    )
    for provider in priced.providers:
        print(provider.name, provider.total_price, provider.url)

    reviews = hotel_reviews(hotel.booking_token)
    for review in reviews.reviews[:3]:
        print(review.source, review.rating, review.text)

Exact hotel queries can return a selected-hotel booking_token for provider prices and reviews. Broad destination queries return hotel cards when Google includes them in the first response, but may be marked is_complete=False because Google's follow-up list RPC can reject direct non-browser calls.

For broad destination searches, pass include_booking_tokens=True to run exact follow-up searches for each returned card and attach tokens when Google resolves the hotel. Use token_enrichment_limit to cap the number of extra requests.

Booking Details (Fare Options)

from swoop import search, get_booking_results

results = search("JFK", "LAX", "2026-06-15")
option = results.results[0]
itinerary = option.legs[0].itinerary

# Get fare tiers — just pass the itinerary
options = get_booking_results(itinerary)

for opt in options:
    label = opt.seller_name or "airline-direct"
    print(f"${opt.price} — {opt.brand_label} ({opt.fare_family}) via {label}")
    if opt.booking_url:
        print(f"  book at: {opt.booking_url}")

Each BookingOption is a booking channel, not just a fare tier: the operating airline (is_airline_direct) plus any OTAs Google offers (Expedia, FlightHub, …), each with its own seller_code and booking_url. If every option shares one seller, the itinerary is either genuinely airline-direct or its OTAs are gated behind Google's session-trust signals (signed-in account, residential IP, a BotGuard request signature) that swoop does not send. swoop returns what an unauthenticated client sees, which is the same list a signed-out browser gets — so one seller does not always mean "no OTAs exist." See examples/booking_options.py for splitting channels and where OTAs show up.

Deals Discovery

deals() is the third primitive: instead of "what flights from A to B?" (search()) or "how much for this exact flight?" (check_price()), it answers "where's cheap from here right now?"

from swoop import deals, search_deal, price_deal, Region

# Top 30 deals from JFK (excluding basic economy by default)
result = deals("JFK")
for deal in result.deals[:5]:
    print(f"{deal.destination_city:25s} ${deal.price}  {deal.discount_pct}% off")

# Filter by region, budget, trip length, discount
result = deals(
    "JFK",
    region=Region.EUROPE,
    max_price=700,
    trip_length=(5, 10),
    min_discount_pct=40,
    depart_window=("2026-06-01", "2026-08-31"),
)

# Multi-origin (NYC). Default: one RPC call. per_origin=True parallelizes
# and merges by fingerprint (cheaper variant wins on collision).
nyc = deals(["JFK", "LGA", "EWR"], per_origin=True)

# Bridge to swoop's existing pricing flow — no field-shuffling.
top = nyc.deals[0]
itineraries = search_deal(top)            # search() with the deal's route + dates + carriers
bookable = price_deal(top)                # cheapest matching itinerary, priced

Deals discovery is roundtrip-only — that's an upstream Google Flights product constraint, not a swoop limitation. The server ignores date and time-window slots in the payload; swoop applies those filters client-side over the 30 deals returned. For one-way exploration, use search() with an explicit destination.

Watching Deals Over Time

from swoop import deals, watch_deals, Region

# First run: every deal goes into diff.new and the cache is created.
# Subsequent runs: diff.new / diff.gone / diff.price_changes / diff.unchanged.
result = deals("JFK", region=Region.EUROPE, max_price=700)
diff = watch_deals(result, cache_path=".swoop-deals-cache.json")

for change in diff.price_changes:
    if change.delta < -50:          # $50+ drop from prior run
        d = change.current
        print(f"Price drop! {d.origin}->{d.destination}: "
              f"${change.prior.price} -> ${d.price}")

for new_deal in diff.new:
    print(f"New deal: {new_deal.destination_city} ${new_deal.price}")

Deal.fingerprint (origin + destination + dates + sorted airlines) identifies "the same trip" across runs — a price drop on the same trip shows up as a PriceChange rather than a new+gone pair. examples/deals_watcher.py is a runnable CLI version.

Tip

Google rate-limits aggressively. All RPC functions default to retries=2 with exponential backoff and jitter. Increase to retries=3 for extra resilience.

Destination Discovery (explore)

explore() is the fourth primitive. Where search() answers "what flights from A to B?", check_price() answers "how much for this exact flight?", and deals() answers "where's cheap right now?", explore() answers "where could I go from here?" — destination inspiration with images, coordinates, and Google's suggested dates, one-way or roundtrip.

from swoop import explore, price_explore, price_explore_all, Region

# Destinations you could fly to from JFK (roundtrip; one_way=True for one-way).
# Narrow client-side with the same filters deals() exposes:
result = explore("JFK", region=Region.EUROPE, trip_length=(5, 10))
for d in result.destinations[:5]:
    print(f"{d.destination_name:20} {d.destination}  {d.departure_date}")

# explore() is metadata-only (no price). Price a chosen destination on demand:
priced = price_explore(result.destinations[0])
if priced:
    print(f"${priced.price}")

# Or price a whole page of destinations concurrently (order-preserving;
# None where a destination can't be priced):
prices = price_explore_all(result.destinations[:10])

Note

The Explore RPC returns no price — it is a discovery/inspiration tool, not a pricing one. Use price_explore() (or price_explore_all() to price many in parallel) to price chosen destinations, or deals() for priced bargains. It supports one-way and roundtrip; the result set is geographic-scope-driven rather than a fixed count. The destinations, exclude_destinations, region, and trip_length filters narrow the set client-side (trip_length is roundtrip-only).

CLI: swoop explore JFK (add --one-way, --region europe, --trip-length 5-10, --destination LIS, -o json -q | jq).

Runnable Examples

Real-world patterns are in examples/:

How It Works

swoop reverse-engineers the FlightsFrontendService RPC interface that powers Google Flights and selected TravelFrontendUi batchexecute RPCs that power Google Travel Hotels. Search parameters are encoded as nested JSON arrays matching Google's internal protobuf-style schemas, then sent as HTTP POST requests. The HTTP client uses TLS fingerprint impersonation (via primp) so requests are indistinguishable from a real Chrome session.

Responses arrive as deeply nested list structures, no field names, just positional indices. swoop's decoder walks these structures and maps them to typed Python dataclasses (Itinerary, Segment, Layover, CarbonEmissions, etc.) with named attributes.

                                    ┌─────────────────────────────────────┐
                                    │         Google Flights              │
                                    │    FlightsFrontendService RPC       │
                                    └──────────┬──────────────────────────┘
                                               │
                                    protobuf response
                                    (nested arrays, no field names)
                                               │
┌──────────────┐   HTTP POST    ┌──────────────▼──────────────┐   typed    ┌──────────────────┐
│  search()    │──────────────▶ │     TLS fingerprint         │──────────▶│  Itinerary       │
│  price()     │  JSON-in-JSON  │     impersonation (primp)   │  Python   │  Segment         │
│  check_price │  URL-encoded   │                             │  dataclass│  Layover         │
└──────────────┘                └─────────────────────────────┘           │  CarbonEmissions │
                                                                          └──────────────────┘

For the full reverse-engineering story (744 lines of handmade schema, binary protobuf decoding, cabin class debugging), read How I Built This.

API Reference

search(origin, destination, date, **kwargs)

Search Google Flights and return a SearchResult.

Parameter Type Default Description
origin str required Origin IATA code
destination str required Destination IATA code
date str required Departure date (YYYY-MM-DD)
return_date str | None None Return date for roundtrip
cabin str "economy" economy, premium-economy, business, first
adults int 1 Number of adults
children int 0 Number of children (2–11)
infants_in_seat int 0 Number of infants in seat
infants_on_lap int 0 Number of infants on lap
max_stops int | None None None=any, 0=nonstop, 1=1 stop, 2=2 stops
sort int SORT_DEPARTURE_TIME Sort order constant
airlines list[str] | None None Filter by airline codes
flight_number str | None None Filter to a specific flight number; carrier is also added to the first-leg airline filter
include_basic_economy bool False Include basic economy fares (excluded by default so prices reflect Main Cabin)
timeout int 90 HTTP timeout in seconds
retries int 2 Retries on HTTP 429 with exponential backoff + jitter
country str | None None Two-letter country code for point of sale (e.g. "GB"). Controls currency and available fares
proxy str | None None Proxy URL for routing requests
max_results int | None None Max trip combinations for beam search (multi-leg only)
beam_width int | None None Candidate prefixes per stage (multi-leg only)
time_budget int | None None Seconds before beam search stops exploring (multi-leg only)

Returns SearchResult. Empty results mean no matches were found. Prices in search results are shopping totals.

search_legs(legs, **kwargs)

Search one or more explicit legs and return a trip-level SearchResult. This is the public multi-city entrypoint.

price_legs(legs, **kwargs)

Price one or more explicit legs and return PriceResult | None.

price_selector(selector, **kwargs)

Price a selected trip row by opaque selector and return PriceResult | None.

search_raw(origin, destination, date, **kwargs)

Low-level single-pass search escape hatch. Returns RawSearchResult with raw best and other itinerary buckets from one RPC pass.

check_price(flight_number, *, origin, destination, date, **kwargs)

Look up the current bookable fare for a specific flight. Optimized for the "what does flight X cost?" use case.

Parameter Type Default Description
flight_number str required Flight number (e.g. "DL2300")
origin str required Origin IATA code
destination str required Destination IATA code
date str required Departure date (YYYY-MM-DD)
return_flight_number str | None None Return flight number for roundtrip
return_date str | None None Return date for roundtrip
cabin str "economy" Cabin class
adults int 1 Number of adults
children int 0 Number of children (2–11)
infants_in_seat int 0 Number of infants in seat
infants_on_lap int 0 Number of infants on lap
include_basic_economy bool False Include basic economy fares
timeout int 90 HTTP timeout in seconds
retries int 2 Retries on HTTP 429
country str | None None Two-letter country code for point of sale
proxy str | None None Proxy URL for routing requests

Returns PriceResult | None. PriceResult has price, currency, fare_brand, is_basic_economy, is_estimate, booking_options, itinerary, resolved_legs, rpc_calls.

hotels(query, check_in, check_out, **kwargs)

Search Google Travel Hotels and return a HotelSearchResult.

Parameter Type Default Description
query str required Hotel or destination query
check_in str required Check-in date (YYYY-MM-DD)
check_out str required Check-out date (YYYY-MM-DD)
adults int 2 Adult guest count
child_ages list[int] | None None Ages for child guests
rooms int 1 Room count
currency str "USD" Requested ISO 4217 currency
sort_by str "default" Sort for returned cards: "default", "price", "total-price", "rating", "most-reviewed", "class", or "name"
min_price / max_price int | None None Nightly price bounds for returned cards
min_total_price / max_total_price int | None None Total stay price bounds for returned cards
min_rating float | None None Minimum Google rating
min_hotel_class int | None None Minimum hotel class/star count
hotel_classes list[int] | None None Exact server-side hotel class filters (2, 3, 4, 5)
amenities list[str] | None None Server-side amenity filters: "free_parking", "parking", "indoor_pool", "outdoor_pool", "pool", "fitness_center", "restaurant", "free_breakfast", "spa", "beach_access", "kid_friendly", "bar", "pet_friendly", "room_service", "free_wifi", "air_conditioned", "all_inclusive_available", "wheelchair_accessible", "ev_charger"
property_types list[str] | None None Server-side property type filters: "beach_hotels", "boutique_hotels", "hostels", "inns", "motels", "resorts", "spa_hotels", "bed_and_breakfasts", "other", "apartment_hotels"
brands list[str] | None None Captured server-side brand filters such as "hilton_honors", "marriott_bonvoy", "ihg_hotels_resorts", "hyatt", "choice_hotels", "extended_stay_america", "wyndham_hotels_resorts"
has_pool bool False Request hotels with a pool
free_cancellation bool False Request hotels with free cancellation offers
special_offers bool False Request hotels with special offers
eco_certified bool False Request eco-certified hotels
require_booking_token bool False Keep only returned cards with provider pricing/review tokens
include_booking_tokens bool False Run exact hotel follow-up searches to attach provider pricing/review tokens to broad result cards
token_enrichment_limit int | None None Maximum number of hotel cards to enrich when token enrichment is enabled
transport TransportConfig TransportConfig() HTTP transport configuration

Exact hotel queries can include Hotel.booking_token, which can be passed to hotel_prices() and hotel_reviews(). Swoop uses captured Google Hotels payloads for supported server-side controls (sort_by="price", sort_by="total-price", sort_by="rating", sort_by="most-reviewed", min_price, max_price, min_rating >= 3.5, min_hotel_class >= 4, hotel_classes, amenities, property_types, brands, has_pool, free_cancellation, special_offers, and eco_certified) and applies a final local filter/sort pass to the returned cards. Server-only filters do not fall back to unfiltered result pages if Google's filtered RPC fails. When is_complete=False, results should still be read as a filtered partial set.

hotel_prices(hotel_token, **kwargs)

Fetch provider offers for a selected hotel token. Returns a Hotel with providers: list[HotelProvider]. Pass the original hotel name in query when available so swoop can rehydrate Google's destination context before pricing.

hotel_reviews(hotel_token, **kwargs)

Fetch reviews for a selected hotel token. Returns HotelReviewsResult.

get_booking_results(itinerary_or_token, **kwargs)

Get fare options for a specific itinerary. Pass an Itinerary object directly, or a booking token string with explicit origin, destination, date, and selected_legs. Returns list[BookingOption] with price, brand_label, brand_code, is_basic, fare_family, rebookability_signal, plus seller fields seller_name, seller_code, booking_url, logo_url, and is_airline_direct for routing users to the actual booking page.

set_country(country_code)

Set the default country code for all subsequent requests. Controls point of sale, currency, and available fares. Pass None to clear.

set_proxy(proxy_url)

Set the default proxy URL for all subsequent requests. Pass None to clear.

Result Types

  • PriceResult — price: int, currency: str | None, fare_brand: str | None, is_basic_economy: bool, is_estimate: bool, booking_options: list[BookingOption], itinerary: Itinerary | None, resolved_legs: list[ResolvedLeg], rpc_calls: int
  • ResolvedLeg — flight_summary: str, origin: str, destination: str, date: str, itinerary: Itinerary | None, selection: str
  • SelectedLeg — flight_number: str, origin: str, destination: str, date: str
  • SearchLeg — date: str, from_airport: str, to_airport: str, max_stops: int | None, airlines: list[str] | None
  • HotelSearchResult — hotels: list[Hotel], query, destination_name, currency, is_complete
  • Hotel — Hotel card/details with hotel_id, name, prices, currency, rating, class, location, image, booking_token, and provider offers
  • HotelProvider — Provider offer with name, nightly/total prices, currency, booking URL, logo, room name, and cancellation signal
  • HotelReviewsResult — reviews: list[HotelReview], hotel_token
  • HotelReview — Review source, author, rating, max rating, relative date, text, and source URL
  • SearchResult — results: list[TripOption], price_range: PriceRange | None, is_complete: bool, currency: str | None
  • TripOption — selector: str, price: int | None, currency: str | None, legs: list[TripLeg], is_resolved: bool
  • TripLeg — origin: str, destination: str, date: str, itinerary: Itinerary | None
  • RawSearchResult — low-level best: list[Itinerary], other: list[Itinerary], price_range: PriceRange | None
  • Itinerary — Full itinerary with price, flights, layovers, travel_time, booking_token, carbon_emissions
  • Segment — Segment details: airline, flight_number, aircraft, legroom, co2_grams, amenities, seat_type, cabin_class
  • Layover — Stop info: minutes, airports, is_overnight
  • CarbonEmissions — this_flight_grams, typical_for_route_grams, difference_percent

Constants

Constant Value Description
SORT_TOP 1 Google's default ranking
SORT_CHEAPEST 2 Cheapest first
SORT_DEPARTURE_TIME 3 By departure time
SORT_ARRIVAL_TIME 4 By arrival time
SORT_DURATION 5 Shortest first

Error Handling

All exceptions inherit from SwoopError. Catch SwoopTransportError for request-send or response-body failures, SwoopRateLimitError for HTTP 429, SwoopHTTPError for other HTTP failures, SwoopParseError for response decoding issues, and SwoopUpstreamError for a structured upstream rejection (Google answers HTTP 200 with a gRPC ErrorResponse envelope) — distinct from a genuinely empty result, which returns empty rather than raising. On a priced result, PriceResult.is_estimate is True when the price is a search-derived shopping estimate rather than a confirmed bookable fare.

Roadmap

swoop is flights-forward. Adjacent Google Travel surfaces (e.g. hotels) ship when there's demand and a maintainer — vote on what's next →.

Contributing

Issues and pull requests welcome at github.com/saraswatayu/swoop.

Testing notes:

  • Push and PR CI runs the deterministic offline suite only: python -m pytest tests/ -v -m 'not live'
  • Benchmarks are opt-in and stay skipped in normal runs unless you pass --run-benchmarks (or use --benchmark-only).
  • Live Google canaries run separately in the live-canary workflow on a weekly schedule or by manual dispatch.
  • Mutation testing is available by manual dispatch in the mutation workflow and is scoped to _selection, _booking, decoder, and rpc.
  • Real-world bugs should be added to the incident regression bank in tests/incidents/manifest.json with a linked regression test or sanitized fixture.
  • When a live canary finds a useful new payload shape, promote it manually into tests/fixtures/contract_corpus_manifest.json and the tracked fixture corpus after review.

License

MIT

Separate tickets and self-transfer

Pass exclude_separate_tickets=True to search, search_legs, search_raw, check_price, price_legs, or deals to request Google Flights’ “Hide separate & self-transfer tickets” filter. The default remains False for compatibility. Trip selectors preserve this setting through search_next_leg and price_selector, including exact booking-option lookups. The same option on TFSData encodes the filter for search-page URLs. This does not exclude ordinary interline or codeshare itineraries sold on one ticket.

Segment cabin and amenity scope

Segment.cabin_class preserves the actual cabin at Google shopping segment index 16: 1 = economy, 2 = premium economy, 3 = business, 4 = first. Missing or unrecognized values remain None. A business search can include economy connecting segments; never apply the requested cabin to every segment. seat_type is a separate raw seat-product/quality indicator, not a cabin code.

Amenity flags are scoped to that segment's cabin. An absent Wi-Fi tier means unknown availability; it does not establish that Wi-Fi is unavailable or included in a fare. The raw tier should not be used to promise a free service without checking the applicable carrier/fare terms.

About

Search Google Flights programmatically. Real prices, typed results, no API key.

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages