# Google Flights Scraper & Price Tracker (`foxlabs/google-flights-scraper`) Actor

Google Flights for any route and dates: every itinerary incl. the ones Google hides, with price, airlines, flight numbers, stops, CO2, aircraft and codeshares; Google's price level and price history; what changed since your last run; the cheapest departure days.

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

## Pricing

from $0.25 / 1,000 flights

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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 & Price Tracker

Get **every flight Google Flights has for a route and dates — including the ones Google hides behind "View more flights" — with price, airlines, flight numbers, times, stops and layovers, CO₂, aircraft, legroom, codeshares and "operated by"**, plus Google's own **price level, typical price range and price history** (61–62 days for most searches). Run it on a schedule in **monitor mode** and get only **what changed since the last run**: new, cheaper, pricier and gone flights. Or scan a range of days for the **cheapest departure day**.

No Google account, no API key, no browser. One request to Google per search.

### Quick start (API)

```bash
curl -X POST "https://api.apify.com/v2/acts/foxlabs~google-flights-scraper/run-sync-get-dataset-items?token=YOUR_APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"routes": ["IST-LHR"], "departureDate": "+30", "maxFlightsPerSearch": 20}'
```

### Three modes

| Mode | What you get | Typical use |
|---|---|---|
| `search` (default) | Every flight for each route and dates (one row per itinerary), plus one **route row** per search with the lowest price, Google's price level, typical range and price history | Fare research, competitor schedules, emissions reporting |
| `monitor` | Only the flights that changed since the previous run of the same search: `NEW`, `CHEAPER`, `PRICIER`, `GONE` (and `PRICE_ADDED` / `PRICE_REMOVED`). `CHEAPER` and `PRICIER` rows carry the previous price and the change, `GONE` and `PRICE_REMOVED` rows the last price seen. Plus the route row with the change of the lowest price | Daily price tracking and alerts; schedule it |
| `cheapest-days` | One row per departure day with the lowest fare, the cheapest flight, Google's price level, and a flag on the cheapest day | "Cheapest day to fly" content, flexible-date planning |

### What you get

Every row has a `type`: `flight`, `route`, `day` or `status`.

| Group | Fields (flight rows) |
|---|---|
| Price | `price` (for all adults together; round trips: Google's "from" price, the lowest round-trip total with this outbound flight), `priceFormatted`, `priceUnavailable` (`true`: Google lists the flight but shows no price; empty on `GONE` rows, which have no current price) |
| Flights | `airlines`, `airlinesText`, `flightNumbers`, `flightNumbersText`, `itineraryKey` (stable ID: every flight number with its departure time), `operatedBy`, `codeshares`, `aircraft`, `aircraftText`, `minLegroomInches` |
| Times | `departureAirport`, `departureAirportName`, `departureTime`, `arrivalAirport`, `arrivalAirportName`, `arrivalTime` (local airport times), `arrivalDayOffset`, `durationMinutes`, `durationText` |
| Stops | `stops`, `layovers[]` (airport, city, minutes, `changeOfAirport`), `layoversText`, `changeOfAirport` |
| Emissions | `emissionsKg`, `typicalEmissionsKg` (Google's typical figure for the route), `emissionsVsTypicalPercent` |
| Google's ranking | `isBest` (Google's "best flights"), `rank` (order in Google's list) |
| Segments | `segments[]`: flight number, airline, operated by, codeshares, airports, times, duration, aircraft, legroom, CO₂ |
| Monitor | `changeType`, `previousPrice`, `priceChange`, `priceChangePercent` |
| Link | `googleFlightsUrl`: one-way → Google's booking page for exactly these flights (it lists who sells the ticket); round trip → the search with this outbound selected (it lists the matching returns) |

| Group | Fields (route rows, one per search) |
|---|---|
| Price | `lowestPrice`, `lowestPriceFormatted`, `cheapestFlight` |
| Google's price insight | `priceLevel` (`low` / `typical` / `high`), `priceLevelCode`, `priceVsTypicalRange` (`below` / `within` / `above`), `typicalPrice`, `typicalPriceLow`, `typicalPriceHigh`, `belowTypicalBy`, `priceHistory[]` (date and lowest price; 61–62 days when Google has a history for the search), `priceHistoryDays` |
| Coverage | `flightsFound`, `hiddenFlightsCount` (how many Google hides behind "View more flights"), `hiddenFlightsIncluded`, `flightsDelivered`, `originCity`, `destinationCity` |
| Monitor | `monitorStatus` (`first-run` / `compared`), `previousCheckAt`, `previousLowestPrice`, `lowestPriceChange`, `previousPriceLevel`, `newCount`, `cheaperCount`, `pricierCount`, `goneCount`, `unchangedCount` |

Day rows (cheapest-days mode) carry `departureDate`, `returnDate`, `lowestPrice`, `cheapestFlight`, `isCheapestDay`, Google's price level and typical range (no price history) and `flightsFound`. Every row also has `route`, `origin`, `destination`, `departureDate`, `returnDate`, `tripType`, `cabinClass`, `adults`, `currency`, `market`, `mode`, `scrapedAt` and `error`.

**About `priceLevel`.** Google sends a price-level code with most searches, and its pages put a word on each code. We read those words on Google's own pages (2026-09-30): code 1 "low" (a booking page), codes 2, 3 and 4 "typical", code 5 "high" (search pages). `priceLevel` carries that word and `priceLevelCode` the code. In our platform runs (81 route and day rows) the code followed where the lowest price sat in Google's typical range: 2 in its lower third, 3 in the middle, 4 in the upper third, 5 above the range (24 of 24). `priceVsTypicalRange` compares the lowest price with Google's typical range (our comparison of Google's numbers). A search with airline and stop filters came back without a price insight; its fields are then empty.

#### Sample output

A real flight row (trimmed) from a run on 2026-09-30 (UTC): `FRA-BKK`, 10 November 2026, economy, 1 adult, USD.

```json
{
  "type": "flight",
  "route": "FRA-BKK",
  "departureDate": "2026-11-10",
  "price": 626,
  "priceFormatted": "$626",
  "airlinesText": "Lufthansa, Oman Air",
  "flightNumbers": ["LH 998", "WY 172", "WY 815"],
  "itineraryKey": "LH998@2026-11-10T16:40/WY172@2026-11-10T20:00/WY815@2026-11-11T08:30",
  "departureTime": "2026-11-10T16:40",
  "arrivalTime": "2026-11-11T17:15",
  "arrivalDayOffset": 1,
  "durationText": "18 hr 35 min",
  "stops": 2,
  "layoversText": "AMS 2h 5m; MCT 2h 40m",
  "operatedBy": ["Air Dolomiti"],
  "codeshares": ["WY 5364"],
  "aircraftText": "Embraer 190 E2, Boeing 787",
  "minLegroomInches": 30,
  "emissionsKg": 698,
  "typicalEmissionsKg": 616,
  "emissionsVsTypicalPercent": 13,
  "isBest": false,
  "googleFlightsUrl": "https://www.google.com/travel/flights/booking?tfs=CBwQAhqBARIKMjAyNi0xMS0xMCIfCgNGUkES…"
}
```

The route row of the same search (trimmed):

```json
{
  "type": "route",
  "route": "FRA-BKK",
  "departureDate": "2026-11-10",
  "lowestPrice": 489,
  "cheapestFlight": { "price": 489, "airlinesText": "Etihad", "flightNumbersText": "EY 124, EY 406", "stops": 1, "durationMinutes": 865 },
  "priceLevel": "typical",
  "priceLevelCode": 3,
  "priceVsTypicalRange": "within",
  "typicalPrice": 429,
  "typicalPriceLow": 395,
  "typicalPriceHigh": 560,
  "priceHistory": [{ "date": "2026-09-28", "price": 398 }, { "date": "2026-09-29", "price": 373 }, { "date": "2026-09-30", "price": 489 }],
  "priceHistoryDays": 61,
  "flightsFound": 177,
  "hiddenFlightsCount": 165,
  "originCity": "Frankfurt am Main",
  "destinationCity": "Bangkok"
}
```

### Input & filters

| Input | What it does | Default |
|---|---|---|
| `mode` | `search`, `monitor` or `cheapest-days` | `search` |
| `routes` | One route per line as two 3-letter airport codes: `IST-LHR`. A line can carry its own dates: `JFK-LAX 2026-11-05` or `JFK-LAX 2026-11-05 2026-11-12` | — |
| `departureDate` | `YYYY-MM-DD`, or days from today such as `+30` (UTC). Monitor mode needs `YYYY-MM-DD`. In cheapest-days mode, the first day scanned | `+30` |
| `returnDate` | Empty = one-way. `YYYY-MM-DD`, or `+N` for N days after the departure. In cheapest-days mode `+N` is the trip length | one-way |
| `scanDays` | Cheapest-days mode: how many departure days to scan (one search per day) | 14 |
| `cabinClass` | `economy`, `premium-economy`, `business`, `first` | economy |
| `adults` | 1–9. Prices are for all adults together | 1 |
| `maxStops` | `any`, `nonstop`, `1`, `2` (Google's stop filter) | any |
| `airlines` | 2-character airline codes such as `TK`, `LH` | all |
| `currency`, `country` | Currency of the prices; the searcher's market | USD, us |
| `maxFlightsPerSearch` | Flight rows per search, up to 300 (in monitor mode: change rows; see the FAQ) | 200 (the form starts at 20) |
| `includeHiddenFlights` | Also the flights Google hides behind "View more flights" | on |
| `monitorName` | Monitor mode: the result of each search is kept in the key-value store `google-flights-monitor-<name>` of your account | `default` |
| `minPriceChangePercent` | Monitor mode: a price counts as changed from this percentage on, measured against the last reported price | 1 |
| `includeUnchanged` | Monitor mode: also list unchanged flights (`UNCHANGED`) | off |
| `proxyConfiguration`, `autoProxyFallback` | Proxy is not needed; the Actor switches to Apify residential proxy by itself if Google rate-limits it | off / on |

Input the form does not allow is rejected by Apify before the run starts (for example `adults: 10`). These stop the run at once, with the reason in its status message and before any request to Google: a route line that is not two 3-letter codes, a departure date in the past, a departure or return date more than 365 days ahead, a relative departure date (`+30`) in monitor mode, and a return date instead of `+N` in cheapest-days mode. A 3-letter code that is not an airport, such as the city code `LON`, is sent to Google; Google refuses that search, and a free row says so (see Troubleshooting). Days of a cheapest-days scan whose departure or return falls more than 365 days ahead are not searched; the log and the `SOURCE_REPORT` record say how many.

**Each search once per run.** Two lines that describe the same search (route, dates and filters) are searched and charged once; the second leaves a free row with `error: "skipped: …"`. In cheapest-days mode, a day that two lines share is searched and charged once; its row appears under the first line, and the second line still counts it when it names its cheapest day in the log.

### Example inputs (copy & paste)

Every flight, including the hidden ones, for one route 40 days ahead:

```json
{ "routes": ["FRA-BKK"], "departureDate": "+40", "maxFlightsPerSearch": 300 }
```

Daily price tracker for two searches (schedule it once a day; put your own travel dates in the lines):

```json
{ "mode": "monitor", "routes": ["IST-LHR 2027-03-10", "JFK-LAX 2027-03-12 2027-03-19"], "monitorName": "my-routes", "minPriceChangePercent": 2 }
```

Cheapest day to fly in a two-week window starting 20 days from today, one-week round trips:

```json
{ "mode": "cheapest-days", "routes": ["IST-LHR"], "departureDate": "+20", "scanDays": 14, "returnDate": "+7" }
```

Business class, two adults, euros, German market, round trip a week later:

```json
{ "routes": ["CDG-DXB"], "departureDate": "+45", "returnDate": "+7", "cabinClass": "business", "adults": 2, "currency": "EUR", "country": "de" }
```

Nonstop flights sold under two airlines' codes (Google's filter also returns their codeshare flights flown by partners):

```json
{ "routes": ["IST-LHR"], "departureDate": "+30", "maxStops": "nonstop", "airlines": ["TK", "BA"] }
```

### Use cases

- **Fare monitoring for travel agencies and tour operators:** schedule monitor mode on the routes you sell and act on `CHEAPER` rows and on the route row's `lowestPriceChange` and price level.
- **Deal newsletters and travel content:** cheapest-days mode for "cheapest day to fly" pages; `priceVsTypicalRange` and the typical range for "is this a good price" copy.
- **Corporate travel and sustainability reporting:** CO₂ per itinerary next to Google's typical figure for the route, with the operating airline and aircraft.
- **Airline and route analysis:** the whole list, including the connections Google hides, with codeshares, operating carriers, aircraft and legroom.
- **Price-history research:** about two months (61–62 days) of Google's price history for most searches, from the very first run.

### Performance & throughput

Measured on the Apify platform on 2026-09-30 (UTC) with build 0.1.1 at the default 512 MB and the default proxy setting:

| Run | Input | Rows | Searches (requests to Google) | Time |
|---|---|---|---|---|
| `iTZh7Q2BTvdDvtrtX` | The quick-start command above (`IST-LHR`, +30, 20 flights) | 20 flights + 1 route row (92 flights found, 80 of them hidden by Google) | 1 | 4 s |
| `v7MUEk4uaS3eoH4je` | `FRA-BKK`, +40, all flights | 169 flights + 1 route row | 1 | 10 s |
| `N77cWevQcfksNrWW9` | 10 routes (2 round trips), +35, all flights | 1,157 flights + 10 route rows | 10 | 24 s |
| `q5zJv3jUx3YztU6cZ` | Monitor, 3 searches, first run | 314 `NEW` + 3 route rows | 3 | 6 s |
| `QbGegxv24Ff8YfFjC` | The same monitor 17 seconds later | 0 changes; 3 free route rows | 3 | 2 s |
| `N1t34QJS5odsq9QKw` | Cheapest days, `IST-LHR`, 30 days | 30 day rows, all priced | 30 | 8 s |

One search is one request to Google. In our 49 platform runs of 2026-09-30 that searched Google with the default setting (135 requests), none was rate-limited or needed the residential proxy. Forced through Apify residential proxy, the `FRA-BKK` search took 32 s instead of 10 s (`UbiFuqPLtEN71NUSc`).

### Integrations

Use the dataset from the API, schedule the Actor in Apify Console, or connect it to Make, Zapier, n8n, Google Sheets or a webhook. For a price tracker, schedule monitor mode and send the `CHEAPER` rows to Slack or email.

```js
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: 'YOUR_APIFY_TOKEN' });
const run = await client.actor('foxlabs/google-flights-scraper').call({ routes: ['IST-LHR'], departureDate: '+30', maxFlightsPerSearch: 20 });
const { items } = await client.dataset(run.defaultDatasetId).listItems();
```

```python
from apify_client import ApifyClient
client = ApifyClient("YOUR_APIFY_TOKEN")
run = client.actor("foxlabs/google-flights-scraper").call(run_input={"routes": ["IST-LHR"], "departureDate": "+30", "maxFlightsPerSearch": 20})
items = client.dataset(run["defaultDatasetId"]).list_items().items
```

### Data quality

Measured on the platform runs of 2026-09-30 (build 0.1.1): 2,834 flight rows, 35 route rows and 47 day rows from 23 runs, including all runs in the table above.

- **Filled on every flight row:** airlines, flight numbers, `itineraryKey`, departure and arrival times, duration, stops, the Google Flights link (2,834 of 2,834); layovers on all 2,499 flights with stops.
- **Price:** 2,496 of 2,834 flight rows (88%). The others are listed by Google without a price, mostly among the hidden flights; they have `priceUnavailable: true` and `price: null`, never 0.
- **CO₂ and aircraft:** 2,832 of 2,834; Google gave neither for two flights (a London–New York connection via Barcelona on Vueling and LEVEL, and a Scoot flight Singapore–Sydney).
- **Legroom:** 2,741 of 2,762 economy rows. In business class Google gives a seat pitch for fewer flights (29 of 73 in a local CDG-DXB test, not a platform run).
- **Uniqueness:** no flight appeared twice in any run (for example 1,157 of 1,157 unique `itineraryKey` in `N77cWevQcfksNrWW9`).
- **Against Google's own page:** we opened 10 `googleFlightsUrl` links from `N77cWevQcfksNrWW9`. The 5 one-way booking pages and 2 round-trip pages contained every flight number and the date of their row. On 3 search pages, all 41 flights the page lists matched our rows in flight numbers, times, duration, stops, CO₂ and price, and the price level, typical range and last history point matched too.
- **Whole list:** in a recon of 5 routes (434 flights), the number of flights equalled Google's shown list plus its own hidden-flight counter on every route.
- **Price history:** 61 or 62 days for 31 of 35 searches. Google sent none for a Turkish domestic route (SAW-AYT, two runs), a business-class round trip (CDG-DXB) and a search with airline and nonstop filters (which had no price level either).
- **Round trips and passengers:** the price of a round-trip row is what Google's own page shows for that outbound flight, "From $407 round trip total": the lowest total with a matching return. On a JFK-LAX round-trip page (2026-09-30), 33 of 33 priced flights carried that label with the same amount, airline and departure time. Prices are for all adults together: in a local test (CDG-DXB round trip, business; 73 flights in both runs, 65 of them priced in both) the 2-adult price was twice the 1-adult price within €1 for 48 flights and higher for 17 (up to 2.9 times), median ratio 2.0.
- **Prices move often, mostly by small amounts:** in a recon, 70 of 177 FRA-BKK flights were $1 cheaper 22 minutes later (0.04–0.2 %). On the platform, 43 of 314 monitored flights changed price within 31 minutes (`CvxX1oSaU1L81c0wM`, threshold 0): 38 by less than 1 %, 5 by 1 % or more (up to −14.7 %). At the default 1 % threshold, monitor mode would have reported only those 5; smaller moves add up until they pass it. Set `minPriceChangePercent` to 0 to see every move.

### Pricing

Pay per event:

- `flight`: every flight row delivered. In monitor mode that is every change row (and every unchanged row when you turn `includeUnchanged` on).
- `route-check`: once per search that delivered at least one flight row. It is charged with that search's route row.
- `date-price`: every cheapest-days row that has a price.

Free rows: status rows for searches that were empty, failed or duplicated; the route row of a search that delivered no flight row; day rows without a price.

**A monitor run in which nothing changed charges none of these events** (unless you turn `includeUnchanged` on). Each search still leaves its free route row with the lowest price, the price level and the history. Platform run pair: `q5zJv3jUx3YztU6cZ` (first run, 314 `NEW` rows, 3 charged searches), then `QbGegxv24Ff8YfFjC` 17 seconds later (0 changes, 0 flight rows, 0 charged searches, 3 free route rows).

If the Pricing tab lists an Actor start event, it is charged once per run. Current prices are on the Pricing tab.

### FAQ

**What are the "hidden" flights?** Google Flights shows a short list and hides the rest behind "View more flights": dearer or slower combinations. The Actor asks for the whole list in the same single request. The route row says how many Google hides (`hiddenFlightsCount`) and whether they are included (`hiddenFlightsIncluded`).

**How does monitor mode decide what changed?** Each search's flights are stored with their `itineraryKey` in the key-value store `google-flights-monitor-<monitorName>`. The next run compares prices with the price last reported for each flight, so small moves below `minPriceChangePercent` add up until they pass it. The first run of a search is the baseline: every flight is stored, and up to `maxFlightsPerSearch` of them are delivered as `NEW` rows (set it to 300 to get the whole list once). Later runs report only changes; changes that do not fit under `maxFlightsPerSearch` come in the following runs. A flight that is no longer listed comes once as `GONE`.

**Why does monitor mode need a fixed date?** It compares the same flights from run to run. With `+30`, every daily run would search a different day and list everything as new.

**Are prices per person?** No: for all adults together, as Google Flights shows them. A round-trip price is Google's "From … round trip total" for that outbound flight, the lowest total with a matching return. The flight rows describe the outbound flights, and `googleFlightsUrl` opens the matching returns with their totals.

**Why is `priceLevel` empty for some searches?** Google sent no price insight for that search; we saw this with airline and stop filters. Then `priceLevelCode` and the typical range are empty too. See "About `priceLevel`" above.

**Can I get the list of sites that sell the ticket?** Not in the data: Google protects that request. The `googleFlightsUrl` of a one-way flight opens Google's booking page for exactly those flights, which lists the sellers.

**Multi-city trips?** Not supported: one-way and round trips only.

**Do I need a proxy?** No. With the default setting the Actor asks Google directly and switches to Apify residential proxy by itself if Google rate-limits it. None of our 49 default-setting test runs that searched Google (135 requests, 2026-09-30) needed it.

**Why fewer flights than on Google?** `maxFlightsPerSearch`, the stop or airline filters, or `includeHiddenFlights` off. Google's list also changes within minutes.

**Why do other airlines appear when I filter by airline?** Google's airline filter includes codeshare flights sold under those airline codes. In our 2026-10-01 test, a `BA`, `VS` filter returned American Airlines AA 103 (sold as BA 1516) and Delta DL 2 (sold as VS 4007). On each row, `airlines` shows the airline that flies the plane and `codeshares` lists the numbers it is also sold under, so you can keep only the flights a given airline flies itself.

### Troubleshooting

- **`error` starts with `empty:`** — Google shows no flights for that route, date and filters. Try other dates or fewer filters, and check the airport codes. Nothing was charged.
- **`error` starts with `failed:` and says Google Flights did not accept the search** — check that both are airport codes (`LHR`, not the city code `LON`). Nothing was charged.
- **`error` starts with `failed:` otherwise** — Google could not be read for that search; the reason follows. Nothing was charged.
- **`error` starts with `skipped:`** — the same search was already in the input.
- **A monitor run delivered nothing** — nothing changed since the last run; each search still left its free route row.

### Notes, limits & legal

- The Actor reads the public Google Flights results any visitor sees. Prices come from airlines and travel sites through Google and change often; keep `scrapedAt` with the data.
- Times are local airport times without a time zone.
- Up to 300 flights per search, 60 days per cheapest-days scan, dates up to 365 days ahead.
- "Google" and "Google Flights" are trademarks of Google LLC. This Actor is not affiliated with or endorsed by Google.

### Support

Open an issue on the Issues tab with the run ID and the input, or write to info@foxlabs.com.tr.

### Changelog

#### 0.1.3 — 2026-10-01

- The run's status message says what was delivered, or why nothing was.

#### 0.1.2 — 2026-10-01

- `priceLevel` for every code Google sends: `low`, `typical`, `high`.
- Monitor mode: the first run stores the whole list as the baseline, so later runs never list an old flight as `NEW`.
- A city code such as `LON` ends with one request and a plain message.
- Cheapest-days: a day that two lines share is searched and charged once.

See CHANGELOG.md for details.

#### 0.1 — 2026-09-30

First version.

# Changelog

This Actor's version history is a separate document: https://apify.com/foxlabs/google-flights-scraper/changelog.md

# Actor input Schema

## `mode` (type: `string`):

Search: every flight Google Flights has for each route and dates, plus one summary row per search with Google's price level and price history. Monitor: only what changed since the previous run of the same search (new, cheaper, pricier and gone flights); schedule it daily. Cheapest days: the lowest fare for each departure day in a range.

## `routes` (type: `array`):

One route per line, as two 3-letter airport codes: IST-LHR, JFK-LAX (use airport codes such as LHR, not city codes such as LON). A line can carry its own dates: "JFK-LAX 2026-11-05" (one-way) or "JFK-LAX 2026-11-05 2026-11-12" (round trip). Lines without dates use the dates below.

## `departureDate` (type: `string`):

YYYY-MM-DD, or days from today such as +30 (counted in UTC, so a scheduled run always searches 30 days ahead). Monitor mode compares the same flights from run to run and needs a fixed date (YYYY-MM-DD). In cheapest-days mode this is the first day scanned.

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

Leave empty for one-way. YYYY-MM-DD, or +N for N days after the departure (+7 = a week). Round-trip prices are the total for both directions. In cheapest-days mode use +N: every scanned day gets a return N days later.

## `scanDays` (type: `integer`):

How many departure days, starting at the departure date, the cheapest-days mode searches. One search per day.

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

Cabin class of every search.

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

Number of adult passengers. Prices are for all of them together, as Google Flights shows them.

## `maxStops` (type: `string`):

Google's own stop filter.

## `airlines` (type: `array`):

Google's airline filter, as 2-character airline codes: TK, LH, BA. Google also returns codeshare flights sold under these codes but flown by partner airlines (a BA filter can return American Airlines AA 103, sold as BA 1516); in the output, airlines shows who flies the plane and codeshares lists the numbers it is sold under. Leave empty for every airline.

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

Currency of every price.

## `country` (type: `string`):

Two-letter country of the searcher, such as us, gb, de, tr. Google Flights shows fares for this market.

## `maxFlightsPerSearch` (type: `integer`):

Stop after this many flight rows for each route and dates (up to 300). In monitor mode it caps the change rows: the first run stores every flight as the baseline and delivers this many of them; in later runs, changes that do not fit come in the following runs, up to this many per run. The form starts at 20 for a quick first run; an API call without this field gets 200.

## `includeHiddenFlights` (type: `boolean`):

Google Flights shows a short list and hides the rest behind "View more flights" (dearer or slower options). On: the whole list, in the same single request. Off: the shown list only.

## `monitorName` (type: `string`):

Monitor mode keeps the last result of each search in a key-value store of your Apify account called google-flights-monitor-<name>. Use a different name for each separate watch list. Letters, digits and hyphens.

## `minPriceChangePercent` (type: `integer`):

A flight counts as cheaper or pricier when its price moved at least this much since it was last reported. 0 reports every change. Smaller moves add up until they pass the threshold.

## `includeUnchanged` (type: `boolean`):

Off: monitor rows are only the changes. On: every flight is listed, unchanged ones marked UNCHANGED (each row is charged as a flight).

## `proxyConfiguration` (type: `object`):

Not needed for most runs. Leave off and the Actor switches to Apify residential proxy by itself if Google rate-limits it.

## `autoProxyFallback` (type: `boolean`):

On by default.

## Actor input object example

```json
{
  "mode": "search",
  "routes": [
    "IST-LHR"
  ],
  "departureDate": "+30",
  "scanDays": 14,
  "cabinClass": "economy",
  "adults": 1,
  "maxStops": "any",
  "currency": "USD",
  "country": "us",
  "maxFlightsPerSearch": 20,
  "includeHiddenFlights": true,
  "monitorName": "default",
  "minPriceChangePercent": 1,
  "includeUnchanged": false,
  "proxyConfiguration": {
    "useApifyProxy": false
  },
  "autoProxyFallback": true
}
```

# Actor output Schema

## `dataset` (type: `string`):

No description

# 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 = {
    "routes": [
        "IST-LHR"
    ],
    "maxFlightsPerSearch": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("foxlabs/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 = {
    "routes": ["IST-LHR"],
    "maxFlightsPerSearch": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("foxlabs/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 '{
  "routes": [
    "IST-LHR"
  ],
  "maxFlightsPerSearch": 20
}' |
apify call foxlabs/google-flights-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,foxlabs/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/lBzHB2aTBvQH99eBY/builds/121N195Kjr7fvo6fY/openapi.json
