# Google Flights Fare Monitor: Cheapest Price & Drop Alerts (`accountable_eel/google-flights-fare-lookup`) Actor

Google Flights price tracker: the cheapest fare for a route and date, Google's own low / typical / high verdict and typical band, as one answer row per check. Monitor fares and get drop alerts in Slack, Discord or Telegram. No login or API key; checks that find nothing are free.

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

## Pricing

from $9.00 / 1,000 fare check 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 Fare Monitor: Cheapest Price & Drop Alerts

Type a route and a date, get one finished answer: the **cheapest fare Google Flights shows**, **Google's own verdict** on whether that price is low, typical or high, the typical price band behind it, and a link to the same search. Switch on monitoring and the actor remembers what each route cost last time, flags a **price drop** and posts it to Slack, Discord or Telegram.

It sells the answer, not 300 raw rows per search. If you want every itinerary Google lists, add them as opt-in rows; if you just want to know "is LHR to JFK cheap this week?", you get one row.

### Who uses this

- **Travellers and travel bloggers** who want a "tell me when it drops" alert for a route and date they care about.
- **Deal newsletters and Telegram / Discord channels** that post cheap fares. One check per route and day, an alert only when the fare moved.
- **Agencies and n8n / Make / Clay builders** who need a fare for a route and date inside a workflow, without a Google account or an API key.

### Try it in 30 seconds

Press **Start** with the pre-filled input. It checks London Heathrow to New York JFK, 60 days from today, and returns one row:

```json
{ "routes": ["LHR-JFK +60d"] }
```

Write your own lines like this, one check per line:

```text
LHR-JFK 2026-12-10                  one way
LHR-JFK 2026-12-10 2026-12-17       return
BUD-LIS +45d                        45 days from today
https://www.google.com/travel/flights?q=Flights+to+JFK+from+LHR+on+2026-12-10+one+way
```

A pasted Google Flights link is read as text for its route and dates. It is never opened.

### What you get

One **answer row** per route and date:

| Field | Meaning |
| --- | --- |
| `cheapestPrice`, `currency` | Lowest fare Google lists (one adult; for a return trip, the full round-trip price). |
| `cheapestAirlines`, `cheapestStops`, `cheapestDurationMin` | Airline(s), number of stops and total travel time of that cheapest itinerary. |
| `cheapestNonstopPrice` | Lowest nonstop fare, or `null` when Google lists no nonstop flight. |
| `bestPrice` | Lowest fare inside Google's own "best flights" group. |
| `itineraryCount` | How many itineraries Google lists for the search. |
| `priceLevel` | `low`, `typical` or `high`: whether the cheapest fare sits below, inside or above Google's typical band for this search. |
| `typicalLow`, `typicalHigh` | The bottom and top of that typical band. |
| `googleFlightsUrl` | The same search on Google Flights, to open and book there. |
| `changeType`, `previousCheapest`, `changePct`, `lowestEverSeen`, `daysSincePrevious` | Monitoring fields (empty unless you turn monitoring on). |

Set **Itinerary rows to add per check** above 0 and the cheapest N itineraries follow as extra rows: airlines, flight numbers, departure and arrival time, duration, stops, layover airports, price, and CO2 estimate where Google shows one.

Other settings, each one verified to change the result: `currency` (USD, EUR, GBP), `cabin` (economy, business, first), `nonstopOnly` (applied after the fetch), `dateWindowDays` (check a run of consecutive days).

### Example output

An answer row for Budapest to Lisbon, with Google saying the price is low:

```json
{
  "rowType": "fare-check",
  "origin": "BUD", "destination": "LIS",
  "departDate": "2026-12-10", "returnDate": null,
  "currency": "USD",
  "cheapestPrice": 70, "cheapestAirlines": "Ryanair", "cheapestStops": 0, "cheapestDurationMin": 235,
  "cheapestNonstopPrice": 70, "bestPrice": 70, "itineraryCount": 9,
  "priceLevel": "low", "typicalLow": 75, "typicalHigh": 205
}
```

### Monitoring: a fare-drop alert in three steps

1. **Turn on monitoring and name the watchlist.** Tick **Track fares across runs**, type a **Watchlist name**, and tick **Only return rows that changed** so a quiet day writes no row and costs nothing. The first run records today's fares; alerts start from the second run (tick **Seed silently** to also skip the first run's rows).
2. **Schedule it.** Save the input as an Apify Task and add a daily Schedule. Each run compares every route and date with the previous run and sets `changeType` to `drop`, `rise`, `level-low` (Google's verdict turned low) or `unchanged`. A drop of 5% or more counts; change the threshold with **Alert when the fare drops by at least (%)**.
3. **Send it somewhere.** Paste a Slack or Discord webhook (or a Telegram bot link) into **Send alerts to**. Every drop and every "verdict turned low" arrives as one message with the route, dates, new and previous price and the Google Flights link. For n8n or Make, leave the format on JSON, or skip the webhook and add an Apify trigger node on run success, then read the dataset: rows with `changeType` = `drop`.

Alerts are only sent for checks that were actually billed, messages are never billed, and a failed message never fails the run (the row's `notifyErrors` says why).

### Limits, stated plainly

- **It returns Google's list as shown.** Whatever Google Flights puts on the page for that search is what you get. It does not search for self-transfer or hidden-city tricks and makes no claim about them.
- **No booking links, no hotels.** `googleFlightsUrl` opens the search on Google Flights. Hotels are not covered.
- **One adult.** Prices are per passenger for one adult. Premium economy is not offered because Google did not understand it in testing.
- **At most 31 checks per run.** A longer list, or a list combined with a wide `dateWindowDays`, is cut at 31; the lines that were cut come back as free "not checked" rows.
- **Google decides what exists.** Dates in the past or too far ahead, and airport codes Google cannot place, come back as not found (free). Google quietly re-reads some wrong codes as another airport; the actor compares Google's airports with yours and returns a not-found row instead of someone else's fare.
- **The verdict follows the search.** With **Nonstop only** on, `cheapestPrice` is the cheapest nonstop fare, while `priceLevel` still describes the whole search.
- **Misses are free.** Not found, blocked, consent page, layout change: nothing is charged.

Pay-per-event. A flat per-run fee covers session/proxy warmup; you're billed
per item only when data is actually found and returned — see
`.actor/pay_per_event.json` for exact prices. A miss is never charged.

### How to use

1. **In the Apify Console.** Open the actor page and click **Start** — the `routes` field is already pre-filled with a working example. Results land in the run's dataset as soon as each item is found.
2. **Via the API.** Call it directly with a POST request — no Console needed once you have an API token:
   ```bash
   curl "https://api.apify.com/v2/acts/accountable_eel~google-flights-fare-lookup/run-sync-get-dataset-items?token=<YOUR_TOKEN>" \
     -X POST \
     -H "Content-Type: application/json" \
     -d '{"routes":["LHR-JFK +60d"]}'
   ```
3. **On a schedule.** Save this actor as an Apify **Task** with the input you want, then add a **Schedule** (hourly, daily, weekly) so it runs on its own — no server of your own required.

### Input

```json
{
  "routes": [
    "LHR-JFK +60d"
  ]
}
```

One check per line: LHR-JFK 2026-12-10 for one way, LHR-JFK 2026-12-10 2026-12-17 for a return. +60d instead of a date means 60 days from today. A pasted google.com/travel/flights link is read as text for its route and dates (it is never opened). Accepted formats: LHR-JFK 2026-12-10, LHR-JFK 2026-12-10 2026-12-17, LHR-JFK +60d, https://www.google.com/travel/flights?q=Flights+to+JFK+from+LHR+on+2026-12-10+one+way.

### Sample output

| query | found | status | rowType | origin | destination | departDate | returnDate | currency | cheapestPrice | cheapestAirlines | cheapestStops | cheapestDurationMin | cheapestNonstopPrice | bestPrice | itineraryCount | priceLevel | typicalLow | typicalHigh | googleFlightsUrl | checkedAt | changeType | previousCheapest | changePct | lowestEverSeen | daysSincePrevious | scrapedAt |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| LHR-JFK +60d | true | OK | <row type> | <from> | <to> | <departure date> | <return date> | <currency> | <cheapest fare> | <cheapest fare: airlines> | <cheapest fare: stops> | \<cheapest fare: duration (min)> | <cheapest nonstop fare> | \<cheapest 'best' fare> | <itineraries listed> | \<google's price verdict> | <typical price: low end> | <typical price: high end> | <google flights link> | <checked at> | <change since last check> | <previous cheapest fare> | \<change (%)> | <lowest fare seen so far> | <days since previous check> | 1970-01-01T00:00:00.000Z |

### Use it from Clay, n8n, Make, or an AI agent

This actor runs synchronously over plain HTTP — call it directly from a script, a workflow tool, or an AI agent, no Apify Console needed once you have an API token.

```bash
curl "https://api.apify.com/v2/acts/accountable_eel~google-flights-fare-lookup/run-sync-get-dataset-items?token=<YOUR_TOKEN>" \
  -X POST \
  -H "Content-Type: application/json" \
  -d '{"routes":["LHR-JFK +60d"]}'
```

**n8n.** Add an HTTP Request node: Method `POST`, URL `https://api.apify.com/v2/acts/accountable_eel~google-flights-fare-lookup/run-sync-get-dataset-items?token=<YOUR_TOKEN>`, Body Content Type `JSON`, JSON Body `{"routes":["LHR-JFK +60d"]}` (swap in an expression from an earlier node for a real value).

**Clay.** Add an "HTTP API" column: Method `POST`, URL `https://api.apify.com/v2/acts/accountable_eel~google-flights-fare-lookup/run-sync-get-dataset-items?token=<YOUR_TOKEN>`, Body `{"routes":["{{fare check}}"]}`, mapping the row's fare check into the `routes` array.

**MCP.** In Claude, Cursor, or any MCP client with the Apify MCP server, ask for "Google Flights Price Tracker: Cheapest Fare & Drop Alerts" — the agent will find and run this actor.

### FAQ

**Is this the official Google Flights API?** No. It reads the public Google Flights page for a route and date without logging in, stays on the `/travel/flights` page that Google's robots.txt allows, and never opens a booking or search path.

**Why does a return trip show only outbound flights?** That is how Google lists a return search: each row is an outbound itinerary priced with the cheapest matching return, so `cheapestPrice` is the full round-trip price.

**What does "typical" mean?** Google compares the cheapest fare with the usual price range for that route and date range. `low` is below the range, `high` is above it, `typical` is inside it. `typicalLow` and `typicalHigh` are the ends of that range.

**Which currencies?** USD, EUR and GBP.

**Why did my check come back "not found"?** The date is in the past or too far ahead, Google could not place an airport code, or (with Nonstop only) no nonstop flight exists. It costs nothing.

# Actor input Schema

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

One check per line: LHR-JFK 2026-12-10 for one way, LHR-JFK 2026-12-10 2026-12-17 for a return. +60d instead of a date means 60 days from today. A pasted google.com/travel/flights link is read as text for its route and dates (it is never opened). Accepted formats: LHR-JFK 2026-12-10, LHR-JFK 2026-12-10 2026-12-17, LHR-JFK +60d, https://www.google.com/travel/flights?q=Flights+to+JFK+from+LHR+on+2026-12-10+one+way. You're only charged for the ones we actually find — a miss costs nothing.

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

Currency of the prices. USD, EUR and GBP are supported.

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

Economy is Google's default. Business and first class change the fares Google shows (premium economy is not offered: Google did not understand it in testing).

## `nonstopOnly` (type: `boolean`):

Keeps only itineraries with no stop before picking the cheapest fare. Applied after the fetch, so Google's price verdict still describes the whole search. A route with no nonstop flight comes back as not found and is not charged.

## `maxItineraries` (type: `integer`):

0 returns only the answer row. Above 0, the cheapest N itineraries are added as extra rows (airline, flight numbers, times, stops, layovers, price). Up to 50. Each itinerary row is billed at the itinerary price.

## `dateWindowDays` (type: `integer`):

1 checks only the date you wrote. 7 checks that date and the next 6 as separate checks (a return trip keeps its length of stay). At most 31 checks per run in total, so a long list plus a wide window is cut off, and the cut-off lines come back as free not-checked rows.

## `deltaMode` (type: `boolean`):

Turns the actor into a fare monitor. Each run compares the cheapest fare of every route and date with the previous run's, and fills in 'Change since last check', previous fare, change % and the lowest fare seen so far. Schedule it daily and combine with 'Only return rows that changed' and 'Send alerts to'.

## `deltaName` (type: `string`):

Leave empty and one is derived from the currency, cabin and nonstop setting. Type your own name to keep one memory across changes, or to run several independent watchlists.

## `alertOnDropPct` (type: `integer`):

A drop (or rise) smaller than this counts as unchanged.

## `alertOnLevelLow` (type: `boolean`):

Sends an alert the first time Google's price verdict for a route and date turns low, even when the drop is smaller than the percentage above.

## `alertOnChangeOnly` (type: `boolean`):

With monitoring on, a check whose fare did not move (changeType unchanged) writes no row and is not billed. New, drop, rise and level-low checks are returned.

## `skipFirstRun` (type: `boolean`):

The first check of a route and date has nothing to compare with. Turn this on to remember it without returning or billing it, so alerts start from the second check.

## `notifyUrl` (type: `string`):

Optional. After a check is billed, each fare drop and each 'verdict turned low' is posted here as one message with route, dates, price, previous price and a Google Flights link. Discord or Slack: paste the channel's webhook URL. Telegram: https://api.telegram.org/bot<token>/sendMessage?chat_id=<chat id>. Any other URL receives JSON. Messages are free, and a failed message never fails the run (the row's notifyErrors says why). Stored encrypted, never logged or written to the results.

## `notifyFormat` (type: `string`):

Leave on 'Detect from the link': discord.com links get Discord messages, hooks.slack.com Slack, api.telegram.org Telegram, anything else JSON. Pick one only to override that.

## `maxNotificationsPerRun` (type: `integer`):

Up to 100. After this, one 'more alerts' message says the rest are in the run's dataset. 0 sends only that one message.

## `testRun` (type: `boolean`):

Turn this on to test your input on a small sample before running the full list. Turn it off to process everything.

## `onlyFound` (type: `boolean`):

Only keep rows where something was actually found. Misses are always free, whether or not you show them here.

## `includeKeywords` (type: `array`):

Optional. Only keep results that mention at least one of these words (e.g. a job title, a city, a product name). Leave empty to keep everything.

## `excludeKeywords` (type: `array`):

Optional. Drop any result that mentions one of these words. Leave empty to skip nothing.

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

Optional. Stop the run once this many results have been found — useful for a quick, cheap sample. Leave blank for no limit.

## `columns` (type: `array`):

Choose which pieces of information to include in each result row. All are included by default.

## `maxConcurrency` (type: `integer`):

Parallel requests. Keep conservative — this target has no browser fallback, so getting blocked costs more than slow-and-steady.

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

Apify Proxy config. Residential recommended for anti-bot-sensitive targets.

## Actor input object example

```json
{
  "routes": [
    "LHR-JFK +60d"
  ],
  "currency": "USD",
  "cabin": "economy",
  "nonstopOnly": false,
  "maxItineraries": 0,
  "dateWindowDays": 1,
  "deltaMode": false,
  "deltaName": "",
  "alertOnDropPct": 5,
  "alertOnLevelLow": true,
  "alertOnChangeOnly": false,
  "skipFirstRun": false,
  "notifyFormat": "auto",
  "maxNotificationsPerRun": 20,
  "testRun": false,
  "onlyFound": false,
  "includeKeywords": [],
  "excludeKeywords": [],
  "columns": [
    "rowType",
    "origin",
    "destination",
    "departDate",
    "returnDate",
    "currency",
    "cheapestPrice",
    "cheapestAirlines",
    "cheapestStops",
    "cheapestDurationMin",
    "cheapestNonstopPrice",
    "bestPrice",
    "itineraryCount",
    "priceLevel",
    "typicalLow",
    "typicalHigh",
    "googleFlightsUrl",
    "checkedAt",
    "changeType",
    "previousCheapest",
    "changePct",
    "lowestEverSeen",
    "daysSincePrevious"
  ],
  "maxConcurrency": 2,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# Actor output Schema

## `results` (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": [
        "LHR-JFK +60d"
    ],
    "includeKeywords": [],
    "excludeKeywords": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("accountable_eel/google-flights-fare-lookup").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": ["LHR-JFK +60d"],
    "includeKeywords": [],
    "excludeKeywords": [],
}

# Run the Actor and wait for it to finish
run = client.actor("accountable_eel/google-flights-fare-lookup").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": [
    "LHR-JFK +60d"
  ],
  "includeKeywords": [],
  "excludeKeywords": []
}' |
apify call accountable_eel/google-flights-fare-lookup --silent --output-dataset

```

## MCP server setup

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

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/14kNM7z8EGizlKSQQ/builds/USgQTHQe2Z2Jeb9rp/openapi.json
