Google Flights API · $1.85/1k avatar

Google Flights API · $1.85/1k

Pricing

from $1.85 / 1,000 flight lookups

Go to Apify Store
Google Flights API · $1.85/1k

Google Flights API · $1.85/1k

Google Flights API for live one-way and round-trip fares. Each row includes Google's low, typical, or high verdict, price band, airlines, layovers, duration, and a booking link. X-Search-Status separates empty routes from failed searches. For travel apps, fare alerts, and AI agents.

Pricing

from $1.85 / 1,000 flight lookups

Rating

0.0

(0)

Developer

Matan Rabi

Matan Rabi

Maintained by Community

Actor stats

0

Bookmarked

163

Total users

56

Monthly active users

5 hours ago

Last modified

Share

✈️ Google Flights Scraper and Real Time Flight Price API

Live Google Flights fares in one call. One-way and round-trip, with airlines, layovers, durations, Google's own price insights and a working booking link on every result.

Every search is scanned against Google Flights at the moment you call it. Nothing is served from a cache, so the price you get back is the price the traveller will see.

🌟 Why choose this Google Flights API

Google's price verdict, not just a number. Every result carries price_insights_low, price_insights_high and a low / typical / high rating for that route and those dates. That is what lets your product say "$56 is a good price for this route" instead of just "$56". Some other Google Flights actors and APIs return it too, so read the output schema of whatever you are comparing rather than taking anyone's word for it.

Round-trip is one search, not two. Not two one-way queries stapled together. You get one object per itinerary with a combined total_price, total_duration_seconds, total_stops, and the outbound and return legs already paired, each with its own airline, duration, stop count and layovers. On SerpApi you get the outbound, then spend a second search with a departure_token to see the return, and HasData does the same with a departureToken (both documented on their own pages, read 2026-09-06). Here both legs are priced together and come back as one total with one booking link.

Per leg filtering. Require a nonstop outbound and accept one connection on the way home. Restrict the return to a specific airline. Set a departure window on one leg and an arrival window on the other. Every filter splits per leg on round-trip searches.

An empty result is not a shrug. Google sometimes serves a consent wall, a bot check or a truncated page instead of results, and most scrapers hand that back as an empty list you cannot tell apart from "there are no flights on this route". A page that could not be read is retried; a page where Google genuinely reports no flights is never retried, so a real empty answer costs no extra time. Every run then says which of the two it was, in its status message and in a SEARCH_OUTCOME record.

Flat JSON, one row per flight. No HTML, no nested scrape output to flatten before you can use it. It drops straight into a dataframe, a database, or an LLM tool call.

Built to fan out. The backend takes up to 150 concurrent requests per minute, so a flexible date scan across a whole month finishes in one burst rather than a slow serial loop.

No second bill. No SerpApi key, no separate proxy account, no monthly rental. Residential proxy routing is included and on by default.

🚀 What you get back

FieldWhat it is
price / price_as_numberFare as a display string and as a number you can sort on
airlineOperating carrier
duration / duration_secondsTotal trip time, formatted and in seconds
stops / stops_info[]Stop count plus every layover airport and its duration in seconds
departure_description / arrival_descriptionLocal departure and arrival times in plain text
buy_linkDeep link into Google Flights for that exact itinerary
price_insights_low / price_insights_highGoogle's historical price band for this route and these dates
price_range_in_relation_to_other_periodsGoogle's low, typical or high verdict on the fare

Round-trip results add total_price, total_price_as_number, total_duration_seconds, total_stops, and a full set of departure_flight_* and return_flight_* fields for each leg.

📖 Usage examples

From the Apify Console

Pick One-way or Round-trip, fill in the airports and dates, press Start. The form is prefilled with a working search, so you can run it once before typing anything.

Example 1: cheapest one-way, nonstop only

{
"endpoint": "oneway",
"from_airport": "JFK",
"to_airport": "LAX",
"departure_date": "2026-12-01",
"max_stops": 0,
"sort_type": "Price",
"currency": "usd"
}
```
### Example 2: round-trip, nonstop out, one stop allowed home
```json
{
"endpoint": "roundtrip",
"from_airport": "JFK",
"to_airport": "TLV",
"departure_date": "2026-12-01",
"return_date": "2026-12-08",
"max_departure_stops": 0,
"max_return_stops": 1,
"seat_type": 1,
"currency": "usd"
}
```
### Example 3: business class, specific airlines, morning departures
```json
{
"endpoint": "oneway",
"from_airport": "LHR",
"to_airport": "SIN",
"departure_date": "2026-12-01",
"airline_codes": ["SQ", "BA"],
"departure_time_min": 6,
"departure_time_max": 12,
"seat_type": 3,
"limit": 20
}
```
### Example 4: family of four, budget capped
```json
{
"endpoint": "roundtrip",
"from_airport": "BER",
"to_airport": "CDG",
"departure_date": "2026-12-01",
"return_date": "2026-12-05",
"passengers": [1, 1, 2, 2],
"max_price": 400,
"currency": "eur"
}
```
### Python
```python
from apify_client import ApifyClient
client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("mtnrabi/google-flights-real-time-api").call(run_input={
"endpoint": "roundtrip",
"from_airport": "JFK",
"to_airport": "TLV",
"departure_date": "2026-12-01",
"return_date": "2026-12-08",
"currency": "usd",
})
items = client.dataset(run["defaultDatasetId"]).list_items().items
for trip in sorted(items, key=lambda t: t["total_price_as_number"]):
print(trip["total_price"], trip["departure_flight_airline"], trip["buy_link"])
```
### JavaScript
```javascript
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });
const run = await client.actor('mtnrabi/google-flights-real-time-api').call({
endpoint: 'oneway',
from_airport: 'JFK',
to_airport: 'LAX',
departure_date: '2026-12-01',
currency: 'usd',
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.sort((a, b) => a.price_as_number - b.price_as_number);
console.log(items[0].price, items[0].airline, items[0].buy_link);
```
### REST
```bash
curl -X POST "https://api.apify.com/v2/acts/mtnrabi~google-flights-real-time-api/run-sync-get-dataset-items" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $APIFY_TOKEN" \
-d '{
"endpoint": "oneway",
"from_airport": "JFK",
"to_airport": "LAX",
"departure_date": "2026-12-01",
"max_stops": 1,
"currency": "usd"
}'
```
## 🔍 Input parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
| `endpoint` | string | ✅ | `oneway` | `oneway` or `roundtrip` |
| `from_airport` | string | ✅ | `JFK` | Departure airport IATA code |
| `to_airport` | string | ✅ | `LAX` | Destination airport IATA code |
| `departure_date` | string | ✅ | | Outbound date, `YYYY-MM-DD` |
| `return_date` | string | ✅* | | Return date, `YYYY-MM-DD`. Required for `roundtrip` |
| `limit` | integer | | `10` | How many flights to return |
| `currency` | string | | `usd` | Currency for all prices |
| `seat_type` | integer | | `1` | `1` Economy, `3` Business |
| `passengers` | integer[] | | `[1]` | `1` adult, `2` child, `3` infant on lap, `4` infant in seat |
| `max_price` | integer | | | Only return fares at or below this price |
| `sort_type` | string | | `Overall` | `Overall`, `Price` or `Duration`. Applies to one-way and round-trip alike |
| `use_fallback` | boolean | | `false` | Accepted, and currently changes nothing. It does **not** make the search wait longer for Google - it selects a second, independent flight-data source, and that source is not switched on for the API behind this Actor. Leave it alone; retries and outcome reporting are unaffected by it |
| `strict` | boolean | | `false` | Accepted for compatibility; changes nothing. A run whose search did not complete always fails now. See the FAQ below |
| `use_ext_proxy` | boolean | | `true` | Route through a residential proxy |
| `max_stops` | integer | | | One-way only. `0` for nonstop |
| `airline_codes` | string[] | | | One-way only. Include only these carriers |
| `exclude_airline_codes` | string[] | | | One-way only. Exclude these carriers |
| `departure_time_min` / `_max` | integer | | | One-way only. Departure hour window, 0 to 23 |
| `arrival_time_min` / `_max` | integer | | | One-way only. Arrival hour window, 0 to 23 |
| `max_departure_stops` / `max_return_stops` | integer | | | Round-trip only. Max stops per leg |
| `departure_airline_codes` / `return_airline_codes` | string[] | | | Round-trip only. Carriers per leg |
| `departure_exclude_airline_codes` / `return_exclude_airline_codes` | string[] | | | Round-trip only. Exclusions per leg |
| `departure_departure_time_min` / `_max` | integer | | | Round-trip only. Outbound departure window |
| `departure_arrival_time_min` / `_max` | integer | | | Round-trip only. Outbound arrival window |
| `return_departure_time_min` / `_max` | integer | | | Round-trip only. Return departure window |
| `return_arrival_time_min` / `_max` | integer | | | Round-trip only. Return arrival window |
\* `return_date` is required only when `endpoint` is `roundtrip`.
## 📊 Output format
One dataset row per flight. Set `limit` to control how many rows a search produces.
### One-way
```json
{
"price_range_in_relation_to_other_periods": "low",
"price_insights_low": 65,
"price_insights_high": 135,
"from_airport": "Berlin (BER)",
"to_airport": "Paris (CDG)",
"departure_date": "2026-06-15",
"price": "$56",
"price_as_number": 56,
"duration": "1 hr 50 min",
"duration_seconds": 6600,
"buy_link": "https://www.google.com/travel/flights?tfs=...&curr=usd",
"airline": "easyJet",
"stops": 0,
"stops_info": [],
"departure_description": "10:15 AM on Mon, Jun 15",
"arrival_description": "12:05 PM on Mon, Jun 15"
}
```
`stops_info` is empty on nonstop flights. Otherwise it lists each layover:
```json
"stops_info": [
{ "stop_airport": "AUH", "stop_duration_seconds": 5700 }
]
```
### Round-trip
```json
{
"price_range_in_relation_to_other_periods": "low",
"price_insights_low": 135,
"price_insights_high": 205,
"from_airport": "Berlin (BER)",
"to_airport": "Paris (CDG)",
"departure_date": "2026-06-01",
"return_date": "2026-06-05",
"total_price": "$119",
"total_price_as_number": 119,
"total_duration_seconds": 12900,
"total_stops": 0,
"buy_link": "https://www.google.com/travel/flights?tfs=...&curr=usd",
"departure_flight_departure_description": "7:00 AM on Mon, Jun 1",
"departure_flight_arrival_description": "8:50 AM on Mon, Jun 1",
"departure_flight_airline": "easyJet",
"departure_flight_stops": 0,
"departure_flight_duration": "1 hr 50 min",
"departure_stops_info": [],
"return_flight_departure_description": "8:20 PM on Fri, Jun 5",
"return_flight_arrival_description": "10:05 PM on Fri, Jun 5",
"return_flight_airline": "easyJet",
"return_flight_stops": 0,
"return_flight_duration": "1 hr 45 min",
"return_stops_info": []
}
```
## 🎯 Use cases
- **Travel agents and AI assistants.** Flat JSON that drops straight into a tool call, with a booking link on every row so the agent can hand the user somewhere to buy.
- **Price alerts.** Poll a route on a schedule and fire when `price_range_in_relation_to_other_periods` flips to `low`.
- **Fare calendars and cheapest month views.** Scan a month of departure dates and chart `price_as_number` by day.
- **Metasearch and comparison sites.** Every result ships with a working `buy_link`, so you never have to reconstruct a booking URL.
- **Revenue and route analysis.** Track how fares on a route move over weeks, by carrier and by cabin.
- **Corporate travel tooling.** Per leg airline and time-of-day filters map directly onto travel policy rules.
## 🔌 Integrations
**Schedules.** Save a search as a Task and put it on a cron. `0 7 * * *` checks a route every morning, `0 */6 * * *` every six hours.
**Webhooks.** Fire `ACTOR.RUN.SUCCEEDED` at your own endpoint to push new fares into your database the moment a run finishes.
**Make, Zapier and n8n.** Apify's connectors let you route results into a spreadsheet, a Slack alert or a CRM without writing code.
**Python and JavaScript clients.** `apify-client` on PyPI and npm, examples above.
## 🤖 Use it from an AI agent over MCP
This Actor is available through Apify's MCP server, so any MCP capable client can search flights as a tool. That includes Claude, Cursor, ChatGPT and anything else that speaks MCP.
```
https://mcp.apify.com/?tools=actors,docs,mtnrabi/google-flights-real-time-api
```
The response shape matters here. A model can read a flat object with `price_as_number`, `airline` and `buy_link` and answer a question in one hop. Scrapers that return nested page dumps force you to flatten and summarise before the model can use anything.
There is also an OpenClaw skill built on the same backend: [clawhub.ai/mtnrabi/google-flights-realtime-api](https://clawhub.ai/mtnrabi/google-flights-realtime-api)
## 📝 Notes worth reading before your first run
**Results are live, so run time tracks route complexity.** A trunk route between two major hubs comes back fast. A small regional airport with two connections takes longer, because Google itself takes longer. A search that has to retry a page it could not read takes longer still, and only the searches that need it pay that. `use_fallback` is not a shortcut here - it changes nothing today, and never made the search wait longer for Google.
**`sort_type: "Price"` follows Google's ordering, which is not a strict numeric sort.** If you need exact price order, sort on `price_as_number` yourself. Every example above does. It applies to one-way and round-trip alike - on one-way it used to be accepted and then quietly dropped on the way to the search, which is fixed.
**Dates are `YYYY-MM-DD` and airports are IATA codes.** New York is `JFK`, London Heathrow is `LHR`, Tel Aviv is `TLV`.
**`limit` controls how many rows you get and therefore what a search costs.** The default is 10.
## ❓ Frequently asked questions
### Does Google Flights have a public API?
No. Google shut down the QPX Express API in 2018 and has not replaced it for general use. Anything calling itself a Google Flights API is either scraping the site or reselling someone else who does. This Actor scrapes it directly and returns the result as clean JSON, with no third party reseller in the chain.
### How is this different from the other Google Flights scrapers on Apify?
Three things, and they are all checkable on the listing pages. Round-trip is a first class search that returns paired itineraries with a combined total, rather than two separate one-way result sets you have to match up. Google's own price insights come back on every row, so you can tell a user whether a fare is good. And filters split per leg on round-trips, so an outbound and a return can have different rules. Store numbers move, so compare the current pages yourself.
### Can I use this as a flight price API for one specific route?
Yes, and it is the most common use. Save the search as a Task, schedule it, and read `price_as_number` and `price_range_in_relation_to_other_periods` on each run. That gives you both the fare and Google's judgement of whether the fare is good.
### Can I search flexible dates or a whole month?
Yes. Run one search per date and fan them out in parallel. The backend takes up to 150 concurrent requests per minute, so a 31 day scan finishes in one burst. That is how cheapest-month calendars and fare heatmaps get built on top of this.
### Does it return booking links?
Every result includes `buy_link`, a deep link into Google Flights for that exact itinerary. It is a real link you can put behind a button, not a search URL the user has to refine.
### Can Claude or Cursor call this as an MCP tool?
Yes. Point your MCP client at the URL in the MCP section above. The flat response shape means a model can answer a route and price question without any post-processing.
### How do I scrape Google Flights with Python?
Use the `apify-client` package and call this Actor, as in the Python example above. Writing your own Google Flights scraper means maintaining a headless browser, rotating residential proxies, and rewriting your parser every time Google changes the page. That is the work this Actor already does.
### Which airport codes does it accept?
Three letter IATA codes. `JFK`, `LHR`, `CDG`, `TLV`, `SIN`. City level codes such as `NYC` and `LON` also work where Google supports them.
### What happens if a route has no flights?
You get an empty dataset rather than an error - and the run tells you whether that empty dataset is a real answer or a search that did not complete. See the next question.
### How do I tell "no flights" apart from a failed search?
An empty dataset used to mean either one. A search that was handed a page it could not read - a consent wall, a bot check, a truncated response - is now retried automatically, while a page where Google genuinely reports no flights is never retried, so a real empty result costs no extra time. Whatever is left over, every run now reports:
- **Run status message** — one sentence on the run, e.g. *"Search completed. Google returned no flights for this route and date, so the empty dataset is a real answer."* or *"Search did NOT complete (blocked_page). 0 row(s) pushed. This is not the same as 'no flights found'."*
- **`SEARCH_OUTCOME`** in the run's key-value store — the machine-readable version: `status` (`ok`, `empty`, `partial`, `degraded`, or `unreported`), `reason`, `results`, and the raw headers the backend sent.
- **The run log** — the same sentence, at `WARNING` level when the search did not complete.
One-way has its own version of this, and it is the one that would have been hardest to spot: flight rows are dropped when their price cannot be parsed, and that happens after the page has already been recognised as a real results page. A markup change at Google that renamed the price node would have emptied every row on every page and reported it, with total confidence, as "there are no flights on this route". Dropped rows now count: lose all of them and the run reports `degraded` with reason `unreadable_prices` and retries; lose some and you get the rest, reported as `partial`.
Round-trip is held to the same standard, which is harder than it sounds: a round-trip prices a return leg for each outbound candidate, and each of those fetches can fail on its own. A run only reports `empty` when every candidate it planned to price was attempted and every one of them read a real Google Flights page saying it had nothing. A fan-out that was blocked, or that stopped on the request's time ceiling, reports `degraded` or `partial` instead — never "no flights". When it stopped early, the reason is `search_truncated`, and `x-search-incomplete-combinations` in the `SEARCH_OUTCOME` headers counts the candidates it did not get to finish.
A run whose search did not complete **fails**, and is **not charged**, instead of finishing with an empty dataset. That used to need `strict`; it is now how every run behaves, because the backend reports an incomplete search as an error rather than as an empty result. So an empty dataset on a run that succeeded means one thing: Google genuinely has no flights for that search. `strict` is still accepted and changes nothing.
## 🔗 Related
- **Booking.com hotel prices**, same approach, live rates and availability: [mtnrabi/booking-real-time-api](https://apify.com/mtnrabi/booking-real-time-api)
- **A live product built entirely on this API**: [flightpowers.com](https://flightpowers.com)
---
**Using this Actor?** A rating takes ten seconds and helps other developers find it. Found a bug or need a field that is not here? Open an issue with the input you used and the output you got, and I will answer.