kestrel actors docs

kestrel actors › Hotel & flight prices

Flight Price Tracker — Cheapest Day to Fly & Airfare Alerts

A flight price tracker that reads Google Flights and returns the cheapest fare for every departure day in your window — one row per route and day, with the airline, stops, times and duration behind that price. Run it once to see the cheapest day to fly; schedule it and you have airfare price tracking you own, with a maxPrice line that turns any run into a flight price alert. No API key, no login, no browser.

Run Flight Price Tracker on Apify →

Flight price tracker pricing: what one row costs

EventWhat it isPriceCharged for
fareFare row (cheapest of the day)$0.004One departure day of one route: its cheapest Google Flights itinerary with price, airline, stops, times, duration and window rank. Days with no fare, days over your maxPrice and status rows are free.

Pay per event, one event per delivered fare row — at most one per route and departure day. Days on which Google lists nothing, days dropped by maxPrice, failed days and status rows are free, and nothing a spending limit refused is ever delivered.

Some worked sizes at the listed per-row price:

The verified runs behind this listing used $0.0002–$0.003 of platform compute each. Exact per-row pricing is on this page's pricing tab.

Flight price tracker input reference

Every field of Flight Price Tracker, straight from the actor's input schema.

FieldTypeDefaultWhat it does
routesarrayrequiredRoutes as airport codes, one per line: "LIS-LHR", "JFK-LAX". Google's city codes work too (NYC-LON searches every New York and London airport). Each route is tracked across every day of the window.
departDatestringrequiredThe first day of the window: YYYY-MM-DD, or relative so a scheduled run never goes stale: "30 days", "6 weeks", "tomorrow". Past dates are rejected.
daysinteger14How many consecutive departure days to price, starting on the first day. 14 = two weeks of fares; 30 = a month. One search and at most one billed row per day. Range 1-60.
tripLengthDaysinteger00 = one-way fares. N = every day is priced as a round trip returning N days after departure, and the price is the total for both legs. Range 0-60.
seatstring (enum)"economy"Cabin to price. Business and first are a different, smaller market than economy. One of: economy, premium_economy, business, first.
adultsinteger1Adult passengers. Every price is the total for all of them. Range 1-9.
maxStopsstring (enum)"any"Limit connections before picking the day's cheapest fare. "Nonstop only" tracks the fare most travellers actually want; "Any" takes the cheapest of everything Google lists. One of: any, nonstop, one_or_fewer, two_or_fewer.
maxPricenumber00 = deliver every day. N = deliver only days whose cheapest fare is at or under N in the currency below. Days over the line are dropped BEFORE billing, so an alert run that finds nothing costs nothing. Minimum 0.
currencystring"USD"ISO code for every price, e.g. USD, EUR, GBP. If Google prices in another currency for your market the day is reported as an error rather than billed in the wrong one.
marketstring"us"Two-letter country code for the Google market (gl), e.g. us, de, gb. Fares and available airlines differ by market. Results are always read in English.
sessionsinteger4How many proxy sessions (egress IPs) run in parallel. More is faster; each one is paced separately. Range 1-16.
perIpnumber0.5Pace for each session. Google tolerates about 0.5/s per IP; raising it causes retries, not speed. Range 0.1-2.
proxyConfigurationobject{"useApifyProxy": true}Apify Proxy is required: Google blocks datacentre traffic that is not rotated. The default group works.

Example input

{ "routes": ["JFK-LHR", "EWR-LHR"], "departDate": "14 days", "days": 30, "maxStops": "nonstop", "maxPrice": 400, "currency": "USD" }
{ "routes": ["LIS-LHR", "LIS-CDG"], "departDate": "tomorrow", "days": 30, "currency": "EUR", "market": "pt" }

Flight price tracker output fields

Two row types share one dataset: fare (one per route and departure day — that day's cheapest itinerary) and status (one per route; never charged).

Dataset views in the Apify Console: Overview, Cheapest fare by day, Run status.

FieldTypeWhat it holds
typestringRow type: fare or status.
routestringThe route as given, e.g. "LIS-LHR".
originstringOrigin airport code on the itinerary.
destinationstringDestination airport code on the itinerary.
tripstringone_way or round_trip.
depart_datestringDeparture day, YYYY-MM-DD. On status rows: the first day of the window.
return_datestringReturn day for round trips, null for one-way.
seatstringCabin priced: economy, premium_economy, business or first.
adultsintegerAdults the price covers.
currencystringCurrency of the price (ISO code), as Google actually priced it.
pricenumberThe day's cheapest fare for all passengers. For round trips this is the total for both legs.
price_displaystringPrice exactly as Google states it, e.g. "127 US dollars".
round_trip_totalbooleanTrue when the price covers both legs of a round trip.
airlinestringOperating airline of the cheapest itinerary; the first when several operate it.
airlinesarrayEvery airline operating the cheapest itinerary.
stopsintegerStops on the cheapest itinerary; 0 is nonstop.
depart_timestringDeparture time, local to the origin.
arrive_timestringArrival time, local to the destination.
depart_daystringDeparture day as Google states it.
arrive_daystringArrival day as Google states it — later than the departure day on overnight itineraries.
durationstringTotal travel time as Google states it, e.g. "2 hr 55 min".
duration_minutesintegerTotal travel time in minutes — the sortable form.
layoversarrayEach layover: index, of, minutes, airport, city. Null on nonstop itineraries.
layover_airportsarrayLayover airport names, in order.
overnight_layoverbooleanTrue when Google flags an overnight layover.
co2_kgintegerGoogle's carbon estimate for the itinerary, in kilograms.
google_urlstringThe Google Flights URL for that day's search — opens the same results.
itineraries_seenintegerHow many itineraries Google listed for that day; the fare row is the cheapest of them.
cheapest_in_windowbooleanTrue on the one day of the route's window with the lowest fare — the cheapest day to fly.
rank_in_windowinteger1 = the cheapest day of the window, 2 = the next, and so on. Ranked over every day searched, before the maxPrice filter.
days_searchedintegerStatus rows: departure days searched for this route.
faresintegerStatus rows: fare rows delivered (and billed) for this route.
filteredintegerStatus rows: days dropped by maxPrice — never billed.
no_resultsintegerStatus rows: days on which Google listed no itinerary.
errorsintegerStatus rows: days whose search failed.
cheapestnumberStatus rows: the lowest fare found across the window.
cheapest_datestringStatus rows: the day that lowest fare departs.
statusstringStatus rows: ok, no_results or error.
errorstringStatus rows: the first failure message, null when every day succeeded.
fetched_atstringUTC timestamp when the row was fetched.

Example row from a real run

Taken from the actor's README — this is the shape your dataset holds.

{
  "type": "fare", "route": "LIS-LHR", "origin": "LIS", "destination": "LHR", "trip": "one_way",
  "depart_date": "2026-10-05", "return_date": null, "seat": "economy", "adults": 1, "currency": "USD",
  "price": 127, "price_display": "127 US dollars", "round_trip_total": false,
  "airline": "Tap Air Portugal", "airlines": ["Tap Air Portugal"], "stops": 0,
  "depart_time": "8:00 PM", "arrive_time": "10:55 PM", "depart_day": "Monday, October 5", "arrive_day": "Monday, October 5",
  "duration": "2 hr 55 min", "duration_minutes": 175,
  "layovers": null, "layover_airports": null, "overnight_layover": false, "co2_kg": 123,
  "google_url": "https://www.google.com/travel/flights?tfs=...",
  "itineraries_seen": 12, "cheapest_in_window": true, "rank_in_window": 1,
  "fetched_at": "2026-08-29T06:25:14+00:00"
}
{
  "type": "status", "route": "LIS-LHR", "trip": "one_way", "seat": "economy", "currency": "USD",
  "depart_date": "2026-10-05", "return_date": null,
  "days_searched": 7, "fares": 7, "filtered": 0, "no_results": 0, "errors": 0,
  "cheapest": 127, "cheapest_date": "2026-10-05", "status": "ok", "error": null,
  "fetched_at": "2026-08-29T06:25:20+00:00"
}

Flight price tracker in Python, JavaScript and curl

Python

from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("kestrel/flight-price-tracker").call(run_input={
    "routes": ["LIS-LHR"], "departDate": "30 days", "days": 14, "currency": "EUR",
})
for row in client.dataset(run["defaultDatasetId"]).iterate_items():
    if row["type"] == "fare":
        print(row["rank_in_window"], row["depart_date"], row["price"], row["airline"], row["stops"])

JavaScript (Node)

import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });
const run = await client.actor('kestrel/flight-price-tracker').call({
    routes: ['JFK-LHR'], departDate: '14 days', days: 21, tripLengthDays: 7, maxStops: 'nonstop',
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
const best = items.find((i) => i.type === 'fare' && i.cheapest_in_window);
console.log(best.depart_date, best.price, best.airline);

curl

curl -X POST "https://api.apify.com/v2/acts/kestrel~flight-price-tracker/run-sync-get-dataset-items?token=<YOUR_APIFY_TOKEN>" \
  -H 'Content-Type: application/json' \
  -d '{"routes":["LIS-LHR"],"departDate":"30 days","days":7,"maxPrice":150,"currency":"EUR"}'

The same three calls are runnable files in the examples repository: examples/flight-price-tracker.py|.js|.sh.

Limits and honest notes

  • One row per day means one itinerary per day. The row is the cheapest fare Google lists for that day under your cabin and stop filters. If you need every itinerary — to compare airlines on the same day, or to see the second-cheapest option — use the Google Flights Scraper, which bills per itinerary instead.
  • Google's list is what you get. Fares visible only after login, in corporate channels or on airline sites Google does not index are not in it.
  • Results are read in English. The parser reads Google's English accessibility labels, which is why there is no language input; currency and market are the two inputs that change the fares themselves.
  • Prices include what Google shows — taxes and carrier fees as Google presents them. Baggage and seat fees are not in the number.
  • A currency swap is an error, not a row. Some markets ignore the requested currency; a day priced in another one is reported in status rather than billed.
  • The window is capped at 60 days per run. For a longer horizon, run twice with different departDate values.
  • Ties go to the earlier day. When two days share the lowest fare, the earlier departure takes rank 1.

FAQ

Does it need a Google Flights API key?

No. There is no public Google Flights API to hold a key for, and this uses none. No login, no cookies, no browser — it builds the same tfs query parameter Google puts in its own URLs and reads the page Google returns. If you want a flight price tracking api shape, call it synchronously through Apify's API (the curl example above) and treat the rows as the response.

How do I find the cheapest day to fly on Google Flights?

Set departDate to the first day you could leave and days to how far you are flexible. The fare row with cheapest_in_window: true is the answer; rank_in_window orders the alternatives. The status row repeats it as cheapest and cheapest_date if you only want the headline. That is the cheapest day to fly google flights search, as one input and one column.

Can I get flight price history?

Not retroactively — Google does not publish history, and neither can this. What you can do is build it: schedule a daily run with a relative departDate, and every row lands with fetched_at. Two weeks of that is two weeks of history per route and departure day that is yours to keep and chart — a flight price calendar over time, not just across dates.

How do I set up Google Flights price alerts with it?

Set maxPrice, schedule the run, and send a message whenever the dataset contains a fare row. Because filtering happens before billing, a quiet day costs nothing. Google flights price alerts in their native form only cover the dates you saved; this covers every day of the window and any stop or cabin filter you set.

Does it do round trips?

Yes. tripLengthDays: 7 prices every day of the window as a week-long round trip; price is the total for both legs and round_trip_total is true. Round trip flight prices across a window are how most leisure searches actually look, and that is one input here.

What about nonstop-only, or business class?

maxStops: "nonstop" restricts every day's search to nonstop itineraries before the cheapest is picked, so the row is the cheapest nonstop that day — a nonstop flight search per day rather than one that a cheap connection would otherwise win. seat: "business" prices business class fares the same way; expect a different, smaller market than economy rather than a fixed multiple of it.

Why do prices differ from what I see in my browser?

Three usual reasons: your market differs from your browser's country, your currency differs, or the fare moved — airline pricing changes through the day. Set market and currency explicitly and re-run to compare like for like; each row carries the google_url that reproduces the exact search.

How many routes and days can one run track?

Up to 60 days per route, any number of routes; each route is searched in parallel across sessions. A 30-day window on five routes is 150 searches and, with no filter, 150 rows. Larger horizons are two runs with different departDate values.

Is it a scraper for Skyscanner or Kayak?

No — it reads Google Flights. Those sites and Google draw on largely the same carriers and online travel agents, so for airline ticket prices on a route the numbers move together and you get them from one source on one schedule. For the full itinerary list from the same source, the google flights scraper linked below is the sibling listing.

Is there a free tier?

Empty days, filtered days, failed days and status rows are always free, and Apify's free plan includes monthly platform credit you can spend on the paid rows. A run that finds nothing under your maxPrice costs nothing beyond a fraction of a cent of compute.

How to use Flight Price Tracker

Ready-made runs you can open and start

Each one is a saved input on the Apify Store — open it, press Start, and it runs with the fields already filled in. All 163 across the suite.

Ready-made runWhat it does
Cheapest Day to Fly in a 30-Day WindowThirty consecutive departure days on one transcontinental route, one row per day carrying that day cheapest fare and its rank in the window.
Flight Price Drop Alert Under a CapmaxPrice turns the tracker into an alert: only departure days whose cheapest week-long round trip falls under the cap are delivered or billed.
Flight Price History on a Daily ScheduleThree routes out of one hub, a fortnight of departure dates each, run daily: the fare history no airline or metasearch will sell you.
Flight Price Calendar, 60 Days of FaresTwo months of departure dates for a week-long holiday route, one row per day: the full calendar view rather than a single quoted price.
Nonstop Flight Search Across a MonthThe cheapest nonstop of each day for a month, with connections excluded before the daily winner is picked - the fare a business traveller books.
Business Class Fares by Departure DateThree weeks of departure dates for a long-haul business round trip: front-cabin fares move on different dates from economy ones.
Track Flight Prices for Five RoutesFive corporate routes, a fortnight of dates each: one run, one dataset, one schedule for a travel desk that needs a weekly fare sheet.
Airline Ticket Prices for Four AdultsFour passengers on a long-haul leisure route: every fare is the total for the party, which changes which day of the four weeks is cheapest.
Airfare Price Tracking in the Indian MarketTwo domestic Indian trunk routes priced in rupees on the in market, where the carriers and the fare ladder differ from any Western search.
Cheapest Flights by Date for a WeekendA three-night trip length across thirty departure days answers a different question: which weekend to go, not which day to depart.
Google Flights Price Tracker by AirportThree New York airports to the same destination over three weeks: which origin is cheapest, and on which dates the answer changes.

Run Flight Price Tracker

The actor lives on the Apify Store, where the pricing tab carries the live per-row price and the input form has every field above: apify.com/kestrel/flight-price-tracker