Google Flights Scraper & Price Tracker avatar

Google Flights Scraper & Price Tracker

Pricing

from $0.25 / 1,000 flights

Go to Apify Store
Google Flights Scraper & Price Tracker

Google Flights Scraper & Price Tracker

Google Flights for any route and dates: every itinerary incl. the ones Google hides, with price, airlines, flight numbers, stops, CO2, aircraft and codeshares; Google's price level and price history; what changed since your last run; the cheapest departure days.

Pricing

from $0.25 / 1,000 flights

Rating

0.0

(0)

Developer

Berkan Kaplan

Berkan Kaplan

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

a day ago

Last modified

Categories

Share

Get every flight Google Flights has for a route and dates — including the ones Google hides behind "View more flights" — with price, airlines, flight numbers, times, stops and layovers, CO₂, aircraft, legroom, codeshares and "operated by", plus Google's own price level, typical price range and price history (61–62 days for most searches). Run it on a schedule in monitor mode and get only what changed since the last run: new, cheaper, pricier and gone flights. Or scan a range of days for the cheapest departure day.

No Google account, no API key, no browser. One request to Google per search.

Quick start (API)

curl -X POST "https://api.apify.com/v2/acts/foxlabs~google-flights-scraper/run-sync-get-dataset-items?token=YOUR_APIFY_TOKEN" \
-H "Content-Type: application/json" \
-d '{"routes": ["IST-LHR"], "departureDate": "+30", "maxFlightsPerSearch": 20}'

Three modes

ModeWhat you getTypical use
search (default)Every flight for each route and dates (one row per itinerary), plus one route row per search with the lowest price, Google's price level, typical range and price historyFare research, competitor schedules, emissions reporting
monitorOnly the flights that changed since the previous run of the same search: NEW, CHEAPER, PRICIER, GONE (and PRICE_ADDED / PRICE_REMOVED). CHEAPER and PRICIER rows carry the previous price and the change, GONE and PRICE_REMOVED rows the last price seen. Plus the route row with the change of the lowest priceDaily price tracking and alerts; schedule it
cheapest-daysOne row per departure day with the lowest fare, the cheapest flight, Google's price level, and a flag on the cheapest day"Cheapest day to fly" content, flexible-date planning

What you get

Every row has a type: flight, route, day or status.

GroupFields (flight rows)
Priceprice (for all adults together; round trips: Google's "from" price, the lowest round-trip total with this outbound flight), priceFormatted, priceUnavailable (true: Google lists the flight but shows no price; empty on GONE rows, which have no current price)
Flightsairlines, airlinesText, flightNumbers, flightNumbersText, itineraryKey (stable ID: every flight number with its departure time), operatedBy, codeshares, aircraft, aircraftText, minLegroomInches
TimesdepartureAirport, departureAirportName, departureTime, arrivalAirport, arrivalAirportName, arrivalTime (local airport times), arrivalDayOffset, durationMinutes, durationText
Stopsstops, layovers[] (airport, city, minutes, changeOfAirport), layoversText, changeOfAirport
EmissionsemissionsKg, typicalEmissionsKg (Google's typical figure for the route), emissionsVsTypicalPercent
Google's rankingisBest (Google's "best flights"), rank (order in Google's list)
Segmentssegments[]: flight number, airline, operated by, codeshares, airports, times, duration, aircraft, legroom, CO₂
MonitorchangeType, previousPrice, priceChange, priceChangePercent
LinkgoogleFlightsUrl: one-way → Google's booking page for exactly these flights (it lists who sells the ticket); round trip → the search with this outbound selected (it lists the matching returns)
GroupFields (route rows, one per search)
PricelowestPrice, lowestPriceFormatted, cheapestFlight
Google's price insightpriceLevel (low / typical / high), priceLevelCode, priceVsTypicalRange (below / within / above), typicalPrice, typicalPriceLow, typicalPriceHigh, belowTypicalBy, priceHistory[] (date and lowest price; 61–62 days when Google has a history for the search), priceHistoryDays
CoverageflightsFound, hiddenFlightsCount (how many Google hides behind "View more flights"), hiddenFlightsIncluded, flightsDelivered, originCity, destinationCity
MonitormonitorStatus (first-run / compared), previousCheckAt, previousLowestPrice, lowestPriceChange, previousPriceLevel, newCount, cheaperCount, pricierCount, goneCount, unchangedCount

Day rows (cheapest-days mode) carry departureDate, returnDate, lowestPrice, cheapestFlight, isCheapestDay, Google's price level and typical range (no price history) and flightsFound. Every row also has route, origin, destination, departureDate, returnDate, tripType, cabinClass, adults, currency, market, mode, scrapedAt and error.

About priceLevel. Google sends a price-level code with most searches, and its pages put a word on each code. We read those words on Google's own pages (2026-09-30): code 1 "low" (a booking page), codes 2, 3 and 4 "typical", code 5 "high" (search pages). priceLevel carries that word and priceLevelCode the code. In our platform runs (81 route and day rows) the code followed where the lowest price sat in Google's typical range: 2 in its lower third, 3 in the middle, 4 in the upper third, 5 above the range (24 of 24). priceVsTypicalRange compares the lowest price with Google's typical range (our comparison of Google's numbers). A search with airline and stop filters came back without a price insight; its fields are then empty.

Sample output

A real flight row (trimmed) from a run on 2026-09-30 (UTC): FRA-BKK, 10 November 2026, economy, 1 adult, USD.

{
"type": "flight",
"route": "FRA-BKK",
"departureDate": "2026-11-10",
"price": 626,
"priceFormatted": "$626",
"airlinesText": "Lufthansa, Oman Air",
"flightNumbers": ["LH 998", "WY 172", "WY 815"],
"itineraryKey": "LH998@2026-11-10T16:40/WY172@2026-11-10T20:00/WY815@2026-11-11T08:30",
"departureTime": "2026-11-10T16:40",
"arrivalTime": "2026-11-11T17:15",
"arrivalDayOffset": 1,
"durationText": "18 hr 35 min",
"stops": 2,
"layoversText": "AMS 2h 5m; MCT 2h 40m",
"operatedBy": ["Air Dolomiti"],
"codeshares": ["WY 5364"],
"aircraftText": "Embraer 190 E2, Boeing 787",
"minLegroomInches": 30,
"emissionsKg": 698,
"typicalEmissionsKg": 616,
"emissionsVsTypicalPercent": 13,
"isBest": false,
"googleFlightsUrl": "https://www.google.com/travel/flights/booking?tfs=CBwQAhqBARIKMjAyNi0xMS0xMCIfCgNGUkES…"
}

The route row of the same search (trimmed):

{
"type": "route",
"route": "FRA-BKK",
"departureDate": "2026-11-10",
"lowestPrice": 489,
"cheapestFlight": { "price": 489, "airlinesText": "Etihad", "flightNumbersText": "EY 124, EY 406", "stops": 1, "durationMinutes": 865 },
"priceLevel": "typical",
"priceLevelCode": 3,
"priceVsTypicalRange": "within",
"typicalPrice": 429,
"typicalPriceLow": 395,
"typicalPriceHigh": 560,
"priceHistory": [{ "date": "2026-09-28", "price": 398 }, { "date": "2026-09-29", "price": 373 }, { "date": "2026-09-30", "price": 489 }],
"priceHistoryDays": 61,
"flightsFound": 177,
"hiddenFlightsCount": 165,
"originCity": "Frankfurt am Main",
"destinationCity": "Bangkok"
}

Input & filters

InputWhat it doesDefault
modesearch, monitor or cheapest-dayssearch
routesOne route per line as two 3-letter airport codes: IST-LHR. A line can carry its own dates: JFK-LAX 2026-11-05 or JFK-LAX 2026-11-05 2026-11-12—
departureDateYYYY-MM-DD, or days from today such as +30 (UTC). Monitor mode needs YYYY-MM-DD. In cheapest-days mode, the first day scanned+30
returnDateEmpty = one-way. YYYY-MM-DD, or +N for N days after the departure. In cheapest-days mode +N is the trip lengthone-way
scanDaysCheapest-days mode: how many departure days to scan (one search per day)14
cabinClasseconomy, premium-economy, business, firsteconomy
adults1–9. Prices are for all adults together1
maxStopsany, nonstop, 1, 2 (Google's stop filter)any
airlines2-character airline codes such as TK, LHall
currency, countryCurrency of the prices; the searcher's marketUSD, us
maxFlightsPerSearchFlight rows per search, up to 300 (in monitor mode: change rows; see the FAQ)200 (the form starts at 20)
includeHiddenFlightsAlso the flights Google hides behind "View more flights"on
monitorNameMonitor mode: the result of each search is kept in the key-value store google-flights-monitor-<name> of your accountdefault
minPriceChangePercentMonitor mode: a price counts as changed from this percentage on, measured against the last reported price1
includeUnchangedMonitor mode: also list unchanged flights (UNCHANGED)off
proxyConfiguration, autoProxyFallbackProxy is not needed; the Actor switches to Apify residential proxy by itself if Google rate-limits itoff / on

Input the form does not allow is rejected by Apify before the run starts (for example adults: 10). These stop the run at once, with the reason in its status message and before any request to Google: a route line that is not two 3-letter codes, a departure date in the past, a departure or return date more than 365 days ahead, a relative departure date (+30) in monitor mode, and a return date instead of +N in cheapest-days mode. A 3-letter code that is not an airport, such as the city code LON, is sent to Google; Google refuses that search, and a free row says so (see Troubleshooting). Days of a cheapest-days scan whose departure or return falls more than 365 days ahead are not searched; the log and the SOURCE_REPORT record say how many.

Each search once per run. Two lines that describe the same search (route, dates and filters) are searched and charged once; the second leaves a free row with error: "skipped: …". In cheapest-days mode, a day that two lines share is searched and charged once; its row appears under the first line, and the second line still counts it when it names its cheapest day in the log.

Example inputs (copy & paste)

Every flight, including the hidden ones, for one route 40 days ahead:

{ "routes": ["FRA-BKK"], "departureDate": "+40", "maxFlightsPerSearch": 300 }

Daily price tracker for two searches (schedule it once a day; put your own travel dates in the lines):

{ "mode": "monitor", "routes": ["IST-LHR 2027-03-10", "JFK-LAX 2027-03-12 2027-03-19"], "monitorName": "my-routes", "minPriceChangePercent": 2 }

Cheapest day to fly in a two-week window starting 20 days from today, one-week round trips:

{ "mode": "cheapest-days", "routes": ["IST-LHR"], "departureDate": "+20", "scanDays": 14, "returnDate": "+7" }

Business class, two adults, euros, German market, round trip a week later:

{ "routes": ["CDG-DXB"], "departureDate": "+45", "returnDate": "+7", "cabinClass": "business", "adults": 2, "currency": "EUR", "country": "de" }

Nonstop flights sold under two airlines' codes (Google's filter also returns their codeshare flights flown by partners):

{ "routes": ["IST-LHR"], "departureDate": "+30", "maxStops": "nonstop", "airlines": ["TK", "BA"] }

Use cases

  • Fare monitoring for travel agencies and tour operators: schedule monitor mode on the routes you sell and act on CHEAPER rows and on the route row's lowestPriceChange and price level.
  • Deal newsletters and travel content: cheapest-days mode for "cheapest day to fly" pages; priceVsTypicalRange and the typical range for "is this a good price" copy.
  • Corporate travel and sustainability reporting: CO₂ per itinerary next to Google's typical figure for the route, with the operating airline and aircraft.
  • Airline and route analysis: the whole list, including the connections Google hides, with codeshares, operating carriers, aircraft and legroom.
  • Price-history research: about two months (61–62 days) of Google's price history for most searches, from the very first run.

Performance & throughput

Measured on the Apify platform on 2026-09-30 (UTC) with build 0.1.1 at the default 512 MB and the default proxy setting:

RunInputRowsSearches (requests to Google)Time
iTZh7Q2BTvdDvtrtXThe quick-start command above (IST-LHR, +30, 20 flights)20 flights + 1 route row (92 flights found, 80 of them hidden by Google)14 s
v7MUEk4uaS3eoH4jeFRA-BKK, +40, all flights169 flights + 1 route row110 s
N77cWevQcfksNrWW910 routes (2 round trips), +35, all flights1,157 flights + 10 route rows1024 s
q5zJv3jUx3YztU6cZMonitor, 3 searches, first run314 NEW + 3 route rows36 s
QbGegxv24Ff8YfFjCThe same monitor 17 seconds later0 changes; 3 free route rows32 s
N1t34QJS5odsq9QKwCheapest days, IST-LHR, 30 days30 day rows, all priced308 s

One search is one request to Google. In our 49 platform runs of 2026-09-30 that searched Google with the default setting (135 requests), none was rate-limited or needed the residential proxy. Forced through Apify residential proxy, the FRA-BKK search took 32 s instead of 10 s (UbiFuqPLtEN71NUSc).

Integrations

Use the dataset from the API, schedule the Actor in Apify Console, or connect it to Make, Zapier, n8n, Google Sheets or a webhook. For a price tracker, schedule monitor mode and send the CHEAPER rows to Slack or email.

import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: 'YOUR_APIFY_TOKEN' });
const run = await client.actor('foxlabs/google-flights-scraper').call({ routes: ['IST-LHR'], departureDate: '+30', maxFlightsPerSearch: 20 });
const { items } = await client.dataset(run.defaultDatasetId).listItems();
from apify_client import ApifyClient
client = ApifyClient("YOUR_APIFY_TOKEN")
run = client.actor("foxlabs/google-flights-scraper").call(run_input={"routes": ["IST-LHR"], "departureDate": "+30", "maxFlightsPerSearch": 20})
items = client.dataset(run["defaultDatasetId"]).list_items().items

Data quality

Measured on the platform runs of 2026-09-30 (build 0.1.1): 2,834 flight rows, 35 route rows and 47 day rows from 23 runs, including all runs in the table above.

  • Filled on every flight row: airlines, flight numbers, itineraryKey, departure and arrival times, duration, stops, the Google Flights link (2,834 of 2,834); layovers on all 2,499 flights with stops.
  • Price: 2,496 of 2,834 flight rows (88%). The others are listed by Google without a price, mostly among the hidden flights; they have priceUnavailable: true and price: null, never 0.
  • CO₂ and aircraft: 2,832 of 2,834; Google gave neither for two flights (a London–New York connection via Barcelona on Vueling and LEVEL, and a Scoot flight Singapore–Sydney).
  • Legroom: 2,741 of 2,762 economy rows. In business class Google gives a seat pitch for fewer flights (29 of 73 in a local CDG-DXB test, not a platform run).
  • Uniqueness: no flight appeared twice in any run (for example 1,157 of 1,157 unique itineraryKey in N77cWevQcfksNrWW9).
  • Against Google's own page: we opened 10 googleFlightsUrl links from N77cWevQcfksNrWW9. The 5 one-way booking pages and 2 round-trip pages contained every flight number and the date of their row. On 3 search pages, all 41 flights the page lists matched our rows in flight numbers, times, duration, stops, CO₂ and price, and the price level, typical range and last history point matched too.
  • Whole list: in a recon of 5 routes (434 flights), the number of flights equalled Google's shown list plus its own hidden-flight counter on every route.
  • Price history: 61 or 62 days for 31 of 35 searches. Google sent none for a Turkish domestic route (SAW-AYT, two runs), a business-class round trip (CDG-DXB) and a search with airline and nonstop filters (which had no price level either).
  • Round trips and passengers: the price of a round-trip row is what Google's own page shows for that outbound flight, "From $407 round trip total": the lowest total with a matching return. On a JFK-LAX round-trip page (2026-09-30), 33 of 33 priced flights carried that label with the same amount, airline and departure time. Prices are for all adults together: in a local test (CDG-DXB round trip, business; 73 flights in both runs, 65 of them priced in both) the 2-adult price was twice the 1-adult price within €1 for 48 flights and higher for 17 (up to 2.9 times), median ratio 2.0.
  • Prices move often, mostly by small amounts: in a recon, 70 of 177 FRA-BKK flights were $1 cheaper 22 minutes later (0.04–0.2 %). On the platform, 43 of 314 monitored flights changed price within 31 minutes (CvxX1oSaU1L81c0wM, threshold 0): 38 by less than 1 %, 5 by 1 % or more (up to −14.7 %). At the default 1 % threshold, monitor mode would have reported only those 5; smaller moves add up until they pass it. Set minPriceChangePercent to 0 to see every move.

Pricing

Pay per event:

  • flight: every flight row delivered. In monitor mode that is every change row (and every unchanged row when you turn includeUnchanged on).
  • route-check: once per search that delivered at least one flight row. It is charged with that search's route row.
  • date-price: every cheapest-days row that has a price.

Free rows: status rows for searches that were empty, failed or duplicated; the route row of a search that delivered no flight row; day rows without a price.

A monitor run in which nothing changed charges none of these events (unless you turn includeUnchanged on). Each search still leaves its free route row with the lowest price, the price level and the history. Platform run pair: q5zJv3jUx3YztU6cZ (first run, 314 NEW rows, 3 charged searches), then QbGegxv24Ff8YfFjC 17 seconds later (0 changes, 0 flight rows, 0 charged searches, 3 free route rows).

If the Pricing tab lists an Actor start event, it is charged once per run. Current prices are on the Pricing tab.

FAQ

What are the "hidden" flights? Google Flights shows a short list and hides the rest behind "View more flights": dearer or slower combinations. The Actor asks for the whole list in the same single request. The route row says how many Google hides (hiddenFlightsCount) and whether they are included (hiddenFlightsIncluded).

How does monitor mode decide what changed? Each search's flights are stored with their itineraryKey in the key-value store google-flights-monitor-<monitorName>. The next run compares prices with the price last reported for each flight, so small moves below minPriceChangePercent add up until they pass it. The first run of a search is the baseline: every flight is stored, and up to maxFlightsPerSearch of them are delivered as NEW rows (set it to 300 to get the whole list once). Later runs report only changes; changes that do not fit under maxFlightsPerSearch come in the following runs. A flight that is no longer listed comes once as GONE.

Why does monitor mode need a fixed date? It compares the same flights from run to run. With +30, every daily run would search a different day and list everything as new.

Are prices per person? No: for all adults together, as Google Flights shows them. A round-trip price is Google's "From … round trip total" for that outbound flight, the lowest total with a matching return. The flight rows describe the outbound flights, and googleFlightsUrl opens the matching returns with their totals.

Why is priceLevel empty for some searches? Google sent no price insight for that search; we saw this with airline and stop filters. Then priceLevelCode and the typical range are empty too. See "About priceLevel" above.

Can I get the list of sites that sell the ticket? Not in the data: Google protects that request. The googleFlightsUrl of a one-way flight opens Google's booking page for exactly those flights, which lists the sellers.

Multi-city trips? Not supported: one-way and round trips only.

Do I need a proxy? No. With the default setting the Actor asks Google directly and switches to Apify residential proxy by itself if Google rate-limits it. None of our 49 default-setting test runs that searched Google (135 requests, 2026-09-30) needed it.

Why fewer flights than on Google? maxFlightsPerSearch, the stop or airline filters, or includeHiddenFlights off. Google's list also changes within minutes.

Why do other airlines appear when I filter by airline? Google's airline filter includes codeshare flights sold under those airline codes. In our 2026-10-01 test, a BA, VS filter returned American Airlines AA 103 (sold as BA 1516) and Delta DL 2 (sold as VS 4007). On each row, airlines shows the airline that flies the plane and codeshares lists the numbers it is also sold under, so you can keep only the flights a given airline flies itself.

Troubleshooting

  • error starts with empty: — Google shows no flights for that route, date and filters. Try other dates or fewer filters, and check the airport codes. Nothing was charged.
  • error starts with failed: and says Google Flights did not accept the search — check that both are airport codes (LHR, not the city code LON). Nothing was charged.
  • error starts with failed: otherwise — Google could not be read for that search; the reason follows. Nothing was charged.
  • error starts with skipped: — the same search was already in the input.
  • A monitor run delivered nothing — nothing changed since the last run; each search still left its free route row.
  • The Actor reads the public Google Flights results any visitor sees. Prices come from airlines and travel sites through Google and change often; keep scrapedAt with the data.
  • Times are local airport times without a time zone.
  • Up to 300 flights per search, 60 days per cheapest-days scan, dates up to 365 days ahead.
  • "Google" and "Google Flights" are trademarks of Google LLC. This Actor is not affiliated with or endorsed by Google.

Support

Open an issue on the Issues tab with the run ID and the input, or write to info@foxlabs.com.tr.

Changelog

0.1.3 — 2026-10-01

  • The run's status message says what was delivered, or why nothing was.

0.1.2 — 2026-10-01

  • priceLevel for every code Google sends: low, typical, high.
  • Monitor mode: the first run stores the whole list as the baseline, so later runs never list an old flight as NEW.
  • A city code such as LON ends with one request and a plain message.
  • Cheapest-days: a day that two lines share is searched and charged once.

See CHANGELOG.md for details.

0.1 — 2026-09-30

First version.