Skip to main content

The fast, robust, strongly-typed Google Flights scraper (API) implemented in Python.

Project description

✈️ fast-flights (v3.1.0)

The fast and strongly-typed Google Flights scraper (API) implemented in Python. Based on Base64-encoded Protobuf string.

Documentation (v2)IssuesPyPi (v3.0rc0)

$ pip install fast-flights

At a glance

from fast_flights import (
    FlightQuery,
    Passengers, 
    create_query, 
    get_flights
)

query = create_query(
    flights=[
        FlightQuery(
            date="YYYY-MM-DD",   # change the date
            from_airport="MYJ",  # three-letter name
            to_airport="TPE",    # three-letter name
        ),
    ],
    seat="economy",  # business/economy/first/premium-economy
    trip="one-way",  # multi-city/one-way/round-trip
    passengers=Passengers(adults=1),
    language="zh-TW",
)
res = get_flights(query)

Round-trip (return flights)

For round-trip searches, Google Flights uses a two-step flow: first you query outbound flights, then you select one and query return flights. fast-flights now supports this:

from fast_flights import (
    FlightQuery, Passengers,
    create_query, get_flights,
    select_flight, get_return_flights, # new!
)

# Step 1 – query outbound flights
query = create_query(
    flights=[
        FlightQuery(date="2026-03-15", from_airport="CDG", to_airport="TPE"),
        FlightQuery(date="2026-03-19", from_airport="TPE", to_airport="CDG"),
    ],
    seat="economy",
    trip="round-trip",
    passengers=Passengers(adults=1),
)
outbound = get_flights(query)

# Step 2 – pick a flight, then query return flights
return_query = select_flight(query, outbound[0])
returning = get_return_flights(return_query)

Each outbound result carries an internal session token (select_token) that links to the available return options. The select_flight() helper wraps it into a ReturnQuery that get_return_flights() can consume.

Note: This also works with integrations (e.g. get_return_flights(rq, integration=BrightData())).

Multi-city (N legs)

For multi-city/multi-leg trips, you have two options:

Option 1: get_flights_multicity_chained (Recommended) Makes a single call to Google's internal GetShoppingResults RPC. Google's response already contains all available first-leg flight options, each priced as the total cost of the entire multi-city trip — no sequential chaining required.

from fast_flights import FlightQuery, get_flights_multicity_chained

legs = [
    FlightQuery(date="2026-03-15", from_airport="SIN", to_airport="TPE"),
    FlightQuery(date="2026-03-21", from_airport="TPE", to_airport="NRT"),
    FlightQuery(date="2026-03-24", from_airport="NRT", to_airport="TPE"),
    FlightQuery(date="2026-03-27", from_airport="TPE", to_airport="SIN"),
]

result = get_flights_multicity_chained(legs)

# All legs share the same flights list and total_price
for flight in result[0].flights:
    print(f"{flight.airlines} — total trip: ${flight.price}")

⚙️ Technical Note: fast_flights calls Google's hidden GetShoppingResults HTTP RPC endpoint with all legs in a single f.req payload. The response includes complete flight options with per-option total-trip prices parsed directly from the JSON payload — no Playwright or Selenium required.

Caveats:

  • The flights field on each MulticityLegChained reflects first-leg options only (e.g. SIN→TPE). Subsequent legs' specific flight times are encoded in each result's select_token for further chaining.
  • Integrations / Fallbacks are not supported: get_flights_multicity_chained uses a primp HTTP session and cannot use BrightData or Playwright integrations.

Option 2: Manual Selection (fine-grained control) You can manually chain select_flight() calls to step through each leg yourself:

query = create_query(
    flights=[
        FlightQuery(date="2026-03-15", from_airport="SIN", to_airport="TPE"),
        FlightQuery(date="2026-03-21", from_airport="TPE", to_airport="NRT"),
        FlightQuery(date="2026-03-24", from_airport="NRT", to_airport="TPE"),
        FlightQuery(date="2026-03-27", from_airport="TPE", to_airport="SIN"),
    ],
    seat="economy",
    trip="multi-city",
    passengers=Passengers(adults=1),
)

# Leg 1
leg1 = get_flights(query)
rq = select_flight(query, leg1[0])

# Leg 2
leg2 = get_return_flights(rq)
rq = select_flight(rq, leg2[0])       # pass ReturnQuery to chain

# Leg 3
leg3 = get_return_flights(rq)
rq = select_flight(rq, leg3[0])

# Leg 4
leg4 = get_return_flights(rq)

Integrations

If you'd like, you can use integrations.

Bright data:

from fast_flights import get_flights
from fast_flights.integrations import BrightData

get_flights(..., integration=BrightData())

What's new

  • v3.4.0Native Multi-City Support in get_flights() using GetShoppingResults RPC. get_flights_multicity_chained makes a single API call to fetch all first-leg options with full trip prices — no sequential chaining needed.
  • v3.1.0Round-trip return flights and multi-city (N-leg) support via select_flight() + get_return_flights().
  • v3.0rc0 – Uses Javascript data instead.
  • v2.2 – Now supports local playwright for sending requests.
  • v2.0 – New (much more succinct) API, fallback support for Playwright serverless functions, and documentation!

Contributing

Contributing is welcomed! A few notes though:

  1. please no ai slop. i am not reading all that.
  2. one change at a time. what your title says is what you've changed.
  3. no new dependencies unless it's related to the core parsing.
  4. really, i cant finish reading all of them, i have other projects and life to do. really sorry

How it's made

The other day, I was making a chat-interface-based trip recommendation app and wanted to add a feature that can search for flights available for booking. My personal choice is definitely Google Flights since Google always has the best and most organized data on the web. Therefore, I searched for APIs on Google.

🔎 Search
google flights api

The results? Bad. It seems like they discontinued this service and it now lives in the Graveyard of Google.

🧏‍♂️ duffel.com
Google Flights API: How did it work & what happened to it?

The Google Flights API offered developers access to aggregated airline data, including flight times, availability, and prices. Over a decade ago, Google announced the acquisition of ITA Software Inc. which it used to develop its API. However, in 2018, Google ended access to the public-facing API and now only offers access through the QPX enterprise product.

That's awful! I've also looked for free alternatives but their rate limits and pricing are just 😬 (not a good fit/deal for everyone).


However, Google Flights has their UI – flights.google.com. So, maybe I could just use Developer Tools to log the requests made and just replicate all of that? Undoubtedly not! Their requests are just full of numbers and unreadable text, so that's not the solution.

Perhaps, we could scrape it? I mean, Google allowed many companies like Serpapi to scrape their web just pretending like nothing happened... So let's scrape our own.

🔎 Search
google flights api scraper pypi

Excluding the ones that are not active, I came across hugoglvs/google-flights-scraper on Pypi. I thought to myself: "aint no way this is the solution!"

I checked hugoglvs's code on GitHub, and I immediately detected "playwright," my worst enemy. One word can describe it well: slow. Two words? Extremely slow. What's more, it doesn't even run on the 🗻 Edge because of configuration errors, missing libraries... etc. I could just reverse try.playwright.tech and use a better environment, but that's just too risky if they added Cloudflare as an additional security barrier 😳.

Life tells me to never give up. Let's just take a look at their URL params...

https://www.google.com/travel/flights/search?tfs=CBwQAhoeEgoyMDI0LTA1LTI4agcIARIDVFBFcgcIARIDTVlKGh4SCjIwMjQtMDUtMzBqBwgBEgNNWUpyBwgBEgNUUEVAAUgBcAGCAQsI____________AZgBAQ&hl=en
Param Content My past understanding
hl en Sets the language.
tfs CBwQAhoeEgoyMDI0LTA1LTI4agcIARID… What is this???? 🤮🤮

I removed the ?tfs= parameter and found out that this is the control of our request! And it looks so base64-y.

If we decode it to raw text, we can still see the dates, but we're not quite there — there's too much unwanted Unicode text.

Or maybe it's some kind of a data-storing method Google uses? What if it's something like JSON? Let's look it up.

🔎 Search
google's json alternative

🐣 Result
Solution: The Power of Protocol Buffers

LinkedIn turned to Protocol Buffers, often referred to as protobuf, a binary serialization format developed by Google. The key advantage of Protocol Buffers is its efficiency, compactness, and speed, making it significantly faster than JSON for serialization and deserialization.

Gotcha, Protobuf! Let's feed it to an online decoder and see how it does:

🔎 Search
protobuf decoder

🐣 Result
protobuf-decoder.netlify.app

I then pasted the Base64-encoded string to the decoder and no way! It DID return valid data!

annotated, Protobuf Decoder screenshot

I immediately recognized the values — that's my data, that's my query!

So, I wrote some simple Protobuf code to decode the data.

syntax = "proto3"

message Airport {
    string name = 2;
}

message FlightInfo {
    string date = 2;
    Airport dep_airport = 13;
    Airport arr_airport = 14;
}

message GoogleSucks {
    repeated FlightInfo = 3;
}

It works! Now, I won't consider myself an "experienced Protobuf developer" but rather a complete beginner.

I have no idea what I wrote but... it worked! And here it is, fast-flights.


(c) 2024-2026 AWeirdDev, and all the awesome people

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

faster_flights-3.4.0.tar.gz (24.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

faster_flights-3.4.0-py3-none-any.whl (24.2 kB view details)

Uploaded Python 3

File details

Details for the file faster_flights-3.4.0.tar.gz.

File metadata

  • Download URL: faster_flights-3.4.0.tar.gz
  • Upload date:
  • Size: 24.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for faster_flights-3.4.0.tar.gz
Algorithm Hash digest
SHA256 b7f74b8306130aef987e10f461f5f2b7b099ef209265a85fc63c954d5377e803
MD5 8284cb1b50cd589d322980eb5f62816e
BLAKE2b-256 ffe6459f690415387bcfb51335f92691fe05b22f25987853d88a32b7484c71f6

See more details on using hashes here.

Provenance

The following attestation bundles were made for faster_flights-3.4.0.tar.gz:

Publisher: workflow.yml on jamexhuang/flights

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file faster_flights-3.4.0-py3-none-any.whl.

File metadata

  • Download URL: faster_flights-3.4.0-py3-none-any.whl
  • Upload date:
  • Size: 24.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for faster_flights-3.4.0-py3-none-any.whl
Algorithm Hash digest
SHA256 301519fdb17a7fed1606a2484671fd1479aed409ae98c6417c46f01dc59c064b
MD5 6d7feffb83124dedb13c6a5afcfe344f
BLAKE2b-256 8640e1913cf9b75813427283e074c8b54ca7f3e2e9fe34f3534932f7669bb6a3

See more details on using hashes here.

Provenance

The following attestation bundles were made for faster_flights-3.4.0-py3-none-any.whl:

Publisher: workflow.yml on jamexhuang/flights

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page