How to Scrape Google Flights

Google stopped supporting the Google Flights API back in 2018. Unfortunately, this meant developers could no longer get the data they needed directly from the source. But that doesn’t mean the need for this data went away. If anything, it pushed people to find alternative ways of getting the information.
In this post, I’ll share how you can still get flight data from Google Flights, and give some examples of how you might use that data.
Getting Started
All the examples in this article use HasData’s Google Flights API. To start using this API and run the examples provided, sign up for a free account at HasData.com and get your API key.
Endpoint for a GET request to the Flights API:
https://api.hasdata.com/scrape/google/flights
A basic request to the Google Flights API looks like this:
curl --location 'https://api.hasdata.com/scrape/google/flights?departureId=LHR&arrivalId=JFK &outboundDate=2025-05-15&returnDate=2025-05-30' \
--header 'Content-Type: application/json' \
--header 'x-api-key: YOUR-API-KEY'
Don’t forget to include your HasData API key. Here’s a Python example:
import requests
api_key = "YOUR-API-KEY"
params = {
"departureId": "BER", # Departure airport code (IATA)
"arrivalId": "BKK", # Arrival airport code (IATA)
"outboundDate": "2025-07-15", # Departure date (YYYY-MM-DD)
"returnDate": "2025-07-25", # Return date (for round trips)
}
params = {k: v for k, v in params.items() if v}
url = "https://api.hasdata.com/scrape/google/flights"
headers = {
"Content-Type": "application/json",
"x-api-key": api_key,
}
response = requests.get(url, headers=headers, params=params)
print(response.json())
Now, let’s go over the available request parameters. You can find full descriptions in the documentation or jump straight to the section with the parameters you’re interested in to see ready-to-use examples.
For convenience, we’ve grouped all parameters and provided 2-3 examples for each group.
Core Route Parameters: From & To, Dates
The main parameters used most often are:
departureId
,arrivalId
– airport codes (IATA) or location IDs (kgmid – Google Knowledge Graph ID), has a short lifetime.outboundDate
,returnDate
– departure and return dates inyyyy-MM-dd
format.type
– trip type (roundTrip
,oneWay
,multiCity
). If not specified, it defaults toroundTrip
. If you’re usingmultiCity
, you also needmultiCityJson
for flight details.departureToken
– a token that helps retrieve return flight data forroundTrip
or the next leg for amultiCity
trip.
The required parameters are departureId
, arrivalId
, and outboundDate
. If the trip type
isn’t set (or is explicitly roundTrip
), then returnDate
is also required.
cURL request:
curl --location 'https://api.hasdata.com/scrape/google/flights?departureId=BER&arrivalId=BKK&outboundDate=2025-07-15&type=oneWay' \
--header 'Content-Type: application/json' \
--header 'x-api-key: YOUR-API-KEY'
Python script:
import requests
api_key = "YOUR-API-KEY"
params = {
"departureId": "BER", # Departure airport code (IATA)
"arrivalId": "BKK", # Arrival airport code (IATA)
"outboundDate": "2025-07-15", # Departure date (YYYY-MM-DD)
"type": "oneWay" # Trip type: "oneWay", "roundTrip" or "multiCity" (requires multiCityJson)
}
params = {k: v for k, v in params.items() if v}
url = "https://api.hasdata.com/scrape/google/flights"
headers = {
"Content-Type": "application/json",
"x-api-key": api_key,
}
response = requests.get(url, headers=headers, params=params)
print(response.json())
Now, let’s go through some examples.
One-Way Trip to Tokyo
Goal: Find flights from New York (JFK) to Tokyo (HND) on June 10th, 2025, one way.
cURL request:
curl --location 'https://api.hasdata.com/scrape/google/flights?departureId=JFK&arrivalId=HND&outboundDate=2025-06-10&type=oneWay' \
--header 'Content-Type: application/json' \
--header 'x-api-key: YOUR-API-KEY'
Python script parameters:
params = {
"departureId": "JFK",
"arrivalId": "HND",
"outboundDate": "2025-06-10",
"type": "oneWay",
}
Example response:
{
"requestMetadata": {
"html": "https://f005.backblazeb2.com/file/hasdata-screenshots/8422bdd0-0429-4b48-970a-5b8aa8c0aeae.html",
"url": "https://www.google.com/travel/flights/search?hl=en&gl=us&curr=USD&tfs=CBwQAhoeEgoyMDI1LTA2LTEwagcIARIDSkZLcgcIARIDSE5EQgEBSAFwAYIBCwj___________8BmAEC"
},
"bestFlights": [
{
"price": 791,
"type": "One way",
"totalDuration": 1225,
"flights": [
{
"departureAirport": {
"id": "JFK",
"name": "John F. Kennedy International Airport",
"time": "2025-06-10 10:30"
},
"arrivalAirport": {
"id": "YYZ",
"name": "Toronto Pearson International Airport",
"time": "2025-06-10 12:15"
},
"duration": 105,
"airplane": "Embraer 175",
"airline": "Air Canada",
"flightNumber": "AC 8553",
"legroom": "31 in",
"travelClass": "Economy",
"extensions": [
"Average legroom (31 in)"
]
}
],
"layovers": [
{
"id": "YYZ",
"name": "Toronto Pearson International Airport",
"duration": 60
}
],
"carbonEmissions": {
"thisFlight": 714000,
"typicalForThisRoute": 740000,
"differencePercent": -4
},
"bookingToken": "W1t7ImRlcGFydHVyZUW1iZXIiOiIzODI4In1dXQ=="
}
],
"otherFlights": [
{
"price": 753,
"type": "One way",
"totalDuration": 1380,
"flights": [
{
"departureAirport": {
"id": "JFK",
"name": "John F. Kennedy International Airport",
"time": "2025-06-10 10:30"
},
"arrivalAirport": {
"id": "YYZ",
"name": "Toronto Pearson International Airport",
"time": "2025-06-10 12:15"
},
"duration": 105,
"airplane": "Embraer 175",
"airline": "Air Canada",
"flightNumber": "AC 8553",
"legroom": "31 in",
"travelClass": "Economy",
"extensions": [
"Average legroom (31 in)"
]
}
],
"layovers": [
{
"id": "YYZ",
"name": "Toronto Pearson International Airport",
"duration": 60
}
],
"carbonEmissions": {
"thisFlight": 712000,
"typicalForThisRoute": 740000,
"differencePercent": -4
},
"bookingToken": "W1t7ImTYifV1d"
}
],
"priceInsights": {
"lowestPrice": 753,
"typicalPriceRange": [
960,
1100
],
"priceLevel": "low",
"priceHistory": [
[
1736053200,
759
]
]
},
"airports": [
{
"departures": [
{
"id": "JFK",
"name": "John F. Kennedy International Airport",
"city": "New York",
"country": "United States",
"countryCode": "US"
}
],
"arrivals": [
{
"id": "HND",
"name": "Tokyo International Airport (Haneda Airport)",
"city": "Tokyo",
"country": "Japan",
"countryCode": "JP"
}
]
}
]
}
The data was retrieved using a script available in Google Colab.
Round Trip with Fixed Return
Goal: Find flights from Paris (CDG) to Bali (DPS) on July 1st, 2025, and return flights on July 21st, 2025.
cURL request:
curl --location 'https://api.hasdata.com/scrape/google/flights?departureId=CDG&arrivalId=DPS&outboundDate=2025-07-01&returnDate=2025-07-21&type=roundTrip' \
--header 'Content-Type: application/json' \
--header 'x-api-key: YOUR-API-KEY'
Python script:
params = {
"departureId": "CDG",
"arrivalId": "DPS",
"outboundDate": "2025-07-01",
"returnDate": "2025-07-21",
"type": "roundTrip",
}
To get return flights, we make a request with the roundTrip
parameter. This gives us a list of Paris – Bali flights and a departureToken
for return flights. Then, we extract the departureToken
from the results and make another request, adding it as a parameter.
response = requests.get(url, headers=headers, params=params)
data = response.json()
departure_token = None
for key in ["bestFlights", "otherFlights"]:
if key in data and data[key]:
departure_token = data[key][0].get("departureToken")
break
if departure_token:
params["departureToken"] = departure_token
response2 = requests.get(url, headers=headers, params=params)
data2 = response2.json()
print(data2)
Full code:
import requests
api_key = "YOUR-API-KEY"
params = {
"departureId": "CDG",
"arrivalId": "DPS",
"outboundDate": "2025-07-01",
"returnDate": "2025-07-21",
"type": "roundTrip",
}
params = {k: v for k, v in params.items() if v}
url = "https://api.hasdata.com/scrape/google/flights"
headers = {
"Content-Type": "application/json",
"x-api-key": api_key,
}
response = requests.get(url, headers=headers, params=params)
data = response.json()
departure_token = None
for key in ["bestFlights", "otherFlights"]:
if key in data and data[key]:
departure_token = data[key][0].get("departureToken")
break
if departure_token:
params["departureToken"] = departure_token
response2 = requests.get(url, headers=headers, params=params)
data2 = response2.json()
print(data2)
Example response:
{
"bestFlights": [
{
"price": 1144,
"type": "Round trip",
"totalDuration": 1185,
"extensions": [
"Checked baggage for a fee"
],
"flights": [
{
"departureAirport": {
"id": "DPS",
"name": "I Gusti Ngurah Rai International Airport",
"time": "2025-07-21 16:55"
},
"arrivalAirport": {
"id": "CDG",
"name": "Paris Charles de Gaulle Airport",
"time": "2025-07-22 6:40"
},
"duration": 780,
"airline": "Malaysia Airlines",
"flightNumber": "MH 22",
"legroom": "31 in",
"travelClass": "Economy",
"extensions": [
"Average legroom (31 in)"
]
}
],
"layovers": [
{
"id": "KUL",
"name": "Kuala Lumpur International Airport",
"duration": 210
}
],
"carbonEmissions": {
"thisFlight": 780000,
"typicalForThisRoute": 857000,
"differencePercent": -9
},
"bookingToken": "W1t7ImRlcGFQ=="
}
],
"otherFlights": [
{
"price": 1111,
"type": "Round trip",
"totalDuration": 1865,
"extensions": [
"Checked baggage for a fee",
"Fare non-refundable, taxes may be refundable"
],
"flights": [
{
"departureAirport": {
"id": "DPS",
"name": "I Gusti Ngurah Rai International Airport",
"time": "2025-07-21 21:25"
},
"arrivalAirport": {
"id": "CDG",
"name": "Paris Charles de Gaulle Airport",
"time": "2025-07-22 22:30"
},
"duration": 225,
"airline": "Turkish Airlines",
"flightNumber": "TK 1829",
"legroom": "31 in",
"travelClass": "Economy",
"extensions": [
"Average legroom (31 in)"
]
}
],
"layovers": [
{
"id": "KUL",
"name": "Kuala Lumpur International Airport",
"duration": 620,
"overnight": true
}
],
"carbonEmissions": {
"thisFlight": 832000,
"typicalForThisRoute": 857000,
"differencePercent": -3
},
"bookingToken": "W1t7ImRlcGFMTgyOSJ9XV0="
}
],
"airports": [
{
"departures": [
{
"id": "CDG",
"name": "Paris Charles de Gaulle Airport",
"city": "Paris",
"country": "France",
"countryCode": "FR"
}
],
"arrivals": [
{
"id": "DPS",
"name": "I Gusti Ngurah Rai International Airport",
"city": "Denpasar",
"country": "Indonesia",
"countryCode": "ID"
}
]
}
]
}
Passengers & Cabin Class Options
Here’s how you set the cabin class and number of passengers:
travelClass
– flight class (economy
,premiumEconomy
,business
,first
).adults
,children
– number of adults and children.infantsInSeat
,infantsOnLap
– number of infants (with a separate seat or on a lap).
For children
, you need to specify kids aged 2 to 11 years. The flight class should be a text value (not a number): economy
, premiumEconomy
, business
, first
.
CURL request:
curl --location 'https://api.hasdata.com/scrape/google/flights?departureId=FRA&arrivalId=YYZ&outboundDate=2025-08-05&returnDate=2025-07-25&type=roundTrip&travelClass=first&adults=2&children=1&infantsInSeat=1&infantsOnLap=1' \
--header 'Content-Type: application/json' \
--header 'x-api-key: YOUR-API-KEY'
Python script:
import requests
api_key = "YOUR-API-KEY"
params = {
"departureId": "FRA",
"arrivalId": "YYZ",
"outboundDate": "2025-08-05",
"returnDate": "2025-07-25",
"type": "roundTrip",
"travelClass": "first", # Travel class: economy, premiumEconomy, business, first
"adults": 2, # Number of adult passengers
"children": 1, # Number of children (ages 2-11)
"infantsInSeat": 1, # Number of infants with their own seat
"infantsOnLap": 1, # Number of infants on a lap
}
params = {k: v for k, v in params.items() if v}
url = "https://api.hasdata.com/scrape/google/flights"
headers = {
"Content-Type": "application/json",
"x-api-key": api_key,
}
response = requests.get(url, headers=headers, params=params)
print(response.json())
Now, let’s go through some real examples.
Business Class for Two Adults and One Infant on Lap
Goal: Find flights from Frankfurt (FRA) to Toronto (YYZ) in business class for October 15th, 2025, with a return flight on October 30th, 2025. The tickets are for two adults and one infant on lap.
cURL request:
curl --location 'https://api.hasdata.com/scrape/google/flights?departureId=FRA&arrivalId=YYZ&outboundDate=2025-10-15&returnDate=2025-10-30&type=roundTrip&travelClass=business&adults=2&infantsOnLap=1' \
--header 'Content-Type: application/json' \
--header 'x-api-key: YOUR-API-KEY'
Python script parameters:
params = {
"departureId": "FRA",
"arrivalId": "YYZ",
"outboundDate": "2025-10-15",
"returnDate": "2025-10-30",
"type": "roundTrip",
"travelClass": "business",
"adults": 2,
"infantsOnLap": 1,
}
Example response:
{
"otherFlights": [
{
"type": "Round trip",
"totalDuration": 655,
"flights": [
{
"departureAirport": {
"id": "FRA",
"name": "Frankfurt Airport",
"time": "2025-10-15 10:55"
},
"arrivalAirport": {
"id": "DUB",
"name": "Dublin Airport",
"time": "2025-10-15 12:05"
},
"duration": 130,
"airplane": "Airbus A320",
"airline": "Aer Lingus",
"flightNumber": "EI 651",
"legroom": "30 in",
"travelClass": "Premium Economy",
"extensions": [
"Below average legroom (30 in)",
"Carbon emissions estimate: 339 kg"
]
},
{
"departureAirport": {
"id": "DUB",
"name": "Dublin Airport",
"time": "2025-10-15 13:15"
},
"arrivalAirport": {
"id": "YYZ",
"name": "Toronto Pearson International Airport",
"time": "2025-10-15 15:50"
},
"duration": 455,
"airplane": "Airbus A321neo",
"airline": "Aer Lingus",
"flightNumber": "EI 127",
"travelClass": "Business",
"extensions": [
"Lie flat seat",
"In-seat power & USB outlets",
"On-demand video",
"Free Wi‑Fi",
"Carbon emissions estimate: 1719 kg"
]
}
],
"layovers": [
{
"id": "DUB",
"name": "Dublin Airport",
"duration": 70
}
],
"carbonEmissions": {
"thisFlight": 2058000,
"typicalForThisRoute": 4514000,
"differencePercent": -54
},
"departureToken": "W1t7ImRlcGFydHVyZUlkIjoiRlJBIiwiYbGlnaHROdW1iZXIiOiIxMjcifV1d"
}
],
"airports": [
{
"departures": [
{
"id": "FRA",
"name": "Frankfurt Airport",
"city": "Frankfurt",
"country": "Germany",
"countryCode": "DE"
}
],
"arrivals": [
{
"id": "YYZ",
"name": "Toronto Pearson International Airport",
"city": "Toronto",
"country": "Canada",
"countryCode": "CA"
}
]
}
]
}
For return flights, you need to get the departureToken
from one of the flights and use it to fetch return options. We covered this in the first example, and we’ll break it down in detail later in the Complex Example section.
Family Trip (2 Adults, 2 Kids, 1 Infant with a Seat) in Economy
Goal: Find flights from Singapore (SIN) to Melbourne (MEL) in economy for November 1st, 2025, with a return on November 20, 2025. The tickets are for two adults, two kids, and one infant with a separate seat.
cURL request:
curl --location 'https://api.hasdata.com/scrape/google/flights?departureId=SIN&arrivalId=MEL&outboundDate=2025-11-01&returnDate=2025-11-20&type=roundTrip&travelClass=economy&adults=2&children=2&infantsInSeat=1' \
--header 'Content-Type: application/json' \
--header 'x-api-key: YOUR-API-KEY
Python script parameters:
params = {
"departureId": "SIN",
"arrivalId": "MEL",
"outboundDate": "2025-11-01",
"returnDate": "2025-11-20",
"type": "roundTrip",
"travelClass": "economy",
"adults": 2,
"children": 1,
"infantsInSeat": 1,
}
Example response:
{
"bestFlights": [
{
"price": 1697,
"type": "Round trip",
"totalDuration": 465,
"flights": [
{
"departureAirport": {
"id": "SIN",
"name": "Singapore Changi Airport",
"time": "2025-11-01 null:55"
},
"arrivalAirport": {
"id": "MEL",
"name": "Melbourne Airport",
"time": "2025-11-01 11:40"
},
"duration": 465,
"airplane": "Boeing 787",
"airline": "Scoot",
"flightNumber": "TR 18",
"legroom": "30 in",
"travelClass": "Economy",
"extensions": [
"Average legroom (30 in)",
"In-seat power outlet",
"Wi‑Fi for a fee",
"Carbon emissions estimate: 1372 kg"
]
}
],
"carbonEmissions": {
"thisFlight": 1372000,
"typicalForThisRoute": 1723000,
"differencePercent": -20
},
"departureToken": "W1t7ImRlcGFydHmbGlnaHROdW1iZXIiOiIxOCJ9XV0="
}
],
"otherFlights": [
{
"price": 1951,
"type": "Round trip",
"totalDuration": 780,
"flights": [
{
"departureAirport": {
"id": "SIN",
"name": "Singapore Changi Airport",
"time": "2025-11-01 14:15"
},
"arrivalAirport": {
"id": "PER",
"name": "Perth Airport",
"time": "2025-11-01 19:40"
},
"duration": 325,
"airplane": "Airbus A321neo",
"airline": "Jetstar",
"flightNumber": "JQ 98",
"legroom": "29 in",
"travelClass": "Economy",
"extensions": [
"Below average legroom (29 in)",
"In-seat USB outlet",
"Stream media to your device",
"Carbon emissions estimate: 950 kg"
]
}
],
"layovers": [
{
"id": "PER",
"name": "Perth Airport",
"duration": 240
}
],
"carbonEmissions": {
"thisFlight": 1628000,
"typicalForThisRoute": 1723000,
"differencePercent": -6
},
"departureToken": "W1t7ImRlcGFydsImZsaWdodE51bWJlciI6Ijk3MSJ9XV0="
}
],
"airports": [
{
"departures": [
{
"id": "SIN",
"name": "Singapore Changi Airport",
"city": "Singapore",
"country": "Singapore",
"countryCode": "SG"
}
],
"arrivals": [
{
"id": "MEL",
"name": "Melbourne Airport",
"city": "Melbourne",
"country": "Australia",
"countryCode": "AU"
}
]
}
]
}
Same as before, we’re not covering how to get return flights here.
Filtering & Customization
These parameters let you filter, sort, and customize your search:
sortBy
– sorting criteria (price
,duration
, etc.).stops
– number of layovers.excludeAirlines
,includeAirlines
– filter by airlines.bags
– number of carry-on bags per passenger.maxPrice
– max ticket price.lessEmissions
– prioritize flights with lower CO₂ emissions.
You can’t use excludeAirlines
and includeAirlines
together. You can provide only one of these fields.
CURL request:
curl --location 'https://api.hasdata.com/scrape/google/flights?departureId=SIN&arrivalId=MEL&outboundDate=2025-11-01&type=oneWay&sortBy=price&stops=twoStopsOrFewer&includeAirlines=AA%2CDL&bags=1&maxPrice=900&lessEmissions=true' \
--header 'Content-Type: application/json' \
--header 'x-api-key: YOUR-API-KEY'
Python script:
import requests
api_key = "YOUR-API-KEY"
params = {
"departureId": "SIN",
"arrivalId": "MEL",
"outboundDate": "2025-11-01",
"type": "oneWay",
"sortBy": "price", # Sorting preference: price, duration, stops, etc.
"stops": "twoStopsOrFewer", # Max number of layovers: '0', '1', '2', 'nonStop', 'oneStopOrFewer', 'twoStopsOrFewer'
"includeAirlines": "AA,DL", # Airlines to include (comma-separated IATA codes)
# "excludeAirlines": "UA", # Airlines to exclude (comma-separated IATA codes)
"bags": "1", # Number of checked bags per passenger
"maxPrice": "900", # Maximum price
"lessEmissions": "true", # Prefer flights with lower carbon emissions
}
params = {k: v for k, v in params.items() if v}
url = "https://api.hasdata.com/scrape/google/flights"
headers = {
"Content-Type": "application/json",
"x-api-key": api_key,
}
response = requests.get(url, headers=headers, params=params)
print(response.json())
The number of stops, as mentioned in the comments in the code, can be specified either as a number or as a text value.
Cheapest Non-Stop Flight to New York
Goal: Find direct flights from London (LHR) to New York (JFK) on June 10th, 2025 (one-way). Sort by price.
cURL request:
curl --location 'https://api.hasdata.com/scrape/google/flights?departureId=LHR&arrivalId=JFK&outboundDate=2025-06-15&type=oneWay&sortBy=price&stops=nonStop' \
--header 'Content-Type: application/json' \
--header 'x-api-key: YOUR-API-KEY'
Python script parameters:
params = {
"departureId": "LHR",
"arrivalId": "JFK",
"outboundDate": "2025-06-15",
"type": "oneWay",
"sortBy": "price",
"stops": "0",
}
Example response:
{
"otherFlights": [
{
"price": 1089,
"type": "One way",
"totalDuration": 484,
"flights": [
{
"departureAirport": {
"id": "LHR",
"name": "Heathrow Airport",
"time": "2025-06-15 8:15"
},
"arrivalAirport": {
"id": "JFK",
"name": "John F. Kennedy International Airport",
"time": "2025-06-15 11:19"
},
"duration": 484,
"airplane": "Airbus A321neo",
"airline": "JetBlue",
"flightNumber": "B6 2220",
"legroom": "32 in",
"travelClass": "Economy",
"extensions": [
"Above average legroom (32 in)",
"In-seat power & USB outlets",
"On-demand video",
"Free Wi‑Fi",
"Carbon emissions estimate: 500 kg"
]
}
],
"carbonEmissions": {
"thisFlight": 500000,
"typicalForThisRoute": 324000,
"differencePercent": 54
},
"bookingToken": "W1t7ImRlcGFydHXQ=="
}
],
"priceInsights": {
"lowestPrice": 1089,
"typicalPriceRange": [
670,
850
],
"priceLevel": "high",
"priceHistory": [
[
1736035200,
651
]
]
},
"airports": [
{
"departures": [
{
"id": "LHR",
"name": "Heathrow Airport",
"city": "London",
"country": "United Kingdom",
"countryCode": "GB"
}
],
"arrivals": [
{
"id": "JFK",
"name": "John F. Kennedy International Airport",
"city": "New York",
"country": "United States",
"countryCode": "US"
}
]
}
]
}
Flight to Tokyo with Bags, Low CO₂ Priority & No Ryanair
Goal: Find a one-way flight from Los Angeles (LAX) to Tokyo (HND) on August 10th, 2025. The price should be under $900, with one checked bag included. Exclude Ryanair (FR) flights. Priority goes to flights with low CO2 emissions.
cURL request:
curl --location 'https://api.hasdata.com/scrape/google/flights?departureId=LAX&arrivalId=HND&outboundDate=2025-08-10&type=oneWay&bags=1&maxPrice=900&lessEmissions=true&excludeAirlines=FR' \
--header 'Content-Type: application/json' \
--header 'x-api-key: YOUR-API-KEY'
Python script parameters:
params = {
"departureId": "LAX",
"arrivalId": "HND",
"outboundDate": "2025-08-10",
"type": "oneWay",
"excludeAirlines": "FR", # Airlines to exclude (comma-separated IATA codes)
"bags": "1", # Number of checked bags per passenger
"maxPrice": "900", # Maximum price
"lessEmissions": "true", # Prefer flights with lower carbon emissions
}
Example response:
{
"otherFlights": [
{
"price": 409,
"type": "One way",
"totalDuration": 680,
"flights": [
{
"departureAirport": {
"id": "LAX",
"name": "Los Angeles International Airport",
"time": "2025-08-10 10:40"
},
"arrivalAirport": {
"id": "HND",
"name": "Tokyo International Airport (Haneda Airport)",
"time": "2025-08-11 14:00"
},
"duration": 680,
"airplane": "Airbus A350",
"airline": "Delta",
"flightNumber": "DL 7",
"legroom": "31 in",
"travelClass": "Economy",
"extensions": [
"Average legroom (31 in)",
"In-seat power & USB outlets",
"On-demand video",
"Wi‑Fi for a fee",
"Carbon emissions estimate: 469 kg"
]
}
],
"carbonEmissions": {
"thisFlight": 469000,
"typicalForThisRoute": 561000,
"differencePercent": -16
},
"bookingToken": "W1t7ImRlcGFyIn1dXQ=="
}
],
"airports": [
{
"departures": [
{
"id": "LAX",
"name": "Los Angeles International Airport",
"city": "Los Angeles",
"country": "United States",
"countryCode": "US"
}
],
"arrivals": [
{
"id": "HND",
"name": "Tokyo International Airport (Haneda Airport)",
"city": "Tokyo",
"country": "Japan",
"countryCode": "JP"
}
]
}
]
}
Bangkok Flights with Emirates, One Layover, Max $1000
Goal: Find flights from Paris (CDG) to Bangkok (BKK) on September 10th, 2025, with no more than one stop, one-way, and only with Emirates (EK). Sort the flights by duration.
cURL request:
curl --location 'https://api.hasdata.com/scrape/google/flights?departureId=CDG&arrivalId=BKK&outboundDate=2025-09-10&type=oneWay&maxPrice=1000&includeAirlines=EK&stops=oneStopOrFewer&sortBy=duration' \
--header 'Content-Type: application/json' \
--header 'x-api-key: YOUR-API-KEY'
Python script parameters:
params = {
"departureId": "CDG",
"arrivalId": "BKK",
"outboundDate": "2025-09-10",
"type": "oneWay",
"sortBy": "duration",
"stops": "oneStopOrFewer",
"includeAirlines": "EK",
"maxPrice": "1000",
}
Example response:
{
"otherFlights": [
{
"price": 727,
"type": "One way",
"totalDuration": 930,
"extensions": [
"1 checked bag up to 30 kg included",
"Full refund for cancellations"
],
"flights": [
{
"departureAirport": {
"id": "CDG",
"name": "Paris Charles de Gaulle Airport",
"time": "2025-09-10 21:55"
},
"arrivalAirport": {
"id": "BKK",
"name": "Suvarnabhumi Airport",
"time": "2025-09-11 18:25"
},
"duration": 385,
"airplane": "Boeing 777",
"airline": "Emirates",
"flightNumber": "EK 370",
"legroom": "32 in",
"travelClass": "Economy",
"extensions": [
"Above average legroom (32 in)",
"In-seat power & USB outlets"
]
}
],
"layovers": [
{
"id": "DXB",
"name": "Dubai International Airport",
"duration": 145
}
],
"carbonEmissions": {
"thisFlight": 708000,
"typicalForThisRoute": 671000,
"differencePercent": 6
},
"bookingToken": "W1t7ImRlcGFydHVyZMCJ9XV0="
}
],
"priceInsights": {
"lowestPrice": 727,
"typicalPriceRange": [
670,
710
]
},
"airports": [
{
"departures": [
{
"id": "CDG",
"name": "Paris Charles de Gaulle Airport",
"city": "Paris",
"country": "France",
"countryCode": "FR"
}
],
"arrivals": [
{
"id": "BKK",
"name": "Suvarnabhumi Airport",
"city": "Bangkok",
"country": "Thailand",
"countryCode": "TH"
}
]
}
]
}
Time-Based Filtering
These filters let you set flight time and duration limits, including:
outboundTimes
,returnTimes
– departure/arrival time ranges. You can set up to 4 time boundaries (2 for departure, 2 for arrival) to filter flights.layoverDuration
– min and max layover duration.maxDuration
– max flight duration.
The most confusing filters here are outboundTimes
and returnTimes
, they must be set using military time. For example, if you want the outbound flight to be between 5 AM and 7 AM, you can just set:
outboundTimes = "5,7"
Now, for the return flight between 6 PM and 11 PM, you’d set it like this:
returnTimes = "18,23"
But what if we want to set both the departure and arrival times? Let’s say the outbound flight is between 8 AM and 11 AM, and the arrival time is between 4 PM and 9 PM:
outboundTimes = "8,11,16,21"
The same goes for the return flight. For example, outbound from 3 AM to 6 AM, and arrival from 9 AM to 11 AM:
returnTimes = "3,6,9,11"
Here’s a cURL request with all parameters:
curl --location 'https://api.hasdata.com/scrape/google/flights?departureId=CDG&arrivalId=FCO&outboundDate=2025-09-06&returnDate=2025-09-06&outboundTimes=7,10,14,18&returnTimes=16,19,20,23&layoverDuration=45,360&maxDuration=900' \
--header 'Content-Type: application/json' \
--header 'x-api-key: YOUR-API-KEY'
Python script:
import requests
api_key = "YOUR-API-KEY"
params = {
"departureId": "CDG",
"arrivalId": "FCO",
"outboundDate": "2025-09-06",
"returnDate": "2025-09-06",
"outboundTimes": "7,10,14,18", # Preferred outbound time ranges (HH,HH,HH,HH) - first two for earliest/latest departure, last two for arrival
"returnTimes": "16,19,20,23", # Preferred return time ranges (HH,HH,HH,HH) - first two for earliest/latest departure, last two for arrival
"layoverDuration": "45,360", # Min and max layover duration in minutes
"maxDuration": "900" # Maximum total flight duration (in minutes)
}
params = {k: v for k, v in params.items() if v}
url = "https://api.hasdata.com/scrape/google/flights"
headers = {
"Content-Type": "application/json",
"x-api-key": api_key,
}
response = requests.get(url, headers=headers, params=params)
print(response.json())
Now, a real case: let’s find flights from Paris (CDG) to Rome (FCO) on September 6th, 2025, departing in the morning (7 AM – 12 PM) with a max flight time of 3 hours (180 min).
cURL request:
curl --location 'https://api.hasdata.com/scrape/google/flights?departureId=CDG&arrivalId=FCO&outboundDate=2025-09-06&type=roundTrip&returnDate=2025-09-06&outboundTimes=7,12&maxDuration=180' \
--header 'Content-Type: application/json' \
--header 'x-api-key: YOUR-API-KEY'
Python script parameters:
params = {
"departureId": "CDG",
"arrivalId": "FCO",
"outboundDate": "2025-09-06",
"returnDate": "2025-09-06",
"outboundTimes": "7,12",
"maxDuration": "180"
}
Example response:
{
"otherFlights": [
{
"price": 222,
"type": "Round trip",
"totalDuration": 125,
"extensions": [
"Checked baggage for a fee",
"Full refund for cancellations",
"Free change, possible fare difference",
"Bag and fare conditions depend on the return flight"
],
"flights": [
{
"departureAirport": {
"id": "CDG",
"name": "Paris Charles de Gaulle Airport",
"time": "2025-09-06 12:45"
},
"arrivalAirport": {
"id": "FCO",
"name": "Leonardo da Vinci International Airport",
"time": "2025-09-06 14:50"
},
"duration": 125,
"airplane": "Airbus A320",
"airline": "Air France",
"flightNumber": "AF 1504",
"legroom": "29 in",
"travelClass": "Economy",
"extensions": [
"Below average legroom (29 in)",
"In-seat USB outlet",
"Wi‑Fi for a fee",
"Carbon emissions estimate: 105 kg"
]
}
],
"carbonEmissions": {
"thisFlight": 105000,
"typicalForThisRoute": 107000,
"differencePercent": -2
},
"departureToken": "W1t7ImRlcGFydHVyZUlkIjoiQ0RHIiwiYXJyaXZhbElkIjoiRkNPIiwiZGF0ZSI"
}
],
"airports": [
{
"departures": [
{
"id": "CDG",
"name": "Paris Charles de Gaulle Airport",
"city": "Paris",
"country": "France",
"countryCode": "FR"
}
],
"arrivals": [
{
"id": "FCO",
"name": "Leonardo da Vinci International Airport",
"city": "Rome",
"country": "Italy",
"countryCode": "IT"
}
]
}
]
}
Layover Airport Filtering
This one’s simple – just two parameters:
includeConnections
– allowed layover airports.excludeConnections
– excluded layover airports.
One thing to note: you can’t use both at the same time. Pick one.
cURL request:
curl --location 'https://api.hasdata.com/scrape/google/flights?departureId=ORD&arrivalId=BKK&outboundDate=2025-08-15&type=oneWay&excludeConnections=DXB,PEK' \
--header 'Content-Type: application/json' \
--header 'x-api-key: YOUR-API-KEY'
Python script:
import requests
api_key = "YOUR-API-KEY"
params = {
"departureId": "ORD",
"arrivalId": "BKK",
"outboundDate": "2025-08-15",
"type": "oneWay",
# "includeConnections": "DOH,FRA", # Required connection airports (comma-separated IATA codes)
"excludeConnections": "DXB,PEK", # Forbidden connection airports (comma-separated IATA codes)
}
params = {k: v for k, v in params.items() if v}
url = "https://api.hasdata.com/scrape/google/flights"
headers = {
"Content-Type": "application/json",
"x-api-key": api_key,
}
response = requests.get(url, headers=headers, params=params)
print(response.json())
Let’s find direct flights from Berlin (BER) to Barcelona (BCN) on June 15th, 2025, while excluding layovers in London and Paris.
cURL request:
curl --location 'https://api.hasdata.com/scrape/google/flights?departureId=BER&arrivalId=BCN&outboundDate=2025-06-15&type=oneWay&excludeConnections=LHR,CDG' \
--header 'Content-Type: application/json' \
--header 'x-api-key: YOUR-API-KEY'
Python script parameters:
params = {
"departureId": "BER",
"arrivalId": "BCN",
"outboundDate": "2025-06-15",
"type": "oneWay",
"excludeConnections": "LHR,CDG"
}
Example response:
{
"bestFlights": [
{
"price": 64,
"type": "One way",
"totalDuration": 155,
"flights": [
{
"departureAirport": {
"id": "BER",
"name": "Berlin Brandenburg Airport",
"time": "2025-06-15 16:15"
},
"arrivalAirport": {
"id": "BCN",
"name": "Josep Tarradellas Barcelona-El Prat Airport",
"time": "2025-06-15 18:50"
},
"duration": 155,
"airplane": "Boeing 737",
"airline": "Ryanair",
"flightNumber": "FR 132",
"legroom": "30 in",
"travelClass": "Economy",
"extensions": [
"Average legroom (30 in)",
"Carbon emissions estimate: 128 kg"
]
}
],
"carbonEmissions": {
"thisFlight": 128000,
"typicalForThisRoute": 138000,
"differencePercent": -7
},
"bookingToken": "W1t7ImRlcGFydHVyZUlkIjoiQkVSIiw"
}
],
"otherFlights": [
{
"price": 70,
"type": "One way",
"totalDuration": 155,
"flights": [
{
"departureAirport": {
"id": "BER",
"name": "Berlin Brandenburg Airport",
"time": "2025-06-15 6:00"
},
"arrivalAirport": {
"id": "BCN",
"name": "Josep Tarradellas Barcelona-El Prat Airport",
"time": "2025-06-15 8:35"
},
"duration": 155,
"airplane": "Airbus A320",
"airline": "easyJet",
"flightNumber": "U2 5107",
"legroom": "29 in",
"travelClass": "Economy",
"extensions": [
"Below average legroom (29 in)",
"Carbon emissions estimate: 134 kg"
]
}
],
"carbonEmissions": {
"thisFlight": 134000,
"typicalForThisRoute": 138000,
"differencePercent": -3
},
"bookingToken": "W1t7ImRlcGFydHVyZUlkIjoiQkVSIiwiYXJy"
}
],
"airports": [
{
"departures": [
{
"id": "BER",
"name": "Berlin Brandenburg Airport",
"city": "Berlin",
"country": "Germany",
"countryCode": "DE"
}
],
"arrivals": [
{
"id": "BCN",
"name": "Josep Tarradellas Barcelona-El Prat Airport",
"city": "Barcelona",
"country": "Spain",
"countryCode": "ES"
}
]
}
]
}
Booking and Advanced Options
Other parameters worth mentioning:
bookingToken
– fetch booking details for selected flights, has a short lifetime.showHidden
– include hidden flights.
bookingToken
is pulled from flight search results and helps retrieve booking options. Important: It can’t be used with departureToken
.
cURL request:
curl --location 'https://api.hasdata.com/scrape/google/flights?departureId=ORD&arrivalId=BKK&outboundDate=2025-08-15&type=oneWay&showHidden=true&bookingToken=W1t7ImRlcGFydHVyZU' \
--header 'Content-Type: application/json' \
--header 'x-api-key: YOUR-API-KEY'
Python script:
import requests
api_key = "YOUR-API-KEY"
params = {
"departureId": "ORD",
"arrivalId": "BKK",
"outboundDate": "2025-08-15",
"type": "oneWay",
"showHidden": "true", # Show full list of results with hidden ones
"bookingToken": "W1t7ImRlcGFydHVyZU" # Show booking options for selected flights
}
params = {k: v for k, v in params.items() if v}
url = "https://api.hasdata.com/scrape/google/flights"
headers = {
"Content-Type": "application/json",
"x-api-key": api_key,
}
response = requests.get(url, headers=headers, params=params)
print(response.json())
Example response:
{
"requestMetadata": {
"html": "https://f005.backblazeb2.com/file/hasdata-screenshots/0d408ae3-b621-4f8b-9e88-b94b9853ac4f.html",
"url": "https://www.google.com/travel/flights/search?hl=en&gl=us&curr=USD&tfs=CBwQAhpdEgoyMDI1LTA4LTE1Ih0KA09SRBIKMjAyNS0wOC0xNRoDSVNUKgJUSzIBNiIeCgNJU1QSCjIwMjUtMDgtMTYaA0JLSyoCVEsyAjU4agcIARIDT1JEcgcIARIDQktLQgEBSAFwAYIBCwj___________8BmAEC&tfu=EgYIABABGAA"
},
"selectedFlights": [
{
"type": "Round trip",
"totalDuration": 1285,
"flights": [
{
"departureAirport": {
"id": "ORD",
"name": "Chicago O'Hare International Airport",
"time": "2025-08-15 19:50"
},
"arrivalAirport": {
"id": "IST",
"name": "Istanbul Airport",
"time": "2025-08-16 14:40"
},
"duration": 650,
"airplane": "Boeing 777",
"airline": "Turkish Airlines",
"flightNumber": "TK 6",
"oftenDelayedByOver30Min": true,
"legroom": "31 in",
"travelClass": "Economy",
"extensions": [
"Average legroom (31 in)"
]
}
],
"layovers": [
{
"id": "IST",
"name": "Istanbul Airport",
"duration": 80
}
],
"carbonEmissions": {
"thisFlight": 1012000,
"typicalForThisRoute": 1079000,
"differencePercent": -6
}
}
],
"priceInsights": {
"lowestPrice": 614,
"typicalPriceRange": [
590,
1550
],
"priceLevel": "typical",
"priceHistory": [
[
1735970400,
658
],
[
1736056800,
658
]
]
},
"bookingOptions": [
{
"together": {
"bookWith": "Turkish Airlines",
"marketedAs": [
"TK 6",
"TK 58"
],
"optionTitle": "Promotional",
"price": 614,
"bookingRequest": {
"url": "https://www.google.com/travel/clk/f",
"postData": "u=EqgjCgJUSxoECAMQAULJBAoCVVMSBWVuLVVTGhQtOTE2ODkyNjg2OTkxNzE1OTQ0NyIDVVNEKAA6BQj-"
}
}
}
]
}
Also, keep in mind that when using bookingToken
, basic search parameters like departureId
and outboundDate
don’t affect the final result.
Complex Example
Sometimes, a single request isn’t enough to get the data you need. That’s why we’ve set aside this section to show an example of a more complex request. Since we’ve already covered how to work with all the parameters in the sections above, we won’t go over cURL requests again.
So, let’s say we need economy class tickets from Paris (CDG) to London (LTN) on July 1st, 2025, for two adults and two kids. We’re only looking for flights from EASYJET UK LIMITED (U2) and British Airways PLC (BA), sorted by price, with a max budget of $700. Ideally, just one layover. We also need return tickets for July 25th, 2025, with the same conditions.
To get this data, we need to make three requests:
- Find flights to London and pick the cheapest one.
- Get return flights from the same airline.
- Fetch booking options for the selected flights.
The parameters will stay the same across all requests, the only differences will be departureID
and bookingID
, which we’ll extract from the responses.
The script starts the same way as in previous examples, with just a few parameter adjustments:
params = {
"departureId": "CDG",
"arrivalId": "LTN",
"outboundDate": "2025-07-01",
"returnDate": "2025-07-25",
"type": "roundTrip",
"adults": "2",
"children": "2",
"travelClass": "economy",
"stops": "1",
"maxPrice": "700",
"includeAirlines": "U2,BA",
"sort": "price",
"showHidden": "true"
}
Once we get the data, we extract the departureToken
from the first flight. Since the results are sorted by price
, this will be the cheapest option:
departure_token = None
for key in ["bestFlights", "otherFlights"]:
if key in data and data[key]:
departure_token = data[key][0].get("departureToken")
break
If we have the departureToken
, we move to the next step - adding it to the request parameters and fetching return flights:
if departure_token:
params["departureToken"] = departure_token
response2 = requests.get(url, headers=headers, params=params)
data2 = response2.json()
Same process here – we need to extract the bookingToken
:
booking_token = None
for key in ["bestFlights", "otherFlights"]:
if key in data2 and data2[key]:
booking_token = data2[key][0].get("bookingToken")
break
Once we have the bookingToken
, we make the third and final request to retrieve booking details and display them:
if booking_token:
params.pop("departureToken", None)
params["bookingToken"] = booking_token
response3 = requests.get(url, headers=headers, params=params)
booking_data = response3.json()
print("\n=== Booking Results ===\n")
print(f"\nURL: {booking_data['requestMetadata']['url']}\n")
for i, trip in enumerate(booking_data.get("selectedFlights", []), 1):
print(f" Flight {i} ({trip['type']}):")
print(f" Total duration: {trip['totalDuration']} min.")
print(f" Layovers: {', '.join([lay['name'] for lay in trip.get('layovers', [])]) if trip.get('layovers') else 'Non-stop'}")
print(f" Carbon footprint: {trip['carbonEmissions']['thisFlight'] / 1000} kg CO₂")
for flight in trip["flights"]:
print(f" {flight['departureAirport']['name']} ({flight['departureAirport']['time']}) -> {flight['arrivalAirport']['name']} ({flight['arrivalAirport']['time']})")
print(f" {flight['airline']} | {flight['airplane']} | {flight['flightNumber']}")
print(f" {flight['travelClass']} | {flight['legroom']} | {'Frequently delayed' if flight.get('oftenDelayedByOver30Min') else 'On time'}")
print()
print("\n=== Booking Options ===\n")
for option in booking_data.get("bookingOptions", []):
if "together" in option:
together = option["together"]
print(f" Book with {together['bookWith']}")
print(f" Flights: {', '.join(together['marketedAs'])}")
Now, let’s put it all together:
import requests
api_key = "YOUR-API-KEY"
params = {
"departureId": "CDG",
"arrivalId": "LTN",
"outboundDate": "2025-07-01",
"returnDate": "2025-07-25",
"type": "roundTrip",
"adults": "2",
"children": "2",
"travelClass": "economy",
"stops": "1",
"maxPrice": "700",
"includeAirlines": "U2,BA",
"sort": "price",
"showHidden": "true"
}
params = {k: v for k, v in params.items() if v}
url = "https://api.hasdata.com/scrape/google/flights"
headers = {
"Content-Type": "application/json",
"x-api-key": api_key,
}
response = requests.get(url, headers=headers, params=params)
data = response.json()
departure_token = None
for key in ["bestFlights", "otherFlights"]:
if key in data and data[key]:
departure_token = data[key][0].get("departureToken")
break
if departure_token:
params["departureToken"] = departure_token
response2 = requests.get(url, headers=headers, params=params)
data2 = response2.json()
booking_token = None
for key in ["bestFlights", "otherFlights"]:
if key in data2 and data2[key]:
booking_token = data2[key][0].get("bookingToken")
break
if booking_token:
params.pop("departureToken", None)
params["bookingToken"] = booking_token
response3 = requests.get(url, headers=headers, params=params)
booking_data = response3.json()
print("\n=== Booking Results ===\n")
print(f"\nURL: {booking_data['requestMetadata']['url']}\n")
for i, trip in enumerate(booking_data.get("selectedFlights", []), 1):
print(f" Flight {i} ({trip['type']}):")
print(f" Total duration: {trip['totalDuration']} min.")
print(f" Layovers: {', '.join([lay['name'] for lay in trip.get('layovers', [])]) if trip.get('layovers') else 'Non-stop'}")
print(f" Carbon footprint: {trip['carbonEmissions']['thisFlight'] / 1000} kg CO₂")
for flight in trip["flights"]:
print(f" {flight['departureAirport']['name']} ({flight['departureAirport']['time']}) -> {flight['arrivalAirport']['name']} ({flight['arrivalAirport']['time']})")
print(f" {flight['airline']} | {flight['airplane']} | {flight['flightNumber']}")
print(f" {flight['travelClass']} | {flight['legroom']} | {'Frequently delayed' if flight.get('oftenDelayedByOver30Min') else 'On time'}")
print()
print("\n=== Booking Options ===\n")
for option in booking_data.get("bookingOptions", []):
if "together" in option:
together = option["together"]
print(f"Book with {together['bookWith']}")
print(f" Flights: {', '.join(together['marketedAs'])}")
else:
print("Error: No bookingToken found in second request.")
else:
print("Error: No departureToken found in first request.")
Here’s the result:
You can also find this script in Colab Research and use it for your own needs, just replace the parameters as needed.
Conclusion
In this article, we explored how to work with the HasData’s Google Flights API, covered all its features, and provided many examples. Now, you can use these skills to find real-time ticket prices, track price changes, and analyze past trends to spot the best deals. You can also build a custom flight search and booking site, making travel planning easier.

Might Be Interesting

Feb 10, 2025
How To Retry Failed Python Requests
Learn how to implement retry mechanisms in Python Requests to handle timeout errors, HTTP status codes like 403, 429, 500, 502, 503, and 504, and avoid infinite loops with effective backoff strategies.
- Python
- Tools and Libraries
- Tutorials and guides

Jan 6, 2025
How to Scrape Google Maps Reviews
Learn how to scrape Google Maps reviews effectively using Python, APIs, or no-code tools. Explore methods to extract reviews for specific places, multiple locations, and search results, with step-by-step guidance and tips.
- Tutorials and guides
- Business
- Python

Oct 29, 2024
How to Scrape YouTube Data for Free: A Complete Guide
Learn effective methods for scraping YouTube data, including extracting video details, channel info, playlists, comments, and search results. Explore tools like YouTube Data API, yt-dlp, and Selenium for a step-by-step guide to accessing valuable YouTube insights.
- Python
- Tutorials and guides
- Tools and Libraries