# Google Flights Scraper API - Date Range & Price Drop Monitor (`neverempty/google-flights-scraper`) Actor

For fare alerts and airfare dashboards: Google Flights prices for one route across up to 31 departure dates in one run, every flight with price, airline, times, stops and CO2. 5-7 dates took 6.8-9.8 s (2026-09-24). Monitor returns only new flights and price changes. Unreadable dates are free.

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

## Pricing

from $1.90 / 1,000 flight returneds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
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 - Date Range & Price Drop Monitor

Get **Google Flights prices and flights for a route across a range of departure dates** in one run, as clean JSON: every flight Google Flights lists, with price, airline, flight numbers, local departure and arrival time, duration, stops, layovers, aircraft, legroom and CO2. Turn on **Monitor** and scheduled runs return **only new flights and price changes** (with the previous price), so a fare alert, travel deal site or pricing dashboard does not re-download and pay for the same flights every hour.

- **Date range in one run.** Search every departure date from `departureDate` to `departureDateTo` (up to 31 dates). For round trips the length of stay is kept: departing a day later returns a day later.
- **Price drop monitor.** `onlyChanges` returns flights that are new or whose price moved since the last run of the same search, with `previousPrice`, `priceChange` and `priceChangePercent`. Moves under 1% (exchange-rate noise) are not returned unless they add up.
- **What Google Flights shows, nothing guessed.** Prices are the totals Google Flights shows for all travelers, in the currency you choose; the row carries the currency Google actually answered in.
- **No charge when Google Flights cannot be read.** Dates that could not be read come back as free rows that say why. A run where no date could be read is not charged at all.
- **Fast.** One departure date is one Google Flights page: production runs on 2026-09-24 took 2.9-4.3 s for one date and 6.8-9.8 s for 5-7 dates, at 256 MB.

Unofficial. Reads the public Google Flights results page (`google.com/travel/flights`), the same page a person sees without logging in. No login, no cookies, no API key.

### What you get

One row per flight per departure date. Example (production run, 2026-09-24, ORD to MIA on 2026-10-15; the second run of the same search, so the change columns are filled):

```json
{
  "status": "ok",
  "changeType": "unchanged",
  "departureDate": "2026-10-15",
  "returnDate": null,
  "origin": "ORD",
  "destination": "MIA",
  "price": 139,
  "currency": "USD",
  "priceScope": "one-way",
  "travelers": 1,
  "previousPrice": 139,
  "priceChange": 0,
  "priceChangePercent": 0,
  "previousCheckedAt": "2026-09-24T09:37:40.028Z",
  "priceRankOnDate": 1,
  "cheapestPriceOnDate": 139,
  "isCheapestOnDate": true,
  "flightsFoundOnDate": 13,
  "airlines": ["Frontier"],
  "flightNumbers": ["F9 3241", "F9 2676"],
  "departureLocal": "2026-10-15T12:26",
  "arrivalLocal": "2026-10-15T21:59",
  "durationMinutes": 513,
  "stops": 1,
  "layovers": [{ "airport": "DFW", "airportName": "Dallas Fort Worth International Airport", "city": "Dallas", "minutes": 176 }],
  "legs": [
    { "flightNumber": "F9 3241", "airlineCode": "F9", "airline": "Frontier", "departureAirport": "ORD", "arrivalAirport": "DFW",
      "departureLocal": "2026-10-15T12:26", "arrivalLocal": "2026-10-15T15:09", "durationMinutes": 163, "aircraft": "Airbus A321neo", "legroom": "28 in" },
    { "flightNumber": "F9 2676", "airlineCode": "F9", "airline": "Frontier", "departureAirport": "DFW", "arrivalAirport": "MIA",
      "departureLocal": "2026-10-15T18:05", "arrivalLocal": "2026-10-15T21:59", "durationMinutes": 174, "aircraft": "Airbus A320neo", "legroom": "28 in" }
  ],
  "co2Kg": 225,
  "typicalCo2Kg": 206,
  "googleSection": "other",
  "cabinClass": "economy",
  "googleFlightsUrl": "https://www.google.com/travel/flights?tfs=GhoSCjIwMjYtMTAtMTVqBRIDT1JEcgUSA01JQUABSAGYAQI&hl=en&gl=us&curr=USD",
  "watchName": null,
  "checkedAt": "2026-09-24T09:54:49.960Z"
}
```

Checked against the Google Flights page in a browser right after the run: 5 of 5 flights had the same departure and arrival time, airline, price and CO2 on screen (`12:26 PM – 9:59 PM Frontier ... 225 kg CO2e +9% emissions ... $139`). The same check on a round trip in EUR: 5 of 5 (`€495 round trip`).

| Column | Meaning |
|---|---|
| `status` | `ok` for a flight row. Other values are free rows that say why nothing was returned (below). |
| `changeType` | `first-check` (first run of this search), `new` (not seen by this watch on this date before), `price-down`, `price-up` or `unchanged` (also used for a flight seen before but never returned) |
| `departureDate`, `returnDate` | The searched dates. `returnDate` is null for one way |
| `origin`, `destination` | Airports of this flight (with several airports per side, the one this flight uses) |
| `price`, `currency` | Total price for all travelers as Google Flights shows it, and the currency Google answered in |
| `priceScope` | `one-way`, or `round-trip-total` (Google Flights shows outbound flights with the round-trip total before a return flight is picked; return flights are not listed) |
| `travelers` | Number of travelers the price is for |
| `previousPrice`, `priceChange`, `priceChangePercent`, `previousCheckedAt` | The price at which this watch last returned this flight on this date, the difference, and when that was. Null on a first check, a new flight, or a flight this watch has seen but never returned |
| `priceRankOnDate`, `cheapestPriceOnDate`, `isCheapestOnDate`, `flightsFoundOnDate` | Where this flight stands among all flights Google Flights listed for that date |
| `airlines`, `flightNumbers` | Airline names and flight numbers of all segments |
| `departureLocal`, `arrivalLocal` | Local date and time at the departure and arrival airport (no time zone, as shown on Google Flights) |
| `durationMinutes`, `stops`, `layovers` | Total travel time, number of stops, and each layover's airport, city and minutes |
| `legs` | Each flight segment: flight number, airline, airports, times, duration, aircraft, legroom |
| `co2Kg`, `typicalCo2Kg` | Estimated CO2 of this flight and the typical CO2 for the route, as Google Flights shows them |
| `googleSection` | `top` (Google's "Top flights") or `other` |
| `cabinClass` | The searched cabin class |
| `googleFlightsUrl` | The same search on Google Flights |
| `note`, `search` | Free rows only: why nothing (or not everything) was returned, and the search in words |
| `watchName`, `checkedAt` | The watch name you gave, and the time of the check |

#### Free rows (not charged)

| `status` | When |
|---|---|
| `no-flights` | Google Flights shows no flights for this route on this date (or none at or under `maxPrice`) |
| `no-change` | Monitor on: no new flight and no price move on this date since the last run |
| `more-not-returned` | More flights than `maxFlightsPerDate`; says how many were left out (not charged) |
| `bad-input` | The input could not be used, or Google Flights did not accept it (unknown airport code, a date in the past or more than about 330 days ahead) |
| `unreadable` | Google Flights could not be read for this date, even after asking again from other IP addresses |
| `blocked` | Google showed a check page. This Actor does not solve or bypass check pages; it stops and does not search the remaining dates |
| `budget-reached` | The run hit the maximum total charge you set. Rows not returned are not remembered, so the next monitor run returns them |

### Input

| Field | Type | Default | Description |
|---|---|---|---|
| `origin` | string | - | IATA airport code(s) to fly from: `JFK`, or several searched together: `JFK, EWR, LGA`. Leave both From and To empty to run the example route JFK to LAX |
| `destination` | string | - | IATA airport code(s) to fly to: `LAX`, or `LAX, BUR, SNA` |
| `departureDate` | string | `+30` | First departure date: YYYY-MM-DD, or `+N` for N days from today (UTC) so a schedule never runs out of dates |
| `departureDateTo` | string | - | Last departure date (YYYY-MM-DD or `+N`): every date in between is searched (up to 31) |
| `returnDate` | string | - | Makes it a round trip (YYYY-MM-DD or `+N`); with a date range the length of stay is kept |
| `adults` | integer 1-9 | 1 | Adults |
| `children` | integer 0-8 | 0 | Children (2-11). At most 9 travelers. Infants are not supported (Google Flights returns incomplete results for searches with infants) |
| `cabinClass` | `economy`, `premium-economy`, `business`, `first` | `economy` | Cabin class |
| `maxStops` | `any`, `nonstop`, `1`, `2` | `any` | Google Flights' Stops filter |
| `airlines` | list of 2-character codes | - | Only these airlines, for example `DL`, `B6`, `UA` |
| `currency` | string | `USD` | One of the currencies Google Flights offers (USD, EUR, GBP, JPY, CAD, AUD, INR ...) |
| `maxPrice` | number (1 or more) | - | Only flights at or under this total price |
| `maxFlightsPerDate` | integer 1-100 | 20 | At most this many flights per date, in the order below |
| `sortBy` | `cheapest`, `google`, `departure`, `duration` | `cheapest` | Which flights come first |
| `onlyChanges` | boolean | false | Monitor: return only new flights and price changes since the last run of the same search |
| `minPriceChangePercent` | number 0-50 | 1 | Monitor: smallest price move to report, in percent of the last returned price |
| `watchName` | string | - | Runs with the same search and watch name share what they have seen; give two schedules different names |
| `resetMonitoringState` | boolean | false | Forget what this watch remembered, so the run is a first check again |

### Examples

Cheapest flights for every departure date in a week:

```json
{ "origin": "JFK", "destination": "LAX", "departureDate": "2026-11-12", "departureDateTo": "2026-11-18", "maxFlightsPerDate": 5 }
```

Hourly price-drop alert for a round trip (schedule this; each run returns only what changed):

```json
{ "origin": "SFO", "destination": "NRT, HND", "departureDate": "+60", "departureDateTo": "+66", "returnDate": "+70", "onlyChanges": true, "watchName": "tokyo-trip" }
```

Business class for two, in euros, nonstop only:

```json
{ "origin": "LHR", "destination": "JFK", "departureDate": "2026-12-03", "returnDate": "2026-12-10", "adults": 2, "cabinClass": "business", "maxStops": "nonstop", "currency": "EUR" }
```

### How monitoring works

The Actor remembers, per search and `watchName`, which flights it has seen on each departure date and the price at which it last returned each one (in a named key-value store in your account). On the next run it compares: a flight it never saw on that date (or that was above `maxPrice` last time and is now under it) is `new`; a flight whose price moved by at least `minPriceChangePercent` from the price it was last returned at is `price-down` or `price-up`. Flights it saw but never returned (beyond `maxFlightsPerDate` on the first run) and flights above `maxPrice` do not change the remembered price, so you are never sold the same price twice. With `onlyChanges` on, only new and moved flights are returned and charged; a date with nothing to report comes back as one free `no-change` row. **The run start fee is still charged on a run with no changes** (it pays for the check): an hourly watch with no changes costs 720 start fees a month. With a `+N` date range, each day one new departure date enters the window and its flights are returned once as `first-check`. Google Flights sometimes lists a flight in one search and not in the next; such a flight is reported as `new` once and compared by price after that. Two runs of the same search and watch name at the same moment can overwrite each other's memory, so do not overlap schedules of one watch.

### Pricing

Pay per event: a small start fee per run that read at least one flight, plus a fee per flight row returned. Free rows are never charged. A run where no date could be read, or where Google Flights showed no flights, charges nothing. If your maximum total charge for a run has no room for the start fee plus one flight, the run requests nothing and charges nothing.

### Limits

- Prices change all the time; a row is what Google Flights showed at `checkedAt`. The final price is on the airline or agency site.
- Infants are not supported: with infants, Google Flights lists only some flights or none (for example 0 of 36, or 4 of 13), so such searches are refused with a free `bad-input` row.
- Round trips list outbound flights with the round-trip total; return flights are not listed.
- One route per run (several airports per side are fine). Up to 31 departure dates per run.
- Google may show a check page to automated traffic; the Actor does not bypass it and returns a free `blocked` row instead.

### Support

Found a route where the output differs from Google Flights? Open an issue on the Issues tab with the run ID and it will be looked at.

# Actor input Schema

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

IATA airport code of the departure airport, for example JFK. Several airports separated by commas search them together, like Google Flights does (JFK, EWR, LGA). Leave both From and To empty to run the example route JFK to LAX.

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

IATA airport code of the arrival airport, for example LAX. Several airports separated by commas are searched together (LAX, BUR, SNA).

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

First departure date to search: YYYY-MM-DD (for example 2026-11-12), or +N for N days from today in UTC (+30 = 30 days from today, so a schedule never runs out of dates). Empty = +30. Google Flights sells up to about 330 days ahead.

## `departureDateTo` (type: `string`):

Optional. Search every departure date from Departure date to this date, one Google Flights search per date (up to 31 dates in one run). YYYY-MM-DD or +N days from today. Empty = only the Departure date.

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

Optional. Makes the search a round trip: rows are the outbound flights with the round-trip total price, as Google Flights shows them before you pick a return flight (return flights are not listed). With a date range, the length of stay is kept: a trip departing one day later returns one day later. YYYY-MM-DD or +N days from today. Empty = one way.

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

Number of adult travelers (1 to 9). The price is the total for all travelers, as Google Flights shows it. Empty = 1.

## `children` (type: `integer`):

Number of children (2 to 11 years). Empty = 0. Infants are not supported: Google Flights returns incomplete results for searches with infants.

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

Cabin class, as in Google Flights.

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

Google Flights' Stops filter.

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

Optional. 2-character IATA airline codes, for example DL, B6, UA. Only flights of these airlines are searched (Google Flights' Airlines filter). Empty = all airlines.

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

Currency of the prices, one of the currencies Google Flights offers (USD, EUR, GBP, JPY, CAD, AUD, INR and more). The row shows the currency Google actually answered in; if it differs, nothing from that answer is returned.

## `maxPrice` (type: `number`):

Optional. Return only flights at or under this total price (in the chosen currency). Flights above it are not returned and not charged.

## `maxFlightsPerDate` (type: `integer`):

At most this many flights are returned per departure date, in the order chosen below. Google Flights usually lists 10 to 60 flights per route and date. Empty = 20.

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

Which flights come first (and are kept when Flights per date cuts the list).

## `onlyChanges` (type: `boolean`):

On = return only flights that are new or whose price moved since this watch last returned them (same search and watch name), with previousPrice and priceChange. The first run returns the current flights. A date with nothing to report comes back as a free no-change row. The run start fee is still charged on a run with no changes (it pays for the check). Off (or empty) = return the flights every run; the change columns are still filled.

## `minPriceChangePercent` (type: `number`):

With Monitor on, a price move smaller than this percentage of the price this watch last returned is not returned and not charged (prices shown in a currency other than the fare's own move by small amounts with exchange rates). Small moves add up: once the price is this far from the last returned price, it is returned. 0 = report every change. Empty = 1.

## `watchName` (type: `string`):

Optional. Runs with the same search and the same watch name share what they have seen. Give different names to two schedules of the same search, so each gets every change. Letters, digits, dot, dash and underscore.

## `resetMonitoringState` (type: `boolean`):

Forget what this watch remembered for this search at the start of this run, so every flight is a first check again. Turn it off again for scheduled runs.

## Actor input object example

```json
{
  "origin": "JFK",
  "destination": "LAX",
  "departureDate": "+30",
  "cabinClass": "economy",
  "maxStops": "any",
  "currency": "USD",
  "sortBy": "cheapest"
}
```

# Actor output Schema

## `results` (type: `string`):

One row per flight per departure date: price and currency, change since the last check (changeType, previousPrice, priceChange), rank and cheapest price on that date, airlines, flight numbers, local departure and arrival time, duration, stops, layovers, each leg with aircraft and legroom, CO2, and a Google Flights link. A date with no flights, no change, an input Google does not accept, a refused request or a run that hit its maximum charge comes back as a free row that says why.

# 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",
    "departureDate": "+30"
};

// Run the Actor and wait for it to finish
const run = await client.actor("neverempty/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",
    "departureDate": "+30",
}

# Run the Actor and wait for it to finish
run = client.actor("neverempty/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",
  "departureDate": "+30"
}' |
apify call neverempty/google-flights-scraper --silent --output-dataset

```

## MCP server setup

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