Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "scrapebadger"
version = "0.13.1"
version = "0.14.0"
description = "Official Python SDK for ScrapeBadger - Async web scraping APIs for Twitter and more"
readme = "README.md"
license = { text = "MIT" }
Expand Down
56 changes: 55 additions & 1 deletion src/scrapebadger/google/flights.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,11 @@ async def search(

Returns:
Response with `best_flights[]`, `other_flights[]`,
`price_insights`, `airports[]`, and trip-type metadata.
`price_insights`, `airports[]`, a `search_url`, and trip-type
metadata. Each offer carries a `booking_url` (deep link that
pre-selects the flight) and a `selection_token` for
:meth:`booking_options`; each leg carries the flight number,
departure/arrival times, airport names, and aircraft.
"""
params: dict[str, Any] = {
"departure_id": departure_id,
Expand All @@ -105,3 +109,53 @@ async def search(
if max_price is not None:
params["max_price"] = max_price
return await self._client.get("/v1/google/flights/search", params=params)

async def booking_options(
self,
selection_token: str,
*,
currency: str = "USD",
gl: str = "us",
hl: str = "en",
) -> dict[str, Any]:
"""Retrieve the provider booking list for a selected itinerary.

Given the ``selection_token`` from a :meth:`search` offer (one-way or
fully-selected itinerary), returns the airline/OTA providers that can
book it. This renders the Google Flights booking page, so it is slower
than :meth:`search`. The actual per-provider booking links open from
the returned ``booking_url``.

Args:
selection_token: ``selection_token`` from a search offer.
currency: ISO-4217 currency code (default "USD").
gl: Country code.
hl: Language for the returned ``booking_url``.

Returns:
Response with ``booking_options[]`` (each ``book_with``, ``price``,
``currency``, ``type``), a ``booking_url``, and ``currency``.

Example:
```python
flights = await client.google.flights.search(
departure_id="CNF", arrival_id="GRU",
outbound_date="2026-06-25", trip_type="one_way", currency="BRL",
)
token = flights["best_flights"][0]["selection_token"]
options = await client.google.flights.booking_options(
token, currency="BRL", gl="br",
)
for opt in options["booking_options"]:
print(opt["book_with"], opt["price"], opt["currency"])
```
"""
return await self._client.get(
"/v1/google/flights/booking_options",
params={
"selection_token": selection_token,
"currency": currency,
"gl": gl,
"hl": hl,
},
)
27 changes: 27 additions & 0 deletions tests/test_google.py
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,33 @@ async def test_details(self, google: GoogleClient, mock_base_client: MagicMock)
assert kwargs["params"]["property_token"] == "PTOKEN"


class TestFlightsClient:
@pytest.mark.asyncio
async def test_search(self, google: GoogleClient, mock_base_client: MagicMock) -> None:
await google.flights.search(
"CNF",
"GRU",
"2026-06-25",
trip_type="one_way",
currency="BRL",
gl="br",
hl="pt-BR",
)
args, kwargs = mock_base_client.get.call_args
assert args[0] == "/v1/google/flights/search"
assert kwargs["params"]["departure_id"] == "CNF"
assert kwargs["params"]["trip_type"] == "one_way"
assert kwargs["params"]["currency"] == "BRL"

@pytest.mark.asyncio
async def test_booking_options(self, google: GoogleClient, mock_base_client: MagicMock) -> None:
await google.flights.booking_options("CBwQAhxx", currency="BRL", gl="br")
args, kwargs = mock_base_client.get.call_args
assert args[0] == "/v1/google/flights/booking_options"
assert kwargs["params"]["selection_token"] == "CBwQAhxx"
assert kwargs["params"]["currency"] == "BRL"


class TestTrendsClient:
@pytest.mark.asyncio
async def test_interest(self, google: GoogleClient, mock_base_client: MagicMock) -> None:
Expand Down
2 changes: 1 addition & 1 deletion uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading