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
4 changes: 2 additions & 2 deletions package-lock.json

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

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "scrapebadger",
"version": "0.13.1",
"version": "0.14.0",
"description": "Official Node.js SDK for ScrapeBadger - Async web scraping APIs for Twitter, Google, Vinted, Reddit, and more",
"type": "module",
"main": "./dist/index.js",
Expand Down
42 changes: 40 additions & 2 deletions src/google/flights.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
*/

import type { BaseClient } from "../internal/client.js";
import type { FlightsSearchParams, GoogleResponse } from "./types.js";
import type { FlightsBookingOptionsParams, FlightsSearchParams, GoogleResponse } from "./types.js";

/**
* Client for Google Flights search.
Expand All @@ -26,6 +26,8 @@ import type { FlightsSearchParams, GoogleResponse } from "./types.js";
* });
* for (const offer of flights.best_flights) {
* console.log(offer.price, offer.currency, offer.total_duration_minutes);
* // offer.booking_url deep-links to the booking page for that flight;
* // offer.selection_token feeds bookingOptions() below.
* }
* if (flights.price_insights) {
* console.log("Price level:", flights.price_insights.price_level);
Expand All @@ -35,10 +37,46 @@ import type { FlightsSearchParams, GoogleResponse } from "./types.js";
export class FlightsClient {
constructor(private readonly client: BaseClient) {}

/** Search Google Flights for available itineraries. */
/**
* Search Google Flights for available itineraries.
*
* Each offer includes a `booking_url` (deep link that pre-selects the
* flight) and a `selection_token` for {@link bookingOptions}. The response
* also carries a `search_url` for the whole search, and each leg carries
* the flight number, departure/arrival times, airport names and aircraft.
*/
async search(params: FlightsSearchParams): Promise<GoogleResponse> {
return this.client.request<GoogleResponse>("/v1/google/flights/search", {
params: { ...params },
});
}

/**
* Retrieve the provider booking list (airline + OTAs, with prices) for a
* selected itinerary, given the `selection_token` from a search offer.
*
* Works for one-way / fully-selected itineraries. This renders the Google
* Flights booking page, so it is slower than `search`. The actual
* per-provider booking links open from the returned `booking_url`.
*
* @example
* ```typescript
* const flights = await client.google.flights.search({
* departure_id: "CNF", arrival_id: "GRU",
* outbound_date: "2026-06-25", trip_type: "one_way", currency: "BRL",
* });
* const token = flights.best_flights[0].selection_token;
* const options = await client.google.flights.bookingOptions({
* selection_token: token, currency: "BRL", gl: "br",
* });
* for (const opt of options.booking_options) {
* console.log(opt.book_with, opt.price, opt.currency);
* }
* ```
*/
async bookingOptions(params: FlightsBookingOptionsParams): Promise<GoogleResponse> {
return this.client.request<GoogleResponse>("/v1/google/flights/booking_options", {
params: { ...params },
});
}
}
1 change: 1 addition & 0 deletions src/google/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export type {
AiTextBlock,
AutocompleteParams,
FinanceQuoteParams,
FlightsBookingOptionsParams,
FlightsSearchParams,
FlightsStopsFilter,
FlightsTravelClass,
Expand Down
13 changes: 13 additions & 0 deletions src/google/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -669,3 +669,16 @@ export interface FlightsSearchParams {
/** Upper price filter. */
max_price?: number;
}

export interface FlightsBookingOptionsParams {
/**
* `selection_token` from a flights search offer (one-way or
* fully-selected itinerary). Pass it to retrieve the provider booking list.
*/
selection_token: string;
/** ISO-4217 currency code (default "USD"). */
currency?: string;
gl?: string;
/** Language for the returned `booking_url`. */
hl?: string;
}
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ export type {
AiTextBlock,
AutocompleteParams,
FinanceQuoteParams,
FlightsBookingOptionsParams,
FlightsSearchParams,
FlightsStopsFilter,
FlightsTravelClass,
Expand Down
43 changes: 40 additions & 3 deletions tests/google.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,9 +123,7 @@ describe("MapsClient", () => {

it("place throws when no identifier is supplied", async () => {
const client = makeClient();
await expect(client.google.maps.place({})).rejects.toThrow(
"place_id or data_id"
);
await expect(client.google.maps.place({})).rejects.toThrow("place_id or data_id");
});

it("reviews forwards all params", async () => {
Expand Down Expand Up @@ -214,6 +212,45 @@ describe("HotelsClient", () => {
});
});

// ---------------------------------------------------------------------------
// Flights
// ---------------------------------------------------------------------------

describe("FlightsClient", () => {
it("search forwards itinerary params", async () => {
mockFetch({ best_flights: [], other_flights: [] });
const client = makeClient();
await client.google.flights.search({
departure_id: "CNF",
arrival_id: "GRU",
outbound_date: "2026-06-25",
trip_type: "one_way",
currency: "BRL",
gl: "br",
hl: "pt-BR",
});
const url = capturedUrl();
expect(url.pathname).toBe("/v1/google/flights/search");
expect(url.searchParams.get("departure_id")).toBe("CNF");
expect(url.searchParams.get("trip_type")).toBe("one_way");
expect(url.searchParams.get("currency")).toBe("BRL");
});

it("bookingOptions forwards selection_token", async () => {
mockFetch({ booking_options: [] });
const client = makeClient();
await client.google.flights.bookingOptions({
selection_token: "CBwQAhxx",
currency: "BRL",
gl: "br",
});
const url = capturedUrl();
expect(url.pathname).toBe("/v1/google/flights/booking_options");
expect(url.searchParams.get("selection_token")).toBe("CBwQAhxx");
expect(url.searchParams.get("currency")).toBe("BRL");
});
});

// ---------------------------------------------------------------------------
// Trends
// ---------------------------------------------------------------------------
Expand Down
Loading