Google Flights price scraper. Search flights programmatically via the same RPC endpoints the web app uses.
Project description
swoop
Google Flights price scraper. Search flights programmatically using the same RPC endpoints the Google Flights web app uses.
from swoop import search
results = search("JFK", "LAX", "2026-06-01")
for flight in results.best:
airline = ", ".join(flight.airline_names)
print(f"${flight.price} — {airline}")
Install
pip install swoop-flights
# With CLI (adds `swoop` command)
pip install swoop-flights[cli]
CLI
Search Google Flights directly from the terminal:
# 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
# JSON output for piping
swoop search JFK LAX 2026-06-15 -o json -q | jq '.results[0].price_usd'
# CSV for spreadsheets
swoop search JFK LAX 2026-06-15 -o csv -q > flights.csv
# Look up a specific flight
swoop flight DL2300 -f JFK -t LAX -d 2026-06-15
# See fare tiers for search result #1
swoop book 1 JFK LAX 2026-06-15
# Filter by airline, time window
swoop search JFK LAX 2026-06-15 -a DL -a UA --depart-after 8 --depart-before 14
Output formats: table (default, colored), json, csv, brief. Use --help on any subcommand for all options.
Python API
One-way search
from swoop import search
results = search("SFO", "JFK", "2026-06-15")
# results.best — top-ranked flights
# results.other — remaining flights
for flight in results.best:
print(f"${flight.price}")
print(f" {flight.departure_airport} → {flight.arrival_airport}")
print(f" {flight.airline_names}, {flight.stop_count} stops")
print(f" {flight.travel_time} min total")
Roundtrip search
results = search("SFO", "JFK", "2026-06-15", return_date="2026-06-22")
# Price in results is the 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
)
Time window filtering
results = search(
"JFK", "LHR", "2026-06-15",
earliest_departure=8, # depart after 8am
latest_departure=14, # depart before 2pm
)
Booking details (fare options)
from swoop import search, get_booking_results
results = search("JFK", "LAX", "2026-06-15")
itinerary = results.best[0]
# Get fare tiers — just pass the itinerary
options = get_booking_results(itinerary)
for opt in options:
print(f"${opt.price} — {opt.brand_label} ({opt.fare_family})")
You can also pass a booking token string with explicit parameters:
options = get_booking_results(
itinerary.booking_token,
origin="JFK",
destination="LAX",
date="2026-06-15",
selected_legs=[
[
flight.departure_airport,
f"{flight.departure_date[0]}-{flight.departure_date[1]:02d}-{flight.departure_date[2]:02d}",
flight.arrival_airport,
None,
flight.airline,
flight.flight_number,
]
for flight in itinerary.flights
],
)
Retry and timeout
# Retry up to 3 times on HTTP 429 (rate limit) with exponential backoff
results = search("JFK", "LAX", "2026-06-15", retries=3, timeout=90)
# Same for booking results
options = get_booking_results(itinerary, retries=2, timeout=60)
Flight details
Each Itinerary contains detailed segment data:
results = search("JFK", "LAX", "2026-06-15")
for itinerary in results.best:
for flight in itinerary.flights:
print(f"{flight.airline} {flight.flight_number}")
print(f" {flight.departure_airport} → {flight.arrival_airport}")
print(f" Aircraft: {flight.aircraft}")
print(f" Legroom: {flight.legroom}")
if flight.co2_grams:
print(f" CO₂: {flight.co2_grams}g")
if itinerary.carbon_emissions:
ce = itinerary.carbon_emissions
print(f" Route emissions: {ce.difference_percent}% vs typical")
if results.price_range:
print(f" Price range: ${results.price_range.low}–${results.price_range.high}")
Error handling
from swoop import search, SwoopHTTPError, SwoopRateLimitError, SwoopParseError
try:
results = search("JFK", "LAX", "2026-06-15")
except SwoopRateLimitError:
print("Rate limited — wait a few minutes")
except SwoopHTTPError as e:
print(f"HTTP {e.status_code}")
except SwoopParseError as e:
print(f"Parse error: {e}")
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 |
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 |
include_basic_economy |
bool |
False |
Include basic economy fares |
timeout |
int |
90 |
HTTP timeout in seconds |
retries |
int |
0 |
Retries on HTTP 429 with exponential backoff |
Returns SearchResult | None. None means no results found.
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, fare_family, etc. BookingOption supports both attribute access (opt.price) and dict-style access (opt["price"], opt.get("price")).
Result types
SearchResult—best: list[Itinerary],other: list[Itinerary],price_range: PriceRange | NoneItinerary— Full itinerary withprice,flights,layovers,travel_time,booking_token,carbon_emissionsFlight— Segment details:airline,flight_number,aircraft,legroom,co2_grams,amenitiesLayover— Stop info:minutes, airports,is_overnightCarbonEmissions—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 |
Pricing notes
Use itinerary.price to get the USD price as an integer.
By default, search() excludes basic economy fares so prices reflect Main Cabin. Pass include_basic_economy=True to include them:
results = search("JFK", "LAX", "2026-06-15", include_basic_economy=True)
How it works
Swoop uses Google Flights' internal GetShoppingResults and GetBookingResults RPC endpoints — the same ones the web app calls when you search for flights. Requests are serialized as nested JSON payloads and sent via HTTP POST with browser impersonation (via primp).
Responses are decoded from nested list structures into typed Python dataclasses.
Dependencies
- primp — HTTP client with browser TLS impersonation
- protobuf — Protocol buffer serialization
- click — CLI framework (optional,
[cli]extra) - rich — Terminal formatting (optional,
[cli]extra)
License
MIT
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file swoop_flights-0.2.0.tar.gz.
File metadata
- Download URL: swoop_flights-0.2.0.tar.gz
- Upload date:
- Size: 33.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2ee03533e866d2c2b4b64176b3cb649910cf76e1a025ae7e2e7eab933c8c64c3
|
|
| MD5 |
3df34c0f771dfa19a407ee6e5051dda8
|
|
| BLAKE2b-256 |
37f15ca9e0b63054fad05a6e1322a4fd7ace0693e46a8759066fb6ed024a5ee1
|
File details
Details for the file swoop_flights-0.2.0-py3-none-any.whl.
File metadata
- Download URL: swoop_flights-0.2.0-py3-none-any.whl
- Upload date:
- Size: 39.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
916875a2ab7c4d0999204ea9b60327504a109b092243caf11653fbfc701c98ac
|
|
| MD5 |
3daba8840662e39cc084326c5a5e6223
|
|
| BLAKE2b-256 |
28576995819ae206daaaba4d586ff64954bec7a06087172496f946014771a482
|