DEV Community

Data Xplorer
Data Xplorer

Posted on AI-assisted

Build a Google Flights Price Tracker in Python (No Official API Needed)

Google Flights has the best flight search on the web and no public API. If you want to watch a route and get a message when the fare drops, you have to get the data some other way.

In this tutorial we build a small tracker in about 60 lines of Python. It:

  1. searches Google Flights for the routes you care about,
  2. stores the cheapest fare of each route in a CSV file, so you build a price history,
  3. sends a Telegram message when a fare goes under your target price or hits a new low.

Disclosure: I built the scraper used here, Google Flights Scraper on Apify. The tracker logic works the same with any source that returns a price per flight.

What you need

  • Python 3.9+
  • An Apify account. The free plan includes monthly credits, and one search of about 100 flights costs roughly $0.15 on that plan.
  • A Telegram bot token and your chat ID (optional, for alerts). Create a bot with @BotFather.
pip install apify-client requests
Enter fullscreen mode Exit fullscreen mode

Put your secrets in environment variables:

export APIFY_TOKEN="your_apify_token"
export TELEGRAM_TOKEN="your_bot_token"
export TELEGRAM_CHAT_ID="your_chat_id"
Enter fullscreen mode Exit fullscreen mode

Step 1: search Google Flights from Python

The scraper takes a list of searches. Each one has from, to, a departureDate and an optional returnDate. Dates can be absolute (2026-12-26) or relative (30 days), which is what makes a daily tracker possible: every run looks 30 days ahead without you touching the code.

import os
from apify_client import ApifyClient

client = ApifyClient(os.environ["APIFY_TOKEN"])

run = client.actor("data_xplorer/google-flights-scraper").call(run_input={
    "flights": [
        {"from": "CDG", "to": "JFK", "departureDate": "2026-12-26", "returnDate": "2027-01-04"},
        {"from": "LHR", "to": "DXB", "departureDate": "30 days"},
    ],
    "currency": "EUR",
    "country": "FR",
    "maxStops": "1",
})

flights = list(client.dataset(run["defaultDatasetId"]).iterate_items())
print(len(flights), "flights")
print(flights[0]["price"], flights[0]["airline"], flights[0]["flightNumbers"])
Enter fullscreen mode Exit fullscreen mode

Each item is one flight, with fields such as price, currency, airline, flightNumbers, departureAirport, arrivalAirport, departureDate, departureTime, arrivalTime, duration, stops, layovers, co2EmissionsKg and googleFlightsUrl.

Two details matter for a tracker:

  • By default the scraper returns the full list of itineraries, including the ones Google hides behind "View more flights". The cheapest fare is sometimes in that hidden part.
  • Fares depend on the country you search from. Set country and currency once and keep them, or your history will mix prices that are not comparable.

Step 2: keep the cheapest fare per route

We group the flights by route and date, then keep the cheapest one.

def cheapest_by_route(flights):
    best = {}
    for f in flights:
        if not f.get("price"):
            continue
        key = (f["departureAirport"], f["arrivalAirport"], f["departureDate"])
        if key not in best or f["price"] < best[key]["price"]:
            best[key] = f
    return best
Enter fullscreen mode Exit fullscreen mode

Step 3: store a price history

A CSV file is enough. One line per route per run.

import csv
from datetime import date
from pathlib import Path

HISTORY = Path("price_history.csv")

def previous_low(key):
    if not HISTORY.exists():
        return None
    with HISTORY.open() as fh:
        prices = [
            float(row["price"]) for row in csv.DictReader(fh)
            if (row["from"], row["to"], row["departureDate"]) == key
        ]
    return min(prices) if prices else None

def save(key, flight):
    new_file = not HISTORY.exists()
    with HISTORY.open("a", newline="") as fh:
        writer = csv.writer(fh)
        if new_file:
            writer.writerow(["checkedOn", "from", "to", "departureDate", "price", "currency", "airline"])
        writer.writerow([date.today(), *key, flight["price"], flight["currency"], flight["airline"]])
Enter fullscreen mode Exit fullscreen mode

Step 4: send an alert

import requests

TARGETS = {("CDG", "JFK"): 450, ("LHR", "DXB"): 300}  # your target price per route

def notify(text):
    token, chat = os.environ.get("TELEGRAM_TOKEN"), os.environ.get("TELEGRAM_CHAT_ID")
    if not token or not chat:
        print(text)
        return
    requests.post(
        f"https://api.telegram.org/bot{token}/sendMessage",
        json={"chat_id": chat, "text": text},
        timeout=30,
    )
Enter fullscreen mode Exit fullscreen mode

Step 5: put it together

for key, flight in cheapest_by_route(flights).items():
    origin, destination, day = key
    price = flight["price"]
    low = previous_low(key)
    target = TARGETS.get((origin, destination))

    reasons = []
    if target and price <= target:
        reasons.append(f"under your target of {target}")
    if low is not None and price < low:
        reasons.append(f"new low (was {low:g})")

    if reasons:
        notify(
            f"{origin} -> {destination} on {day}: {price} {flight['currency']} "
            f"with {flight['airline']} ({', '.join(reasons)})\n{flight['googleFlightsUrl']}"
        )
    save(key, flight)
Enter fullscreen mode Exit fullscreen mode

Note that save runs after the comparison, so today's price is compared with the past only.

Step 6: run it every day

On Linux or macOS, add a cron entry to run the script every morning at 8:00:

0 8 * * * cd /path/to/tracker && /usr/bin/python3 tracker.py
Enter fullscreen mode Exit fullscreen mode

If you do not want a machine running, create a schedule in the Apify Console for the scraper itself and read the latest dataset from your script, or send the results to Google Sheets, Make, Zapier or n8n.

Going further

  • Find the cheapest day, not just the cheapest flight. Set "priceCalendar": true to get the cheapest price of each departure date around your date, up to 330 days. The results appear in a separate "Price calendar" tab of the run.
  • Know where to book. Set "bookingOptions": true to get every seller (airline or travel agency) with its price and booking link for the first results.
  • Narrow the search. Useful filters include maxPrice, airlines, excludeAirlines, maxDurationHours, maxLayoverHours and checkedBags.
  • Control the cost. maxResultsPerSearch caps the number of flights returned per search.

FAQ

Is there an official Google Flights API?

No. Google closed its QPX Express API in 2018 and has not released a replacement. A scraper is the practical way to get this data programmatically.

How often should I check prices?

Once a day is enough for most routes. Fares move more in the last weeks before departure, so you can check twice a day then.

Is scraping Google Flights legal?

The data is public and contains no personal information. Rules depend on your country and on what you do with the data, so check what applies to your use case.


The scraper used in this tutorial is here: Google Flights Scraper on Apify. If something is missing for your use case, open an issue on the Actor page.

The complete script is on GitHub: google-flights-price-tracker.

Top comments (0)