google-flights-ts
v1.0.0
Published
A fast, robust, strongly-typed Google Flights scraper for Node.js
Maintainers
Readme
✈️ google-flights-ts
A fast, strongly-typed Google Flights scraper for Node.js.
TypeScript port of AWeirdDev/flights. Encodes search parameters into Google's protobuf-based tfs query parameter, fetches the results page, and parses flight data from the HTML or embedded JS response.
Zero binary dependencies — the protobuf encoding is hand-rolled, no native modules needed.
Table of Contents
Install
npm install google-flights-tsOptional: Local Playwright
If you want to use the local fetch mode (headless browser):
npm install playwright
npx playwright install chromiumQuick Start
import { FlightData, Passengers, getFlights } from "google-flights-ts";
const result = await getFlights({
flight_data: [
new FlightData({
date: "2026-07-01",
from_airport: "JFK",
to_airport: "LAX",
}),
],
trip: "one-way",
seat: "economy",
adults: 1,
});
if (result && "flights" in result) {
console.log("Price trend:", result.current_price); // "low" | "typical" | "high"
for (const flight of result.flights) {
console.log(`${flight.name} ${flight.departure}-${flight.arrival} $${flight.price}`);
}
}API Reference
getFlights(options)
High-level function that builds a filter and fetches results in one call.
const result = await getFlights({
// Required
flight_data: FlightData[],
trip: "one-way" | "round-trip" | "multi-city",
// Passengers (either pass a Passengers object or use convenience counters)
passengers?: Passengers,
adults?: number, // default: 1
children?: number, // default: 0
infants_in_seat?: number, // default: 0
infants_on_lap?: number, // default: 0
// Optional
seat?: "economy" | "premium-economy" | "business" | "first", // default: "economy"
fetch_mode?: FetchMode, // default: "common"
max_stops?: number, // undefined = any
data_source?: "html" | "js", // default: "html"
cookies?: Record<string, string>,
request_options?: RequestOptions,
cookie_consent?: boolean, // default: true
fetchFn?: FetchFn, // custom fetch implementation (e.g. for proxies)
});Returns: Promise<Result | DecodedResult | null>
Resultwhendata_sourceis"html"(default)DecodedResultwhendata_sourceis"js"nullif no data found
getFlightsFromFilter(filter, options?)
Lower-level function that takes a pre-built TFSData filter.
import { createFilter, getFlightsFromFilter, FlightData, Passengers } from "google-flights-ts";
const filter = createFilter({
flight_data: [
new FlightData({ date: "2026-07-01", from_airport: "LHR", to_airport: "SLC" }),
new FlightData({ date: "2026-07-15", from_airport: "SLC", to_airport: "LHR" }),
],
trip: "round-trip",
passengers: new Passengers({ adults: 2, children: 1 }),
seat: "economy",
max_stops: 1,
});
const result = await getFlightsFromFilter(filter, {
currency: "USD",
mode: "common",
data_source: "html",
});Options:
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| currency | string | "" | Currency code (e.g. "USD", "EUR") |
| mode | FetchMode | "common" | How to fetch the page (see Fetch Modes) |
| data_source | "html" \| "js" | "html" | Which parser to use (see Data Sources) |
| cookies | Record<string, string> | - | Custom cookies to send |
| request_options | RequestOptions | - | Custom headers/cookies for the request |
| cookie_consent | boolean | true | Auto-include default consent cookies |
| fetchFn | FetchFn | fetch | Custom fetch function (e.g. for proxy support) |
FetchFn
Type alias for a custom fetch implementation. Useful when you need to route requests through a proxy or add custom transport logic.
type FetchFn = (url: string, init?: RequestInit) => Promise<Response>;Both getFlights and getFlightsFromFilter accept an optional fetchFn parameter. When provided, it replaces the global fetch for the HTTP request to Google Flights.
import { FlightData, getFlights, type FetchFn } from "google-flights-ts";
// Example: route through a SOCKS proxy using node:https + socks-proxy-agent
import { SocksProxyAgent } from "socks-proxy-agent";
import https from "node:https";
const agent = new SocksProxyAgent("socks5://127.0.0.1:1080");
const proxyFetch: FetchFn = (url, init) =>
new Promise((resolve, reject) => {
const parsed = new URL(url);
const req = https.request(
{
hostname: parsed.hostname,
path: parsed.pathname + parsed.search,
method: init?.method ?? "GET",
headers: init?.headers as Record<string, string>,
agent,
},
(res) => {
const chunks: Buffer[] = [];
res.on("data", (c: Buffer) => chunks.push(c));
res.on("end", () =>
resolve(
new Response(Buffer.concat(chunks), {
status: res.statusCode ?? 200,
headers: new Headers(res.headers as Record<string, string>),
}),
),
);
},
);
req.on("error", reject);
req.end();
});
const result = await getFlights({
flight_data: [
new FlightData({ date: "2026-07-01", from_airport: "JFK", to_airport: "LAX" }),
],
trip: "one-way",
adults: 1,
fetchFn: proxyFetch,
});createFilter(options)
Builds a TFSData filter object without fetching. Useful when you want to inspect the encoded URL or reuse the filter.
const filter = createFilter({
flight_data: [new FlightData({ date: "2026-07-01", from_airport: "JFK", to_airport: "LAX" })],
trip: "one-way",
passengers: new Passengers({ adults: 1 }),
seat: "economy",
});
// Get the Google Flights URL
const url = `https://www.google.com/travel/flights?tfs=${filter.toBase64()}`;
console.log(url);new FlightData(options)
Represents one leg of a journey.
new FlightData({
date: string, // "YYYY-MM-DD"
from_airport: string, // 3-letter IATA code or AIRPORTS key
to_airport: string, // 3-letter IATA code or AIRPORTS key
max_stops?: number, // 0 = nonstop, 1, 2, etc.
airlines?: string[], // 2-letter codes or alliance names
})Airport codes can be either raw IATA codes ("JFK", "LAX") or the full name from the AIRPORTS object ("ZURICH_AIRPORT" resolves to "ZRH").
Airlines accepts 2-letter IATA airline codes ("UA", "AA") or alliance names: "STAR_ALLIANCE", "ONEWORLD", "SKYTEAM".
new Passengers(options?)
new Passengers({
adults?: number, // default: 0
children?: number, // default: 0
infants_in_seat?: number, // default: 0
infants_on_lap?: number, // default: 0
})Constraints:
- Total passengers must be <= 9
infants_on_lapmust be <=adults
searchAirport(query)
Case-insensitive search across all 3,311 airports.
import { searchAirport } from "google-flights-ts";
const results = searchAirport("zurich");
// [{ name: "ZURICH_AIRPORT", code: "ZRH" }]
const results2 = searchAirport("NEW_YORK");
// [{ name: "NEW_YORK_JOHN_F_KENNEDY_INTERNATIONAL_AIRPORT", code: "JFK" }, ...]Cookies
Generate Google consent cookies for requests.
import { Cookies } from "google-flights-ts";
const cookies = Cookies.create({ locale: "en" });
cookies.toDict(); // { CONSENT: "PENDING+987", SOCS: "<base64>" }
cookies.toBase64(); // base64-encoded SOCS protobufThe library embeds default consent cookies automatically (controlled by the cookie_consent option). You only need Cookies if you want to generate fresh ones or customize the locale.
Fetch Modes
| Mode | Description |
|------|-------------|
| "common" | Direct HTTP request with Chrome-like headers. Fastest option. (default) |
| "fallback" | Tries common first, falls back to serverless Playwright if it fails. Recommended for reliability. |
| "force-fallback" | Always uses serverless Playwright via try.playwright.tech. |
| "local" | Uses a local Playwright browser. Requires the playwright package. Best for privacy. |
| "bright-data" | Uses Bright Data proxy API. Requires BRIGHT_DATA_API_KEY env var. |
Bright Data Configuration
Set these environment variables:
| Variable | Required | Default |
|----------|----------|---------|
| BRIGHT_DATA_API_KEY | Yes | - |
| BRIGHT_DATA_API_URL | No | https://api.brightdata.com/request |
| BRIGHT_DATA_SERP_ZONE | No | serp_api1 |
Data Sources
HTML mode (data_source: "html") -- default
Parses the rendered HTML using CSS selectors. Returns a Result:
interface Result {
current_price: "low" | "typical" | "high" | string;
flights: Flight[];
}
interface Flight {
is_best: boolean;
name: string; // Airline name
departure: string; // e.g. "2:30 PM"
arrival: string; // e.g. "11:45 PM"
arrival_time_ahead: string; // e.g. "+1 day"
duration: string; // e.g. "18h 15m"
stops: number | string; // 0 for nonstop, or "Unknown"
delay: string | null;
price: string; // e.g. "523" (no currency symbol)
}JS mode (data_source: "js")
Extracts structured data from the embedded JavaScript. Returns a DecodedResult with richer detail:
interface DecodedResult {
raw: unknown[];
best: Itinerary[];
other: Itinerary[];
}
interface Itinerary {
airline_code: string;
airline_names: string[];
flights: DecodedFlight[];
layovers: Layover[];
travel_time: number; // minutes
departure_airport: string;
arrival_airport: string;
departure_date: [number, number, number]; // [year, month, day]
arrival_date: [number, number, number];
departure_time: [number, number]; // [hour, minute]
arrival_time: [number, number];
itinerary_summary: ItinerarySummary;
}
interface DecodedFlight {
airline: string;
airline_name: string;
flight_number: string;
operator: string;
codeshares: Codeshare[];
aircraft: string;
departure_airport: string;
departure_airport_name: string;
arrival_airport: string;
arrival_airport_name: string;
departure_date: [number, number, number];
arrival_date: [number, number, number];
departure_time: [number, number];
arrival_time: [number, number];
travel_time: number;
seat_pitch_short: string;
}
interface Layover {
minutes: number;
departure_airport: string;
departure_airport_name: string;
departure_airport_city: string;
arrival_airport: string;
arrival_airport_name: string;
arrival_airport_city: string;
}
interface Codeshare {
airline_code: string;
flight_number: number;
airline_name: string;
}
interface ItinerarySummary {
flights: string;
price: number; // dollars (e.g. 150.00)
currency: string; // e.g. "USD"
}Examples
One-way flight
import { FlightData, getFlights } from "google-flights-ts";
const result = await getFlights({
flight_data: [
new FlightData({ date: "2026-07-01", from_airport: "SFO", to_airport: "NRT" }),
],
trip: "one-way",
adults: 1,
seat: "business",
max_stops: 1,
});Round trip
import { FlightData, Passengers, getFlights } from "google-flights-ts";
const result = await getFlights({
flight_data: [
new FlightData({ date: "2026-12-20", from_airport: "LHR", to_airport: "JFK" }),
new FlightData({ date: "2027-01-03", from_airport: "JFK", to_airport: "LHR" }),
],
trip: "round-trip",
passengers: new Passengers({ adults: 2, children: 1, infants_on_lap: 1 }),
seat: "premium-economy",
fetch_mode: "fallback",
});JS data source (detailed itinerary info)
import { FlightData, getFlights, type DecodedResult } from "google-flights-ts";
const result = await getFlights({
flight_data: [
new FlightData({ date: "2026-10-04", from_airport: "SJC", to_airport: "LAS" }),
],
trip: "one-way",
adults: 1,
data_source: "js",
}) as DecodedResult | null;
if (result) {
for (const itinerary of result.best) {
console.log(`${itinerary.airline_names.join(", ")} - ${itinerary.travel_time} min`);
console.log(` $${itinerary.itinerary_summary.price} ${itinerary.itinerary_summary.currency}`);
for (const flight of itinerary.flights) {
console.log(` ${flight.flight_number} ${flight.departure_airport}->${flight.arrival_airport} (${flight.aircraft})`);
}
for (const layover of itinerary.layovers) {
console.log(` Layover: ${layover.minutes} min at ${layover.departure_airport_name}`);
}
}
}Filter-only (generate URL without fetching)
import { FlightData, Passengers, createFilter } from "google-flights-ts";
const filter = createFilter({
flight_data: [
new FlightData({ date: "2026-07-01", from_airport: "EWR", to_airport: "CDG" }),
],
trip: "one-way",
passengers: new Passengers({ adults: 1 }),
seat: "economy",
});
console.log(`https://www.google.com/travel/flights?tfs=${filter.toBase64()}`);Custom cookies and headers
import { FlightData, Cookies, getFlights } from "google-flights-ts";
const cookies = Cookies.create({ locale: "de" });
const result = await getFlights({
flight_data: [
new FlightData({ date: "2026-08-15", from_airport: "FRA", to_airport: "BKK" }),
],
trip: "one-way",
adults: 1,
cookies: cookies.toDict(),
request_options: {
headers: { "Accept-Language": "de-DE,de;q=0.9" },
},
cookie_consent: false, // disable default cookies since we're providing our own
});How It Works
Protobuf encoding -- Flight search parameters (airports, dates, passengers, seat class, trip type) are serialized into a Protocol Buffer binary message, then base64-encoded. This becomes the
tfsquery parameter that Google Flights uses internally.HTTP request -- The encoded
tfsparameter is sent tohttps://www.google.com/travel/flightsusing one of five fetch strategies (direct HTTP, serverless Playwright, local Playwright, or Bright Data proxy).Response parsing -- The HTML response is parsed with cheerio to extract flight cards (HTML mode), or the embedded JavaScript data blob is decoded from nested JSON arrays (JS mode).
The protobuf encoding is hand-rolled (no runtime protobuf library needed) since the schema is small and fixed. The .proto definitions are in src/proto/ for reference.
Scripts
npm run build # Compile TypeScript to dist/
npm test # Run tests (vitest)
npm run test:watch # Run tests in watch modeDisclaimer
This project is not affiliated with, endorsed by, or associated with Google. "Google Flights" is a trademark of Google LLC. This library is provided as-is for personal and educational use. Use responsibly and in accordance with Google's Terms of Service.
License
MIT
