# Google Flights Scraper (`bgfc97/google-flights-scraper`) Actor

🔥 Up to 30% OFF! Scrape Google Flights — no login. Airlines, price, duration, stops, layovers, departure/arrival times, CO2 emissions + round-trip & multi-city. Fast & reliable, pay per flight.

- **URL**: https://apify.com/bgfc97/google-flights-scraper.md
- **Developed by:** [Bruno](https://apify.com/bgfc97) (community)
- **Categories:** Travel
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.10 / 1,000 flight scrapeds

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## 🔥 Google Flights Scraper (Real Browser)

Pulls **real flight itineraries and prices** from Google Flights by driving an actual headless
browser (Playwright/Chromium) through an **Apify residential proxy** — the same way a person would
browse the site. A plain HTTP request to Google Flights gets blocked; this Actor renders the real
page, waits for the results to actually appear, scrolls to load more, and reads them off the live
DOM and Google's own accessibility labels.

**Honesty first:** if Google shows a CAPTCHA, a cookie-consent wall, or simply doesn't render
results in time, this Actor reports exactly what it saw instead of guessing or inventing prices.
It **never fabricates a fare**. Only public Google Flights data is read.

### ✨ What it gives you

- 💲 **Price** (string + numeric amount + currency + fare type: round-trip / one-way)
- 🛫 **Airlines** (marketing carrier **and** the airline actually operating each codeshare)
- 🕑 **Departure / arrival** times, airport names + IATA codes, day of week, **next-day arrival** flag
- ⏱️ **Total duration** (string **and** minutes) + **number of stops / connections**
- 🔁 **Per-layover detail**: airport, city, layover length (+ minutes) and **overnight** flag
- 🌱 **CO2 emissions** (kg) and how they compare to a typical flight (e.g. `-12%` / `average`)
- 🏆 Computed **cheapest** and **fastest** flags across your result set, plus a `rank`
- 🔗 The Google Flights **search URL**, a `scrapedAt` timestamp, and the full `rawText` / `ariaLabel`
- ✈️ **One-way, round-trip and multi-city**, sort by **best / price / duration**
- 🧰 Optional **flight numbers, aircraft type & amenities** via per-card detail expansion

### Input

```json
{
  "origin": "JFK",
  "destination": "LAX",
  "departDate": "2026-11-15",
  "returnDate": "2026-11-22",
  "adults": 1,
  "cabinClass": "economy",
  "currency": "USD",
  "sortBy": "price",
  "maxResults": 20,
  "expandDetails": false,
  "maxRequestRetries": 3,
  "timeoutSecs": 60
}
```

- `origin` / `destination` — IATA airport code or city name. **Required.**
- `departDate` — `YYYY-MM-DD`. **Required.**
- `returnDate` — `YYYY-MM-DD`, optional. Omit it for a **one-way** search.
- `legs` — optional **multi-city** array; at least 2 objects, each `{ "origin", "destination", "date" }`.
  When provided it takes priority over `origin`/`destination`/`returnDate`.
- `adults` — passenger count, default `1`.
- `cabinClass` — `economy` / `premium` / `business` / `first`, default `economy`.
- `currency` — 3-letter code Google should price in, default `USD`.
- `sortBy` — `best` (Google's ranking, default) / `price` (cheapest first) / `duration` (fastest first).
- `maxResults` — cap on itineraries returned, default `30`.
- `expandDetails` — `true` to also capture flight numbers / aircraft / amenities (slower), default `false`.
- `maxRequestRetries` — retries with a fresh browser + proxy IP on a block/timeout, default `3`.
- `timeoutSecs` — how long to wait for results to render per attempt, default `60`.

#### Multi-city example

```json
{
  "origin": "JFK", "destination": "JFK", "departDate": "2026-11-15",
  "legs": [
    { "origin": "JFK", "destination": "LAX", "date": "2026-11-15" },
    { "origin": "LAX", "destination": "SEA", "date": "2026-11-20" },
    { "origin": "SEA", "destination": "JFK", "date": "2026-11-25" }
  ]
}
```

### Output (one item per itinerary)

```json
{
  "origin": "JFK", "destination": "LAX", "departDate": "2026-11-15", "returnDate": "2026-11-22",
  "cabinClass": "economy", "adults": 1, "currency": "USD", "tripType": "round-trip",
  "price": "$347", "priceAmount": 347, "priceCurrencySymbolOrCode": "$", "priceFareType": "round trip",
  "airline": "Delta", "airlines": ["Delta"], "operatingAirlines": [],
  "departureTime": "6:00 AM", "arrivalTime": "9:25 AM", "departureArrival": "6:00 AM - 9:25 AM",
  "departureAirportName": "John F. Kennedy International Airport",
  "arrivalAirportName": "Los Angeles International Airport",
  "departureDay": "Saturday, November 15", "arrivalDay": "Saturday, November 15", "arrivesNextDay": false,
  "duration": "6 hr 25 min", "durationMinutes": 385,
  "stops": "Nonstop", "stopsCount": 0, "connectionCount": 0, "connectionAirports": [], "layovers": [],
  "originAirportCode": "JFK", "destinationAirportCode": "LAX", "routeAirports": "JFK-LAX",
  "co2Emissions": "48 kg CO2e", "co2EmissionsKg": 48, "emissionsVsTypical": "-12%",
  "isCheapestInResults": true, "isFastestInResults": true, "rank": 1,
  "searchUrl": "https://www.google.com/travel/flights?...",
  "scrapedAt": "2026-10-05T18:00:00.000Z",
  "rawText": "6:00 AM ... Delta $347 ...",
  "ariaLabel": "From 347 US dollars round trip total. Nonstop flight with Delta. ..."
}
```

A connecting itinerary adds entries to `layovers`, e.g.:

```json
"layovers": [
  { "index": 1, "of": 1, "airport": "Fort Lauderdale-Hollywood International Airport",
    "city": "Fort Lauderdale", "duration": "2 hr 10 min", "durationMinutes": 130, "overnight": false }
]
```

`rawText` and `ariaLabel` are kept on every row so you can verify or re-parse anything the field
extraction missed — nothing is hidden.

If results couldn't be read, you'll get a single honest item instead, e.g.:

```json
{ "error": "BLOCKED: Google served an anti-bot/CAPTCHA challenge page instead of flight results.", "url": "..." }
```

Other possible `error` values: `NO_ITEMS_MATCHED` (Google changed its markup), `TIMEOUT`
(nothing rendered in time), `NAVIGATION_FAILED`, or a plain input-validation message.

### How it works

1. Launches real Chromium (Playwright) through Apify's **RESIDENTIAL** proxy (pinned to US by
   default, with automatic fallback if that's unavailable on the account).
2. Opens `https://www.google.com/travel/flights?...&q=Flights from {origin} to {destination} on
   {departDate}...` — Google's own natural-language parser builds the search from that sentence,
   including round-trip / one-way / multi-city, passenger count and cabin class.
3. Dismisses a cookie-consent interstitial if Google shows one.
4. Waits for real priced itinerary list items to appear (not a fixed sleep), then scrolls / clicks
   "more flights" to pull in additional itineraries up to `maxResults`.
5. Reads each rendered card's text **and** Google's rich `aria-label` and extracts every field with
   pattern matching — no guessing, no cached/sample data.
6. On a block or render timeout it **retries with a fresh browser + proxy IP** (`maxRequestRetries`),
   and only reports an honest error once all retries are exhausted.

### Notes

- Google's markup is unlabeled/obfuscated and changes over time; extraction is regex/heuristic over
  rendered text and aria-labels. `rawText`/`ariaLabel` are always included as a source-of-truth backstop.
- Residential proxy IPs and Google's bot-defenses mean an occasional run may still hit a CAPTCHA —
  when that happens the Actor tells you plainly rather than pretending to have data.
- `isCheapestInResults` / `isFastestInResults` are computed **within the results this run returned**,
  not a claim about every fare in the world — the names say so on purpose.
- `adults` and `cabinClass` are passed to Google Flights as part of the natural-language query
  (e.g. "...for 2 adults in business class"); this is Google's interpretation, not a hard API filter —
  always check `cabinClass`/`adults` echoed back against `rawText` if exact filtering matters.
- `flightNumbers` / `aircraftTypes` / `amenities` only appear when `expandDetails` is `true`, and are
  best-effort for the first few results.

### ⭐ Enjoying this Actor?

A quick **rating/review** helps others find it. Found a query pattern Google didn't parse right?
Open a ticket on the **Issues** tab.

# Actor input Schema

## `origin` (type: `string`):

Departure airport IATA code or city, e.g. "JFK" or "New York". (For a multi-city trip, fill this with the first leg's origin and use "legs" below.)

## `destination` (type: `string`):

Arrival airport IATA code or city, e.g. "LAX" or "Los Angeles".

## `departDate` (type: `string`):

Departure date in YYYY-MM-DD format.

## `returnDate` (type: `string`):

Return date in YYYY-MM-DD format. Leave empty to search a one-way trip.

## `legs` (type: `array`):

Optional multi-city itinerary. Provide at least 2 legs, each an object with "origin", "destination" and "date" (YYYY-MM-DD). Example: \[{"origin":"JFK","destination":"LAX","date":"2026-11-15"},{"origin":"LAX","destination":"SEA","date":"2026-11-20"},{"origin":"SEA","destination":"JFK","date":"2026-11-25"}]. When set, "legs" takes priority over origin/destination/returnDate. (Google parses multi-city from a natural-language query — best effort.)

## `adults` (type: `integer`):

Number of adult passengers.

## `cabinClass` (type: `string`):

Cabin class to search.

## `currency` (type: `string`):

3-letter currency code Google Flights should display prices in, e.g. USD, EUR, BRL.

## `sortBy` (type: `string`):

How to order the returned itineraries. "best" keeps Google's own ranking; "price" sorts cheapest-first; "duration" sorts fastest-first.

## `expandDetails` (type: `boolean`):

Best-effort: click each itinerary card to also capture flight numbers, aircraft type and cabin amenities. Slower and only applied to the first few results. Off by default.

## `maxResults` (type: `integer`):

Maximum number of itineraries to return.

## `maxRequestRetries` (type: `integer`):

How many times to retry with a fresh browser + proxy IP if Google blocks the request or results don't render. Higher = more robust but slower on bad luck.

## `timeoutSecs` (type: `integer`):

How long to wait for Google Flights results to render before giving up (per attempt).

## Actor input object example

```json
{
  "origin": "JFK",
  "destination": "LAX",
  "departDate": "2026-11-15",
  "adults": 1,
  "cabinClass": "economy",
  "currency": "USD",
  "sortBy": "best",
  "expandDetails": false,
  "maxResults": 30,
  "maxRequestRetries": 3,
  "timeoutSecs": 60
}
```

# Actor output Schema

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "origin": "JFK",
    "destination": "LAX",
    "departDate": "2026-11-15"
};

// Run the Actor and wait for it to finish
const run = await client.actor("bgfc97/google-flights-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "origin": "JFK",
    "destination": "LAX",
    "departDate": "2026-11-15",
}

# Run the Actor and wait for it to finish
run = client.actor("bgfc97/google-flights-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "origin": "JFK",
  "destination": "LAX",
  "departDate": "2026-11-15"
}' |
apify call bgfc97/google-flights-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,bgfc97/google-flights-scraper"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/y0X6NxDkMgk1Yqa3U/builds/ZVBCh1FqEZwCJwowO/openapi.json
