npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

google-flights-ts

v1.0.0

Published

A fast, robust, strongly-typed Google Flights scraper for Node.js

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-ts

Optional: Local Playwright

If you want to use the local fetch mode (headless browser):

npm install playwright
npx playwright install chromium

Quick 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>

  • Result when data_source is "html" (default)
  • DecodedResult when data_source is "js"
  • null if 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_lap must 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 protobuf

The 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

  1. 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 tfs query parameter that Google Flights uses internally.

  2. HTTP request -- The encoded tfs parameter is sent to https://www.google.com/travel/flights using one of five fetch strategies (direct HTTP, serverless Playwright, local Playwright, or Bright Data proxy).

  3. 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 mode

Disclaimer

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