faster-flights 3.4.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 fast-flights Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,282 @@
1
+ Metadata-Version: 2.4
2
+ Name: faster-flights
3
+ Version: 3.4.0
4
+ Summary: The fast, robust, strongly-typed Google Flights scraper (API) implemented in Python.
5
+ Keywords: flights,google,google-flights,scraper,protobuf,travel,trip,passengers,airport
6
+ Author-email: AWeirdDev <aweirdscratcher@gmail.com>
7
+ Requires-Python: >=3.10
8
+ Description-Content-Type: text/markdown
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Operating System :: OS Independent
12
+ License-File: LICENSE
13
+ Requires-Dist: primp
14
+ Requires-Dist: protobuf>=5.27.0
15
+ Requires-Dist: selectolax
16
+ Requires-Dist: playwright ; extra == "local"
17
+ Project-URL: Documentation, https://aweirddev.github.io/flights/
18
+ Project-URL: Issues, https://github.com/AWeirdDev/flights/issues
19
+ Project-URL: Source, https://github.com/AWeirdDev/flights
20
+ Provides-Extra: local
21
+
22
+ <div align="center">
23
+
24
+ # ✈️ fast-flights (v3.1.0)
25
+
26
+ The fast and strongly-typed Google Flights scraper (API) implemented in Python.
27
+ Based on Base64-encoded Protobuf string.
28
+
29
+ [**Documentation (v2)**](https://aweirddev.github.io/flights) • [Issues](https://github.com/AWeirdDev/flights/issues) • [PyPi (v3.0rc0)](https://pypi.org/project/fast-flights/3.0rc0/)
30
+
31
+ ```haskell
32
+ $ pip install fast-flights
33
+ ```
34
+
35
+ </div>
36
+
37
+ ## At a glance
38
+ ```python
39
+ from fast_flights import (
40
+ FlightQuery,
41
+ Passengers,
42
+ create_query,
43
+ get_flights
44
+ )
45
+
46
+ query = create_query(
47
+ flights=[
48
+ FlightQuery(
49
+ date="YYYY-MM-DD", # change the date
50
+ from_airport="MYJ", # three-letter name
51
+ to_airport="TPE", # three-letter name
52
+ ),
53
+ ],
54
+ seat="economy", # business/economy/first/premium-economy
55
+ trip="one-way", # multi-city/one-way/round-trip
56
+ passengers=Passengers(adults=1),
57
+ language="zh-TW",
58
+ )
59
+ res = get_flights(query)
60
+ ```
61
+
62
+ ## Round-trip (return flights)
63
+ 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:
64
+
65
+ ```python
66
+ from fast_flights import (
67
+ FlightQuery, Passengers,
68
+ create_query, get_flights,
69
+ select_flight, get_return_flights, # new!
70
+ )
71
+
72
+ # Step 1 – query outbound flights
73
+ query = create_query(
74
+ flights=[
75
+ FlightQuery(date="2026-03-15", from_airport="CDG", to_airport="TPE"),
76
+ FlightQuery(date="2026-03-19", from_airport="TPE", to_airport="CDG"),
77
+ ],
78
+ seat="economy",
79
+ trip="round-trip",
80
+ passengers=Passengers(adults=1),
81
+ )
82
+ outbound = get_flights(query)
83
+
84
+ # Step 2 – pick a flight, then query return flights
85
+ return_query = select_flight(query, outbound[0])
86
+ returning = get_return_flights(return_query)
87
+ ```
88
+
89
+ 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.
90
+
91
+ > **Note:** This also works with integrations (e.g. `get_return_flights(rq, integration=BrightData())`).
92
+
93
+ ## Multi-city (N legs)
94
+ For multi-city/multi-leg trips, you have two options:
95
+
96
+ **Option 1: `get_flights_multicity_chained` (Recommended)**
97
+ 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.
98
+
99
+ ```python
100
+ from fast_flights import FlightQuery, get_flights_multicity_chained
101
+
102
+ legs = [
103
+ FlightQuery(date="2026-03-15", from_airport="SIN", to_airport="TPE"),
104
+ FlightQuery(date="2026-03-21", from_airport="TPE", to_airport="NRT"),
105
+ FlightQuery(date="2026-03-24", from_airport="NRT", to_airport="TPE"),
106
+ FlightQuery(date="2026-03-27", from_airport="TPE", to_airport="SIN"),
107
+ ]
108
+
109
+ result = get_flights_multicity_chained(legs)
110
+
111
+ # All legs share the same flights list and total_price
112
+ for flight in result[0].flights:
113
+ print(f"{flight.airlines} — total trip: ${flight.price}")
114
+ ```
115
+
116
+ > **⚙️ Technical Note:**
117
+ > `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.
118
+ >
119
+ > *Caveats:*
120
+ > - 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.
121
+ > - **Integrations / Fallbacks are not supported**: `get_flights_multicity_chained` uses a `primp` HTTP session and cannot use BrightData or Playwright integrations.
122
+
123
+ **Option 2: Manual Selection (fine-grained control)**
124
+ You can manually chain `select_flight()` calls to step through each leg yourself:
125
+
126
+ ```python
127
+ query = create_query(
128
+ flights=[
129
+ FlightQuery(date="2026-03-15", from_airport="SIN", to_airport="TPE"),
130
+ FlightQuery(date="2026-03-21", from_airport="TPE", to_airport="NRT"),
131
+ FlightQuery(date="2026-03-24", from_airport="NRT", to_airport="TPE"),
132
+ FlightQuery(date="2026-03-27", from_airport="TPE", to_airport="SIN"),
133
+ ],
134
+ seat="economy",
135
+ trip="multi-city",
136
+ passengers=Passengers(adults=1),
137
+ )
138
+
139
+ # Leg 1
140
+ leg1 = get_flights(query)
141
+ rq = select_flight(query, leg1[0])
142
+
143
+ # Leg 2
144
+ leg2 = get_return_flights(rq)
145
+ rq = select_flight(rq, leg2[0]) # pass ReturnQuery to chain
146
+
147
+ # Leg 3
148
+ leg3 = get_return_flights(rq)
149
+ rq = select_flight(rq, leg3[0])
150
+
151
+ # Leg 4
152
+ leg4 = get_return_flights(rq)
153
+ ```
154
+
155
+ ## Integrations
156
+ If you'd like, you can use integrations.
157
+
158
+ Bright data:
159
+
160
+ ```python
161
+ from fast_flights import get_flights
162
+ from fast_flights.integrations import BrightData
163
+
164
+ get_flights(..., integration=BrightData())
165
+ ```
166
+
167
+ ## What's new
168
+ - `v3.4.0` – **Native 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.
169
+ - `v3.1.0` – **Round-trip return flights** and **multi-city (N-leg)** support via `select_flight()` + `get_return_flights()`.
170
+ - `v3.0rc0` – Uses Javascript data instead.
171
+ - `v2.2` – Now supports **local playwright** for sending requests.
172
+ - `v2.0` – New (much more succinct) API, fallback support for Playwright serverless functions, and [documentation](https://aweirddev.github.io/flights)!
173
+
174
+ ## Contributing
175
+ Contributing is welcomed! A few notes though:
176
+ 1. please no ai slop. i am not reading all that.
177
+ 2. one change at a time. what your title says is what you've changed.
178
+ 3. no new dependencies unless it's related to the core parsing.
179
+ 4. really, i cant finish reading all of them, i have other projects and life to do. really sorry
180
+
181
+ ***
182
+
183
+ ## How it's made
184
+
185
+ 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](https://flights.google.com) since Google always has the best and most organized data on the web. Therefore, I searched for APIs on Google.
186
+
187
+ > 🔎 **Search** <br />
188
+ > google flights api
189
+
190
+ The results? Bad. It seems like they discontinued this service and it now lives in the Graveyard of Google.
191
+
192
+ > <sup><a href="https://duffel.com/blog/google-flights-api" target="_blank">🧏‍♂️ <b>duffel.com</b></a></sup><br />
193
+ > <sup><i>Google Flights API: How did it work & what happened to it?</i></b>
194
+ >
195
+ > 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**.
196
+
197
+ 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).
198
+
199
+ <br />
200
+
201
+ However, Google Flights has their UI – [flights.google.com](https://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.
202
+
203
+ Perhaps, we could scrape it? I mean, Google allowed many companies like [Serpapi](https://google.com/search?q=serpapi) to scrape their web just pretending like nothing happened... So let's scrape our own.
204
+
205
+ > 🔎 **Search** <br />
206
+ > google flights ~~api~~ scraper pypi
207
+
208
+ Excluding the ones that are not active, I came across [hugoglvs/google-flights-scraper](https://pypi.org/project/google-flights-scraper) on Pypi. I thought to myself: "aint no way this is the solution!"
209
+
210
+ I checked hugoglvs's code on [GitHub](https://github.com/hugoglvs/google-flights-scraper), 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](https://try.playwright.tech) and use a better environment, but that's just too risky if they added Cloudflare as an additional security barrier 😳.
211
+
212
+ Life tells me to never give up. Let's just take a look at their URL params...
213
+
214
+ ```markdown
215
+ https://www.google.com/travel/flights/search?tfs=CBwQAhoeEgoyMDI0LTA1LTI4agcIARIDVFBFcgcIARIDTVlKGh4SCjIwMjQtMDUtMzBqBwgBEgNNWUpyBwgBEgNUUEVAAUgBcAGCAQsI____________AZgBAQ&hl=en
216
+ ```
217
+
218
+ | Param | Content | My past understanding |
219
+ |-------|---------|-----------------------|
220
+ | hl | en | Sets the language. |
221
+ | tfs | CBwQAhoeEgoyMDI0LTA1LTI4agcIARID… | What is this???? 🤮🤮 |
222
+
223
+ I removed the `?tfs=` parameter and found out that this is the control of our request! And it looks so base64-y.
224
+
225
+ 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.
226
+
227
+ 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.
228
+
229
+ > 🔎 **Search** <br />
230
+ > google's json alternative
231
+
232
+ > 🐣 **Result**<br />
233
+ > Solution: The Power of **Protocol Buffers**
234
+ >
235
+ > 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.
236
+
237
+ Gotcha, Protobuf! Let's feed it to an online decoder and see how it does:
238
+
239
+ > 🔎 **Search** <br />
240
+ > protobuf decoder
241
+
242
+ > 🐣 **Result**<br />
243
+ > [protobuf-decoder.netlify.app](https://protobuf-decoder.netlify.app)
244
+
245
+ I then pasted the Base64-encoded string to the decoder and no way! It DID return valid data!
246
+
247
+ ![annotated, Protobuf Decoder screenshot](https://github.com/AWeirdDev/flights/assets/90096971/77dfb097-f961-4494-be88-3640763dbc8c)
248
+
249
+ I immediately recognized the values — that's my data, that's my query!
250
+
251
+ So, I wrote some simple Protobuf code to decode the data.
252
+
253
+ ```protobuf
254
+ syntax = "proto3"
255
+
256
+ message Airport {
257
+ string name = 2;
258
+ }
259
+
260
+ message FlightInfo {
261
+ string date = 2;
262
+ Airport dep_airport = 13;
263
+ Airport arr_airport = 14;
264
+ }
265
+
266
+ message GoogleSucks {
267
+ repeated FlightInfo = 3;
268
+ }
269
+ ```
270
+
271
+ It works! Now, I won't consider myself an "experienced Protobuf developer" but rather a complete beginner.
272
+
273
+ I have no idea what I wrote but... it worked! And here it is, `fast-flights`.
274
+
275
+ ***
276
+
277
+ <div align="center">
278
+
279
+ (c) 2024-2026 AWeirdDev, and all the awesome people
280
+
281
+ </div>
282
+
@@ -0,0 +1,260 @@
1
+ <div align="center">
2
+
3
+ # ✈️ fast-flights (v3.1.0)
4
+
5
+ The fast and strongly-typed Google Flights scraper (API) implemented in Python.
6
+ Based on Base64-encoded Protobuf string.
7
+
8
+ [**Documentation (v2)**](https://aweirddev.github.io/flights) • [Issues](https://github.com/AWeirdDev/flights/issues) • [PyPi (v3.0rc0)](https://pypi.org/project/fast-flights/3.0rc0/)
9
+
10
+ ```haskell
11
+ $ pip install fast-flights
12
+ ```
13
+
14
+ </div>
15
+
16
+ ## At a glance
17
+ ```python
18
+ from fast_flights import (
19
+ FlightQuery,
20
+ Passengers,
21
+ create_query,
22
+ get_flights
23
+ )
24
+
25
+ query = create_query(
26
+ flights=[
27
+ FlightQuery(
28
+ date="YYYY-MM-DD", # change the date
29
+ from_airport="MYJ", # three-letter name
30
+ to_airport="TPE", # three-letter name
31
+ ),
32
+ ],
33
+ seat="economy", # business/economy/first/premium-economy
34
+ trip="one-way", # multi-city/one-way/round-trip
35
+ passengers=Passengers(adults=1),
36
+ language="zh-TW",
37
+ )
38
+ res = get_flights(query)
39
+ ```
40
+
41
+ ## Round-trip (return flights)
42
+ 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:
43
+
44
+ ```python
45
+ from fast_flights import (
46
+ FlightQuery, Passengers,
47
+ create_query, get_flights,
48
+ select_flight, get_return_flights, # new!
49
+ )
50
+
51
+ # Step 1 – query outbound flights
52
+ query = create_query(
53
+ flights=[
54
+ FlightQuery(date="2026-03-15", from_airport="CDG", to_airport="TPE"),
55
+ FlightQuery(date="2026-03-19", from_airport="TPE", to_airport="CDG"),
56
+ ],
57
+ seat="economy",
58
+ trip="round-trip",
59
+ passengers=Passengers(adults=1),
60
+ )
61
+ outbound = get_flights(query)
62
+
63
+ # Step 2 – pick a flight, then query return flights
64
+ return_query = select_flight(query, outbound[0])
65
+ returning = get_return_flights(return_query)
66
+ ```
67
+
68
+ 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.
69
+
70
+ > **Note:** This also works with integrations (e.g. `get_return_flights(rq, integration=BrightData())`).
71
+
72
+ ## Multi-city (N legs)
73
+ For multi-city/multi-leg trips, you have two options:
74
+
75
+ **Option 1: `get_flights_multicity_chained` (Recommended)**
76
+ 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.
77
+
78
+ ```python
79
+ from fast_flights import FlightQuery, get_flights_multicity_chained
80
+
81
+ legs = [
82
+ FlightQuery(date="2026-03-15", from_airport="SIN", to_airport="TPE"),
83
+ FlightQuery(date="2026-03-21", from_airport="TPE", to_airport="NRT"),
84
+ FlightQuery(date="2026-03-24", from_airport="NRT", to_airport="TPE"),
85
+ FlightQuery(date="2026-03-27", from_airport="TPE", to_airport="SIN"),
86
+ ]
87
+
88
+ result = get_flights_multicity_chained(legs)
89
+
90
+ # All legs share the same flights list and total_price
91
+ for flight in result[0].flights:
92
+ print(f"{flight.airlines} — total trip: ${flight.price}")
93
+ ```
94
+
95
+ > **⚙️ Technical Note:**
96
+ > `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.
97
+ >
98
+ > *Caveats:*
99
+ > - 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.
100
+ > - **Integrations / Fallbacks are not supported**: `get_flights_multicity_chained` uses a `primp` HTTP session and cannot use BrightData or Playwright integrations.
101
+
102
+ **Option 2: Manual Selection (fine-grained control)**
103
+ You can manually chain `select_flight()` calls to step through each leg yourself:
104
+
105
+ ```python
106
+ query = create_query(
107
+ flights=[
108
+ FlightQuery(date="2026-03-15", from_airport="SIN", to_airport="TPE"),
109
+ FlightQuery(date="2026-03-21", from_airport="TPE", to_airport="NRT"),
110
+ FlightQuery(date="2026-03-24", from_airport="NRT", to_airport="TPE"),
111
+ FlightQuery(date="2026-03-27", from_airport="TPE", to_airport="SIN"),
112
+ ],
113
+ seat="economy",
114
+ trip="multi-city",
115
+ passengers=Passengers(adults=1),
116
+ )
117
+
118
+ # Leg 1
119
+ leg1 = get_flights(query)
120
+ rq = select_flight(query, leg1[0])
121
+
122
+ # Leg 2
123
+ leg2 = get_return_flights(rq)
124
+ rq = select_flight(rq, leg2[0]) # pass ReturnQuery to chain
125
+
126
+ # Leg 3
127
+ leg3 = get_return_flights(rq)
128
+ rq = select_flight(rq, leg3[0])
129
+
130
+ # Leg 4
131
+ leg4 = get_return_flights(rq)
132
+ ```
133
+
134
+ ## Integrations
135
+ If you'd like, you can use integrations.
136
+
137
+ Bright data:
138
+
139
+ ```python
140
+ from fast_flights import get_flights
141
+ from fast_flights.integrations import BrightData
142
+
143
+ get_flights(..., integration=BrightData())
144
+ ```
145
+
146
+ ## What's new
147
+ - `v3.4.0` – **Native 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.
148
+ - `v3.1.0` – **Round-trip return flights** and **multi-city (N-leg)** support via `select_flight()` + `get_return_flights()`.
149
+ - `v3.0rc0` – Uses Javascript data instead.
150
+ - `v2.2` – Now supports **local playwright** for sending requests.
151
+ - `v2.0` – New (much more succinct) API, fallback support for Playwright serverless functions, and [documentation](https://aweirddev.github.io/flights)!
152
+
153
+ ## Contributing
154
+ Contributing is welcomed! A few notes though:
155
+ 1. please no ai slop. i am not reading all that.
156
+ 2. one change at a time. what your title says is what you've changed.
157
+ 3. no new dependencies unless it's related to the core parsing.
158
+ 4. really, i cant finish reading all of them, i have other projects and life to do. really sorry
159
+
160
+ ***
161
+
162
+ ## How it's made
163
+
164
+ 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](https://flights.google.com) since Google always has the best and most organized data on the web. Therefore, I searched for APIs on Google.
165
+
166
+ > 🔎 **Search** <br />
167
+ > google flights api
168
+
169
+ The results? Bad. It seems like they discontinued this service and it now lives in the Graveyard of Google.
170
+
171
+ > <sup><a href="https://duffel.com/blog/google-flights-api" target="_blank">🧏‍♂️ <b>duffel.com</b></a></sup><br />
172
+ > <sup><i>Google Flights API: How did it work & what happened to it?</i></b>
173
+ >
174
+ > 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**.
175
+
176
+ 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).
177
+
178
+ <br />
179
+
180
+ However, Google Flights has their UI – [flights.google.com](https://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.
181
+
182
+ Perhaps, we could scrape it? I mean, Google allowed many companies like [Serpapi](https://google.com/search?q=serpapi) to scrape their web just pretending like nothing happened... So let's scrape our own.
183
+
184
+ > 🔎 **Search** <br />
185
+ > google flights ~~api~~ scraper pypi
186
+
187
+ Excluding the ones that are not active, I came across [hugoglvs/google-flights-scraper](https://pypi.org/project/google-flights-scraper) on Pypi. I thought to myself: "aint no way this is the solution!"
188
+
189
+ I checked hugoglvs's code on [GitHub](https://github.com/hugoglvs/google-flights-scraper), 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](https://try.playwright.tech) and use a better environment, but that's just too risky if they added Cloudflare as an additional security barrier 😳.
190
+
191
+ Life tells me to never give up. Let's just take a look at their URL params...
192
+
193
+ ```markdown
194
+ https://www.google.com/travel/flights/search?tfs=CBwQAhoeEgoyMDI0LTA1LTI4agcIARIDVFBFcgcIARIDTVlKGh4SCjIwMjQtMDUtMzBqBwgBEgNNWUpyBwgBEgNUUEVAAUgBcAGCAQsI____________AZgBAQ&hl=en
195
+ ```
196
+
197
+ | Param | Content | My past understanding |
198
+ |-------|---------|-----------------------|
199
+ | hl | en | Sets the language. |
200
+ | tfs | CBwQAhoeEgoyMDI0LTA1LTI4agcIARID… | What is this???? 🤮🤮 |
201
+
202
+ I removed the `?tfs=` parameter and found out that this is the control of our request! And it looks so base64-y.
203
+
204
+ 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.
205
+
206
+ 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.
207
+
208
+ > 🔎 **Search** <br />
209
+ > google's json alternative
210
+
211
+ > 🐣 **Result**<br />
212
+ > Solution: The Power of **Protocol Buffers**
213
+ >
214
+ > 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.
215
+
216
+ Gotcha, Protobuf! Let's feed it to an online decoder and see how it does:
217
+
218
+ > 🔎 **Search** <br />
219
+ > protobuf decoder
220
+
221
+ > 🐣 **Result**<br />
222
+ > [protobuf-decoder.netlify.app](https://protobuf-decoder.netlify.app)
223
+
224
+ I then pasted the Base64-encoded string to the decoder and no way! It DID return valid data!
225
+
226
+ ![annotated, Protobuf Decoder screenshot](https://github.com/AWeirdDev/flights/assets/90096971/77dfb097-f961-4494-be88-3640763dbc8c)
227
+
228
+ I immediately recognized the values — that's my data, that's my query!
229
+
230
+ So, I wrote some simple Protobuf code to decode the data.
231
+
232
+ ```protobuf
233
+ syntax = "proto3"
234
+
235
+ message Airport {
236
+ string name = 2;
237
+ }
238
+
239
+ message FlightInfo {
240
+ string date = 2;
241
+ Airport dep_airport = 13;
242
+ Airport arr_airport = 14;
243
+ }
244
+
245
+ message GoogleSucks {
246
+ repeated FlightInfo = 3;
247
+ }
248
+ ```
249
+
250
+ It works! Now, I won't consider myself an "experienced Protobuf developer" but rather a complete beginner.
251
+
252
+ I have no idea what I wrote but... it worked! And here it is, `fast-flights`.
253
+
254
+ ***
255
+
256
+ <div align="center">
257
+
258
+ (c) 2024-2026 AWeirdDev, and all the awesome people
259
+
260
+ </div>
@@ -0,0 +1,39 @@
1
+ from . import integrations
2
+
3
+ from .querying import (
4
+ FlightQuery,
5
+ Query,
6
+ ReturnQuery,
7
+ Passengers,
8
+ create_query,
9
+ create_query as create_filter, # alias
10
+ select_flight,
11
+ )
12
+ from .fetcher import (
13
+ get_flights,
14
+ get_return_flights,
15
+ get_flights_multicity,
16
+ get_flights_multicity_chained,
17
+ fetch_flights_html,
18
+ MulticityLeg,
19
+ MulticityLegChained,
20
+ )
21
+
22
+ __all__ = [
23
+ "FlightQuery",
24
+ "Query",
25
+ "ReturnQuery",
26
+ "Passengers",
27
+ "create_query",
28
+ "create_filter",
29
+ "select_flight",
30
+ "get_flights",
31
+ "get_return_flights",
32
+ "get_flights_multicity",
33
+ "get_flights_multicity_chained",
34
+ "fetch_flights_html",
35
+ "MulticityLeg",
36
+ "MulticityLegChained",
37
+ "integrations",
38
+ ]
39
+