Google Flights Cheapest Date Finder
Pricing
from $1.50 / 1,000 date returneds
Google Flights Cheapest Date Finder
Find the cheapest days to fly on Google Flights. Price every date in your travel window, or a whole departure-by-return grid for round trips, with exact fares, the currency travellers really see, and the cheapest dates flagged and ranked. Up to 20 routes in one run.
Flexible on dates? This actor prices every date in your travel window on Google Flights and tells you which days are actually cheapest — not a single quote for a single day, but the whole calendar in one clean table. Ask for a round trip and you get the full grid instead: every departure date crossed with every return date, so you can see at a glance that leaving on the Tuesday and coming back on the Saturday saves you 40%. Every fare comes back to the cent, in the currency the traveller actually sees, with the cheapest dates flagged and ranked.
Built for flexible-date travellers, travel affiliate sites, fare-alert products and anyone who has to answer "when is it cheapest to fly?" with a number instead of a guess.
What you can do with it
- Find the cheapest day to fly a route across a month, a quarter or a whole year, with the cheapest dates flagged automatically.
- Answer "which departure and return combination is cheapest?" with a complete departure-by-return price grid for round trips.
- Feed a fare-alert or price-drop product by running the same window on a schedule and watching the cheapest date move.
- Price several routes side by side — one run can cover up to 20 routes over the same dates.
- Publish "cheapest month to fly to X" content with real fares behind it, per market and per currency.
- Filter to what you would actually book: only dates under a set fare, only trips of 5–7 nights, only the cheapest dates.
What you get
One row per date (or per departure-and-return combination). A real, abridged record:
{"route": "JFK-LAX","origin": "JFK","destination": "LAX","tripType": "oneway","cabinClass": "economy","passengers": 1,"market": "US","outboundDate": "2026-10-03","outboundDayOfWeek": "Saturday","returnDate": null,"tripLengthNights": null,"daysToDeparture": 48,"price": 168.4,"currency": "USD","priceIsExact": true,"displayPrice": 169,"exactPrice": 168.4,"requestedCurrency": "USD","currencyMatchesRequest": true,"isCheapest": true,"priceRank": 1,"differenceFromCheapest": 0,"source": "google-flights","sourcePath": "price-calendar","isEstimated": false,"collectedAt": "2026-08-16T12:00:00.000Z","flags": []}
A round-trip row is the same shape with both dates filled in:
{"route": "JFK-LAX","tripType": "roundtrip","outboundDate": "2026-11-10","returnDate": "2026-11-14","tripLengthNights": 4,"price": 336.8,"currency": "USD","isCheapest": true,"priceRank": 1}
Input reference
| Field | Type | Default | What it does |
|---|---|---|---|
origin | string | JFK | Departure airport, as a three-letter code. One airport, not a city-wide code. |
destination | string | LAX | Arrival airport, as a three-letter code. |
extraRoutes | array of strings | empty | More routes for the same run, written as LHR-DXB, one per line. Up to 20 routes in total. |
tripType | select | oneWay | oneWay gives a price per departure date. roundTrip gives the departure-by-return price grid. |
dateRangeStart | string (YYYY-MM-DD) | — | First departure date to price. |
dateRangeEnd | string (YYYY-MM-DD) | — | Last departure date to price. |
returnRangeStart | string (YYYY-MM-DD) | empty | Round trips only: first return date in the grid. |
returnRangeEnd | string (YYYY-MM-DD) | empty | Round trips only: last return date in the grid. |
currency | select | USD | The currency to ask for. What actually comes back is always shown on the row. |
market | select | US | The country your travellers book from. Fares differ by market. |
cabinClass | select | economy | Economy, premium economy, business or first. |
adults | integer | 1 | Adult travellers (max 9). |
children | integer | 0 | Children aged 2–11 (max 8). |
infantsInSeat | integer | 0 | Infants in their own seat (max 8). |
infantsOnLap | integer | 0 | Infants on a lap (max 8). |
cheapestDatesOnly | boolean | false | Return only the cheapest date (or tied dates) per route. |
includeDatesWithoutFares | boolean | false | Also return dates with nothing published, with an empty fare, so gaps are visible. These are rows, and rows are charged — see Pricing. |
maxPrice | integer | empty | Drop dates priced above this amount, compared in the currency the fares came back in. |
minTripLengthNights | integer | empty | Round trips only: ignore combinations shorter than this. |
maxTripLengthNights | integer | empty | Round trips only: ignore combinations longer than this. |
sortResultsBy | select | date | Order each route's rows by departure date, or cheapest fare first. |
maxResults | integer | 5000 | Stop after this many dated fares (max 20,000). |
Output fields
| Field | Type | Description |
|---|---|---|
route | string | FROM-TO, e.g. JFK-LAX. |
origin, destination | string | The two airports. |
tripType | string | oneway or roundtrip. |
cabinClass | string | The cabin that was priced. |
passengers | integer | How many travellers the fare covers. |
market | string | The market the fare was collected for. |
outboundDate | string | Departure date, YYYY-MM-DD. |
outboundDayOfWeek | string | Day name for the departure date. |
returnDate | string | null | Return date on a round-trip grid; empty for one-way. |
returnDayOfWeek | string | null | Day name for the return date. |
tripLengthNights | integer | null | Nights between departure and return. |
daysToDeparture | integer | null | Days between the run and the departure date. |
price | number | null | The fare for the whole party. Empty when no fare is published for that date. |
currency | string | null | The currency the fare actually came back in. |
priceIsExact | boolean | True when the fare is the exact figure rather than the rounded one. |
exactPrice | number | null | The fare to the cent. |
displayPrice | number | null | The rounded figure shown on screen for that date. |
requestedCurrency | string | null | The currency you asked for. |
currencyMatchesRequest | boolean | null | False when what came back is not what you asked for. |
isCheapest | boolean | null | True for the cheapest date(s) among the rows returned for that route, after your filters. Empty when no cheapest date can be claimed: part of the window was not searched, or the route came back in more than one currency. |
priceRank | integer | null | 1 for the cheapest date, 2 for the next distinct fare, and so on. Empty whenever isCheapest is empty. |
differenceFromCheapest | number | null | How much more than the cheapest returned date this one costs. Empty whenever isCheapest is empty. |
source | string | Always google-flights. |
sourcePath | string | Which of the two ways this fare was collected: price-calendar (the whole date window in one go, the normal way) or date-search (one departure date at a time, used when the first way is unavailable). Both are the same source and the same fare; the row tells you which was used so you never have to guess. |
isEstimated | boolean | Always false — nothing here is modelled or interpolated. |
collectedAt | string | When the fare was collected, in UTC. |
flags | array | Notes on the row: no-published-fare (nothing on sale that day), date-absent-from-response (the source did not mention that date), served-currency-differs-from-requested, mixed-currency-route (ranking suppressed), route-window-incomplete (part of the window you asked for was not searched, so no cheapest date is claimed for this route), cheapest-among-dates-searched (with "cheapest dates only" on an incomplete route: cheapest of what was searched, not of the window). |
Alongside the table, each run writes a summary you can read straight off the run: how many dates were priced, how many windows had nothing published, how many searches could not be completed, which routes were only searched in part and which exact date windows were missed, how many rows came from each of the two ways of collecting a fare, and the cheapest date found for every route that was searched end to end.
Two ways of getting the same fares
Some days the source will not serve the whole date window at once. When that happens this actor does not give up and hand you an empty table: it switches to a second way of collecting exactly the same dated fares — one departure date at a time — and carries on. You do not have to switch anything on or run anything again; it happens by itself, mid-run.
- The rows are the same rows, in the same shape, from the same source, and they cost exactly the same. A date is a date whichever way it was collected.
- Every row says which way was used, in
sourcePath, and the run summary counts both. You are never left guessing where a number came from. - The second way covers up to 60 departure dates in one run. It is more work per date, so it is deliberately capped. Anything past the cap is reported as not searched, by date, so you can pick it up in a second job — a refused job is never quietly turned into a much bigger one.
- It cannot build a round-trip price grid. A grid is every departure date crossed with every return date, and the second way prices one journey at a time. So a grid the source refuses is reported as not searched rather than answered with one-way fares, which would look like an answer and be the wrong one.
- If both ways come back with nothing, the run fails and says so. An empty table is only ever an empty table.
Pricing
You pay per result, with one extra charge for round-trip grids.
| What you pay for | Price |
|---|---|
| Each row in your table (one date, or one departure-and-return combination) | $1.50 per 1,000 |
| Each round-trip date grid delivered | $0.030 each |
Worked example — one-way. JFK→LAX across 90 days: 90 dated fares = $0.14.
Worked example — round trip. A 7-day departure window crossed with a 7-day return window is 49 priced combinations plus one grid: 49 × $0.0015 + $0.030 = $0.10.
Worked example — a busy job. Ten routes, each priced across 200 days: 2,000 dated fares = $3.00.
You are charged for the rows you get, and a row is a row whether or not a fare was published for that date. By default only dates with a fare become rows, so a window where the source publishes nothing produces no rows and costs nothing. Switching on includeDatesWithoutFares turns those empty dates into rows, and they are charged like any other row — a ten-date window with nothing on sale costs $0.015 with the switch on and $0 with it off. A round-trip grid is charged once per route, never per combination inside it and never more than once even when a large grid has to be covered in several parts, and it is not charged at all when the grid could not be delivered.
Which of the two ways collected a fare makes no difference to the price. A date row is a date row; the second way is more work behind the scenes and none of that is passed on to you. The same input costs the same whichever way the run ends up taking.
Limits & what this actor cannot do
- Fares are a snapshot taken at the moment of collection. Airline pricing moves continuously, and a fare seen now may be gone an hour later.
- It cannot book, hold or reserve anything. It reports what the source publishes to the public; it does not guarantee a fare is still available.
- The fare is the total for the party you searched. Two adults roughly doubles it. Taxes, carrier fees and baggage rules vary by carrier and fare type and are not broken out here.
- A refused search is reported as a failed search, never as "no fares found". If the source will not complete a search, the run says so and counts it — an empty table is only ever an empty table. A refusal is also double-checked before it is believed, because it is not always about you.
- There is a second way of collecting the same fares, used automatically when the first is unavailable, covering up to 60 departure dates in one run and never a round-trip grid. See "Two ways of getting the same fares" above; it changes nothing about what you are charged.
- A route that could not be searched end to end names no cheapest date. If part of your window was refused, failed or left out, that route's rows still carry their fares but come back with
isCheapest,priceRankanddifferenceFromCheapestempty and aroute-window-incompletenote, because the cheapest date might be in the part nobody searched. The summary lists those routes and the dates that were missed. - Routes are seasonal. A route with nothing on sale for the dates you picked is reported as having nothing, which is different from an error, and different again from a route that does not exist.
- Fares and availability differ by market and currency. Results reflect the market you select, and the currency that actually came back is always on the row — never the one you asked for.
- The figure shown on screen is rounded up. Both are given:
exactPriceto the cent,displayPriceas shown. - One airport per side. City-wide codes such as
NYCorLONare not priced by day on this source, so they are rejected up front with the member airports offered instead of a misleading empty result. - A one-way calendar is priced 200 days at a time and a round-trip grid 200 combinations at a time; longer stretches and larger grids are covered in several parts for you, automatically and without gaps, as far as one run's search allowance reaches — see the allowance note below for what happens when it does not.
- Fares are published roughly 318 days ahead. Dates beyond that come back with nothing published, and the run warns you when your window runs past it.
- One run makes a limited number of searches — about 30. A job that needs more is trimmed by whole routes: a route is either searched across its entire date window or left out of the run completely, never half-searched. The run names the routes it left out and the exact date windows it did not search, so you can pick them up next time. The one exception is a single route whose own date window needs more searches than a whole run has: it is covered as far as the run reaches, the dates it did not reach are listed the same way, and every row it produces is marked
route-window-incompletewith no cheapest date claimed. A search that has to be attempted twice uses two of them. - A date the source skips is left empty, never filled in. Nothing is averaged, carried over from the day before, or converted between currencies.
- Speed depends on the size of the job and on the source's own response times; no fixed throughput is promised.
- The source's terms govern automated access. You are responsible for using the data lawfully and in line with those terms.
FAQ
Do I need an account on Google Flights? No. Nothing is signed in to and nothing is signed up for.
Does it need my login or password? No. There is nothing to configure — set a route and a date window and press start.
Can I schedule it? Yes. Run it daily or twice daily to watch the cheapest date move; that is the cadence this data supports. Checking every few minutes is not worth paying for — fares do not move on that scale.
Are these fares bookable? They are what the source published for that date at the moment of collection. Treat them as accurate at collection time, not as a reservation: availability and price can change before you get to the airline.
Why do some rows say price-calendar and others date-search?
Because the actor has two ways of collecting the same dated fares and uses whichever is available, switching over by itself mid-run. price-calendar is the normal one; date-search is the second one, which prices one departure date at a time and is used when the first is not being served. Same source, same fares, same price to you — the field simply tells you which was used.
Does the second way cost me more? No. You pay per row in your table, and a date is a date however it was collected. The second way is more work behind the scenes and none of that reaches your bill.
Why is one date empty when the ones around it have fares?
Because nothing was published for that date. Nothing is ever carried over from a neighbouring day or estimated — an empty fare means empty, and you can switch those dates on with includeDatesWithoutFares if you want to see the holes.
Can I compare two currencies for the same route? Run it twice, once per currency, and compare like for like. Rows from different currencies are never ranked against each other — if a route ever comes back in more than one currency, the ranking is suppressed rather than guessed.
What happens if the route does not exist? An unknown or unusable airport code is rejected before anything is searched, with an explanation, rather than returning an empty table you might mistake for "no flights".
