# Google Flights Scraper: Fares, Cheapest Days, Price Drops (`dottti/google-flights-prices`) Actor

Google Flights prices for any route and date: airlines, flight numbers, times, duration, stops, layovers, CO2 and Google's price level. One-way or round trip, cabin, stops filter, cheapest-day scans and price-drop alerts versus your last run.

- **URL**: https://apify.com/dottti/google-flights-prices.md
- **Developed by:** [Dottti](https://apify.com/dottti) (community)
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.50 / 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: Fares, Cheapest Days, Price Drops

Get flight prices from Google Flights for any route and date. Each row is one flight option with its price, airlines, flight numbers, departure and arrival times, duration, stops, layovers and CO2. Run it on a schedule and every row shows how the price moved since your last run.

It works for any pair of airports Google Flights covers. It is tested on JFK-LHR, AMM-DXB, RUH-CAI, RUH-LHR, DXB-BOM, CAI-JED, DOH-IST, JED-AMM, SFO-NRT and LAX-SYD.

### Who uses it

- **Travel agencies**: quote fares for client routes and dates in your currency.
- **Corporate travel teams**: watch the routes your staff fly and get told when a fare drops.
- **Fare alert and deal sites**: track many routes every day and publish the drops.
- **Analysts**: study fares by route, season, cabin and number of stops.

### What you get per flight

- Route, trip type, departure and return dates, cabin, passengers
- Price and currency. The price is the total for all passengers. For a round trip it is Google's round-trip total.
- Airlines, flight numbers, departure and arrival time (local), duration, number of stops
- Layover airports, cities and minutes, with a flag when you must change airport
- Each segment: flight number, airline, airports, times, aircraft, legroom, CO2, codeshare numbers
- CO2 in kg, the typical CO2 for the route, and the difference in percent
- Google's flags: **isBest** (Google's "best flights" list) and **isCheapest** (lowest price in the search)
- Google's price level for the search ("low", "typical", "high") and the typical price range
- Price watch: **priceStatus** (new, dropped, rose, unchanged), **previousPrice**, **priceChange**, **priceChangePercent**
- A Google Flights link for the same search

Each search also gives one free status row. It says how many flights Google returned, the cheapest price, and how the cheapest price moved since the last run.

### Input examples

#### One-way, fixed date

```json
{
    "routes": ["JFK-LHR", "AMM-DXB"],
    "departureDate": "2026-12-04",
    "adults": 1,
    "currency": "USD"
}
```

#### Round trip, 2 adults, Jordanian dinar

```json
{
    "routes": ["AMM-DXB", "RUH-CAI"],
    "departureDaysFromNow": 30,
    "tripLengthDays": 7,
    "adults": 2,
    "currency": "JOD",
    "country": "jo"
}
```

#### Business class, nonstop only, Saudi riyal

```json
{
    "routes": ["RUH-LHR"],
    "departureDate": "2026-12-15",
    "cabin": "business",
    "stops": "nonstop",
    "currency": "SAR",
    "country": "sa"
}
```

#### Cheapest day to fly in a two-week window

The actor searches every departure day from `departureDate` to `departureDateTo`. With the default output you get one row per day: that day's cheapest flight.

```json
{
    "routes": ["AMM-DXB", "RUH-LHR"],
    "departureDate": "2026-12-01",
    "departureDateTo": "2026-12-14",
    "tripLengthDays": 7,
    "currency": "SAR",
    "country": "sa"
}
```

#### Input tips

- Write a route as two IATA airport codes: `JFK-LHR`, `amm dxb` and `CAI to JED` all work.
- Leave the departure date empty and set **Days from today** for a schedule that always looks the same distance ahead.
- **Stops** sets the most stops allowed: any, nonstop, 1 stop or fewer, 2 stops or fewer.
- **Max flights per search** limits rows per route and date. Google's best flights come first.

### Output example

This row comes from a real run on 2026-11-04 (JFK-LHR, 4 December 2026, one adult, economy). It was the second run of the same search, so the price watch fields are filled.

```json
{
    "type": "flight",
    "route": "JFK-LHR",
    "tripType": "one_way",
    "departureDate": "2026-12-04",
    "returnDate": null,
    "cabin": "economy",
    "passengers": 1,
    "price": 298,
    "currency": "USD",
    "priceType": "one_way_total_all_passengers",
    "rank": 6,
    "isBest": false,
    "isCheapest": true,
    "airlines": "Aer Lingus",
    "flightNumbers": "EI 104, EI 152",
    "departureAirport": "JFK",
    "arrivalAirport": "LHR",
    "departureTime": "2026-12-04T17:00",
    "arrivalTime": "2026-12-05T08:05",
    "durationMinutes": 605,
    "duration": "10h 5m",
    "stops": 1,
    "layoverAirports": "DUB",
    "layoverMinutes": 125,
    "layovers": [
        { "airport": "DUB", "airportName": "Dublin Airport", "city": "Dublin", "minutes": 125, "changeOfAirport": false }
    ],
    "aircraft": "Airbus A330, Airbus A320neo",
    "co2Kg": 392,
    "co2TypicalKg": 434,
    "co2DiffPercent": -10,
    "priceLevel": "typical",
    "typicalPriceLow": 170,
    "typicalPriceHigh": 470,
    "priceStatus": "unchanged",
    "previousPrice": 298,
    "priceChange": 0,
    "priceChangePercent": 0,
    "previousCheckAt": "2026-11-04T10:55:40.100Z",
    "segments": [
        {
            "flightNumber": "EI 104", "airline": "Aer Lingus", "from": "JFK", "to": "DUB",
            "departure": "2026-12-04T17:00", "arrival": "2026-12-05T04:30", "durationMinutes": 390,
            "aircraft": "Airbus A330", "legroom": "31 in", "co2Kg": 340, "alsoSoldAs": []
        },
        {
            "flightNumber": "EI 152", "airline": "Aer Lingus", "from": "DUB", "to": "LHR",
            "departure": "2026-12-05T06:35", "arrival": "2026-12-05T08:05", "durationMinutes": 90,
            "aircraft": "Airbus A320neo", "legroom": "29 in", "co2Kg": 52, "alsoSoldAs": []
        }
    ],
    "googleFlightsUrl": "https://www.google.com/travel/flights/search?tfs=GhoSCjIwMjYtMTItMDRqBRIDSkZLcgUSA0xIUkABSAGYAQI&hl=en&gl=us&curr=USD",
    "scrapedAt": "2026-11-04T11:10:30.695Z"
}
```

The free status row for the same search:

```json
{
    "type": "search_status",
    "route": "JFK-LHR",
    "departureDate": "2026-12-04",
    "status": "ok",
    "flightsFound": 21,
    "flightsWithPrice": 21,
    "flightsDelivered": 20,
    "cheapestPrice": 298,
    "previousCheapestPrice": 298,
    "cheapestPriceChange": 0,
    "typicalPriceLow": 170,
    "typicalPriceHigh": 470,
    "priceLevel": "typical",
    "charged": false
}
```

A date scan gives one free status row per route. This one comes from a real 14-day round-trip scan in SAR:

```json
{
    "type": "scan_status",
    "route": "AMM-DXB",
    "status": "ok",
    "firstDepartureDate": "2026-12-01",
    "lastDepartureDate": "2026-12-14",
    "tripLengthDays": 7,
    "daysScanned": 14,
    "daysWithPrices": 14,
    "daysFailed": [],
    "cheapestDepartureDate": "2026-12-01",
    "cheapestReturnDate": "2026-12-08",
    "cheapestPrice": 1038,
    "charged": false
}
```

Status values: `ok`, `no_results` (Google lists no flights), `no_prices` (flights listed without a price), `invalid_search` (Google could not use an airport code), `failed` (Google kept refusing), `partial_cost_limit` and `skipped_cost_limit`.

### Daily price tracking

1. Set fixed dates (departureDate, and returnDate for a round trip).
2. Give the watch a name in **Watch list name**, for example `amm-dxb-december`.
3. Schedule the actor in Apify (Schedules, then for example every day at 08:00).

Every run saves the prices it saw in the key-value store `google-flights-price-watch`. The next run with the same name compares each flight by its flight numbers:

- `priceStatus`: `new`, `dropped`, `rose` or `unchanged`
- `previousPrice`, `priceChange` and `priceChangePercent`
- In the status row: `previousCheapestPrice` and `cheapestPriceChange` for the whole search

Turn on **only output new or changed prices** to get changes only, or **only output price drops** for drop alerts. Rows that are left out are not charged. Saved prices for past dates are removed automatically.

With **Days from today** the date moves every day, so each run prices a new date. Use fixed dates for tracking.

### Pricing

Pay per result. Platform usage is included in the price.

- **$0.50 per 1,000 flights** (event `flight`). One flight option with a price.
- **$4.00 per 1,000 cheapest days** (event `cheapest-day`). One row per scanned day with that day's cheapest flight. Each day is a full Google search, so it costs more than a flight row. Choose **All flights for every day** in a scan to get every flight at the flight price.
- Free: status rows, flights without a price, unchanged or risen flights left out by the price-watch filters, failed searches.

Example: 10 routes with 20 flights each cost $0.10. A 30-day cheapest-day scan of one route costs $0.12.

You can set a maximum cost per run in Apify. The actor stops when the next row would go over it.

### Speed and limits

- About 1 to 10 seconds per search, depending on retries. 10 routes took 85 seconds in the Apify cloud. The default input (2 routes) finishes in about 5 seconds.
- Google lists about 10 to 35 flights per route and date. Up to 100 rows per search.
- Departure up to 330 days ahead. Up to 9 passengers (adults plus children). A date scan covers up to 62 days. One run does up to 500 searches.
- Infants are not supported. Google's public results page returns no flights for searches with infants.
- Round trips list the outbound flights with Google's round-trip total. The return flight is chosen on Google's next page and is out of scope for now.
- Booking options per seller, multi-city trips and airline filters are out of scope for now.
- Prices depend on the market. Set **Country** and **Currency** to match the traveller.
- Google sometimes lists a flight without a price, mostly for searches with children. Those flights are skipped and counted in the status row.

### Reliability

- Requests start at least 1 second apart. Two searches run at a time by default.
- Google sometimes answers with a page that has no flight list, mostly to cloud IP addresses. The actor treats that as a refusal: it starts a new session, waits, and then moves up the proxy ladder: no proxy, Apify datacenter, Apify residential.
- The run always ends with status SUCCEEDED. Each search gets a status row, and failed searches are free.
- The run summary (key-value store record `RUN_SUMMARY`) shows requests, HTTP status codes, retries, the proxy tier used and bytes per search.

### Data and privacy

The actor reads public Google Flights result pages. It does not log in, and it does not solve captchas. Rows hold flight and fare information only, with no personal data. Check the rules that apply to your use of the data, such as Google's terms and data protection law in your country.

# Actor input Schema

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

One route per line: origin and destination 3-letter IATA airport codes, for example JFK-LHR or AMM-DXB.

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

Format 2026-12-20. Empty: the date is set by "Days from today" below.

## `departureDaysFromNow` (type: `integer`):

Used only when the departure date is empty. 30 = fly 30 days from the run date. Good for daily schedules.

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

Format 2026-12-27. Empty and trip length 0: one-way.

## `tripLengthDays` (type: `integer`):

Used when the return date is empty. 7 = return 7 days after departure. 0 = one-way. Also sets the return day for every day of a date scan.

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

Cheapest-day scan. Format 2026-12-31. Searches every departure day from the departure date to this date, up to 62 days. Empty: one date only.

## `scanOutput` (type: `string`):

For a date scan: one row per day with that day's cheapest flight, or every flight for every day.

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

Prices are the total for all passengers.

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

Adults plus children: at most 9. Infants are not supported.

## `cabin` (type: `string`):

Cabin class to price.

## `stops` (type: `string`):

Most stops allowed.

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

3-letter code, for example USD, SAR, AED, JOD, EUR, GBP.

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

2-letter country code for the Google market, for example us, sa, ae, jo, gb.

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

Most flight rows per route and date. Google's best flights come first. Google usually lists 10 to 35 flights.

## `onlyChanged` (type: `boolean`):

Every run saves the prices it saw. On: flights with the same price as last run are left out and not charged. Each row carries priceStatus, previousPrice and priceChange either way.

## `onlyDrops` (type: `boolean`):

On: only flights that got cheaper since the last run are output and charged.

## `watchListName` (type: `string`):

Name for the saved price state, for example amm-dxb-december. Runs with the same name compare against each other. Empty: a name is made from your routes, cabin, passengers, stops and currency.

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

Leave empty for the automatic ladder: no proxy first, then Apify datacenter, then Apify residential only if Google keeps refusing.

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

Move to the next proxy tier when Google keeps refusing.

## `allowResidentialFallback` (type: `boolean`):

Residential proxy is the most reliable tier.

## `concurrency` (type: `integer`):

Searches in flight at once. Requests still start at least the delay below apart.

## `requestDelayMs` (type: `integer`):

Minimum gap between request starts.

## `maxRetries` (type: `integer`):

Attempts per search before it is marked failed. Each retry waits longer and may switch session or proxy tier.

## Actor input object example

```json
{
  "routes": [
    "JFK-LHR",
    "AMM-DXB"
  ],
  "departureDaysFromNow": 30,
  "tripLengthDays": 0,
  "scanOutput": "cheapest_per_day",
  "adults": 1,
  "children": 0,
  "cabin": "economy",
  "stops": "any",
  "currency": "USD",
  "country": "us",
  "maxResults": 20,
  "onlyChanged": false,
  "onlyDrops": false,
  "autoProxyFallback": true,
  "allowResidentialFallback": true,
  "concurrency": 2,
  "requestDelayMs": 1000,
  "maxRetries": 4
}
```

# Actor output Schema

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

Flight rows with price, airlines, flight numbers, times, stops, layovers, CO2 and price change, plus status rows.

## `runSummary` (type: `string`):

Status per search, price-watch counts, requests, HTTP status codes, retries, proxy tier and bytes per search.

# 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": [
        "JFK-LHR",
        "AMM-DXB"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("dottti/google-flights-prices").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": [
        "JFK-LHR",
        "AMM-DXB",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("dottti/google-flights-prices").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": [
    "JFK-LHR",
    "AMM-DXB"
  ]
}' |
apify call dottti/google-flights-prices --silent --output-dataset

```

## MCP server setup

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

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/VRB5LWxBBjh0GqDuh/builds/1hqkkb8EKsJFnlmsI/openapi.json
