This summer I’m planning to have a trip that will include Moscow, Irkutsk, Beijing, Shanghai, and Tokyo. As I’m flexible on dates I’ve decided to try to find the cheapest flights with the shortest duration. I’ve tried to do this before twice by parsing Google Flights, it was successful, but I don’t want to update those hackish scripts and want to try something a bit saner.
So I chose to try Amadeus API. It was a bit painful to use, some endpoints were randomly failing with 500, and they needed a signed agreement to use real data. But overall it was at least better than parsing Google Flights, and the whole adventure fit inside the free quota for requests.
TLDR: jupyter notebook with the whole adventure
Restrictions
I’m flexible but with boundaries, so I’ll be able to start between 10th and 20th of July and travel no longer than 21 days:
min_start = date(2019, 7, 10)
max_start = date(2019, 7, 20)
max_days = 21
I mostly don’t want to have multi-segment flights and know how many days I want to spend in destinations:
places_df = pd.DataFrame([('Amsterdam', 'NL', 0, (max_start - min_start).days, True), # for enabling tentative start date
('Moscow', 'RU', 3, 5, True),
('Irkutsk', 'RU', 7, 10, True),
('Beijing', 'CN', 3, 5, True),
('Shanghai', 'CN', 3, 5, True),
('Tokyo', 'JP', 3, 5, False),
('Amsterdam', 'NL', 0, 0, True)], # the final destination
columns=['city', 'cc', 'min_days', 'max_days', 'only_direct'])
places_df['min_day_of_dep'] = places_df.min_days.rolling(min_periods=1, window=len(places_df)).sum()
places_df['max_day_of_dep'] = places_df.max_days.rolling(min_periods=1, window=len(places_df)).sum()
places_df
city | cc | min_days | max_days | only_direct | min_day_of_dep | max_day_of_dep | |
---|---|---|---|---|---|---|---|
0 | Amsterdam | NL | 0 | 10 | True | 0.0 | 10.0 |
1 | Moscow | RU | 3 | 5 | True | 3.0 | 15.0 |
2 | Irkutsk | RU | 7 | 10 | True | 10.0 | 25.0 |
3 | Beijing | CN | 3 | 5 | True | 13.0 | 30.0 |
4 | Shanghai | CN | 3 | 5 | True | 16.0 | 35.0 |
5 | Tokyo | JP | 3 | 5 | False | 19.0 | 40.0 |
6 | Amsterdam | NL | 0 | 0 | True | 19.0 | 40.0 |
Airports
A lot of big cities have more than one airport, and usually, some airports are for low-costers and some for pricier flights. But the most important that the API expects me to send IATA codes to get prices for dates. So I needed to get IATA codes for airports for cities I will travel through, and it’s possible with just a request to /reference-data/locations:
def get_iata(city, cc):
response = call_api('/reference-data/locations', # full code in the notebook
keyword=city,
countryCode=cc,
subType='AIRPORT')
return [result['iataCode'] for result in response['data']]
get_iata('Moscow', 'RU')
['DME', 'SVO', 'VKO']
With that function, I was able to get IATA codes for all destinations and get all possible routes with a bit of pandas magic:
places_df['iata'] = places_df.apply(lambda place: get_iata(place['city'], place['cc']), axis=1)
routes_df = places_df.assign(dest_iata=places_df.iloc[1:].reset_index().iata)
routes_df['routes'] = routes_df.apply(
lambda row: [*product(row['iata'], row['dest_iata'])] if isinstance(row['dest_iata'], list) else [],
axis=1)
routes_df = routes_df.routes \
.apply(pd.Series) \
.merge(routes_df, right_index=True, left_index=True) \
.drop(['routes', 'min_days', 'max_days', 'iata', 'dest_iata'], axis=1) \
.melt(id_vars=['city', 'cc', 'min_day_of_dep', 'max_day_of_dep', 'only_direct'], value_name="route") \
.drop('variable', axis=1) \
.dropna()
routes_df['origin'] = routes_df.route.apply(lambda route: route[0])
routes_df['destination'] = routes_df.route.apply(lambda route: route[1])
routes_df = routes_df \
.drop('route', axis=1) \
.rename(columns={'city': 'origin_city',
'cc': 'origin_cc'})
routes_df.head(10)
origin_city | origin_cc | min_day_of_dep | max_day_of_dep | only_direct | origin | destination | |
---|---|---|---|---|---|---|---|
0 | Amsterdam | NL | 0.0 | 10.0 | True | AMS | DME |
1 | Moscow | RU | 3.0 | 15.0 | True | DME | IKT |
2 | Irkutsk | RU | 10.0 | 25.0 | True | IKT | PEK |
3 | Beijing | CN | 13.0 | 30.0 | True | PEK | PVG |
4 | Shanghai | CN | 16.0 | 35.0 | True | PVG | HND |
5 | Tokyo | JP | 19.0 | 40.0 | False | HND | AMS |
7 | Amsterdam | NL | 0.0 | 10.0 | True | AMS | SVO |
8 | Moscow | RU | 3.0 | 15.0 | True | SVO | IKT |
9 | Irkutsk | RU | 10.0 | 25.0 | True | IKT | NAY |
10 | Beijing | CN | 13.0 | 30.0 | True | PEK | SHA |
To understand the complexity of the problem better I draw an ugly graph of possible flights routes:
Prices and dates
After that I’ve calculated all possible dates for flights:
route_dates_df = routes_df.assign(
dates=routes_df.apply(lambda row: [min_start + timedelta(days=days)
for days in range(int(row.min_day_of_dep), int(row.max_day_of_dep) + 1)],
axis=1))
route_dates_df = route_dates_df.dates \
.apply(pd.Series) \
.merge(route_dates_df, right_index=True, left_index=True) \
.drop(['dates', 'min_day_of_dep', 'max_day_of_dep'], axis=1) \
.melt(id_vars=['origin_city', 'origin_cc', 'origin', 'destination', 'only_direct'], value_name="date") \
.drop('variable', axis=1) \
.dropna()
valid_routes_df = route_dates_df[route_dates_df.date <= max_start + timedelta(days=max_days)]
valid_routes_df.head(10)
origin_city | origin_cc | origin | destination | only_direct | date | |
---|---|---|---|---|---|---|
0 | Amsterdam | NL | AMS | DME | True | 2019-07-10 |
1 | Moscow | RU | DME | IKT | True | 2019-07-13 |
2 | Irkutsk | RU | IKT | PEK | True | 2019-07-20 |
3 | Beijing | CN | PEK | PVG | True | 2019-07-23 |
4 | Shanghai | CN | PVG | HND | True | 2019-07-26 |
5 | Tokyo | JP | HND | AMS | False | 2019-07-29 |
6 | Amsterdam | NL | AMS | SVO | True | 2019-07-10 |
7 | Moscow | RU | SVO | IKT | True | 2019-07-13 |
8 | Irkutsk | RU | IKT | NAY | True | 2019-07-20 |
9 | Beijing | CN | PEK | SHA | True | 2019-07-23 |
Eventually, I’ve got 363 possible route-date combinations, and used /shopping/flight-offers to get prices. As the endpoint has a quota of 2000 free requests, I was able to mess everything up a few times and haven’t reached it yet:
def get_prices(origin, destination, date, only_direct):
response = call_api('/shopping/flight-offers',
origin=origin,
destination=destination,
nonStop='true' if only_direct else 'false',
departureDate=date.strftime("%Y-%m-%d"))
if 'data' not in response:
print(response)
return []
return [(origin, destination, date,
Decimal(offer_item['price']['total']),
parse_date(offer_item['services'][0]['segments'][0]['flightSegment']['departure']['at']),
parse_date(offer_item['services'][0]['segments'][-1]['flightSegment']['arrival']['at']),
len(offer_item['services'][0]['segments']))
for flight in response['data']
for offer_item in flight['offerItems']]
get_prices('IKT', 'PEK', date(2019, 7, 20), True)[:5]
[('IKT',
'PEK',
datetime.date(2019, 7, 20),
Decimal('209.11'),
datetime.datetime(2019, 7, 20, 1, 50, tzinfo=tzoffset(None, 28800)),
datetime.datetime(2019, 7, 20, 4, 40, tzinfo=tzoffset(None, 28800)),
1),
('IKT',
'PEK',
datetime.date(2019, 7, 20),
Decimal('262.98'),
datetime.datetime(2019, 7, 20, 15, 15, tzinfo=tzoffset(None, 28800)),
datetime.datetime(2019, 7, 20, 18, 5, tzinfo=tzoffset(None, 28800)),
1)]
Then I’ve fetched flights for the whole set of dates, assigned useful metadata like origin/destination cities and duration of the flights, and removed flights pricier than €800:
prices_df = pd.DataFrame([price
for route in valid_routes_df.to_dict('record')
for price in get_prices(route['origin'], route['destination'], route['date'], route['only_direct'])],
columns=['origin', 'destination', 'date', 'price', 'departure_at', 'arrival_at', 'segments'])
airport_to_city = dict(zip(routes_df.origin, routes_df.origin_city))
prices_with_city_df = prices_df \
.assign(duration=prices_df.arrival_at - prices_df.departure_at,
origin_city=prices_df.origin.apply(airport_to_city.__getitem__),
destination_city=prices_df.destination.apply(airport_to_city.__getitem__))
prices_with_city_df['route'] = prices_with_city_df.origin_city + " ✈️ " + prices_with_city_df.destination_city
valid_prices_with_city_df = prices_with_city_df[prices_with_city_df.price <= 800]
valid_prices_with_city_df.head()
origin | destination | date | price | departure_at | arrival_at | segments | duration | origin_city | destination_city | route | |
---|---|---|---|---|---|---|---|---|---|---|---|
0 | DME | IKT | 2019-07-13 | 257.40 | 2019-07-13 21:40:00+03:00 | 2019-07-14 08:25:00+08:00 | 1 | 05:45:00 | Moscow | Irkutsk | Moscow✈️Irkutsk |
1 | DME | IKT | 2019-07-13 | 257.40 | 2019-07-13 23:00:00+03:00 | 2019-07-14 09:45:00+08:00 | 1 | 05:45:00 | Moscow | Irkutsk | Moscow✈️Irkutsk |
2 | DME | IKT | 2019-07-13 | 254.32 | 2019-07-13 19:55:00+03:00 | 2019-07-14 06:25:00+08:00 | 1 | 05:30:00 | Moscow | Irkutsk | Moscow✈️Irkutsk |
3 | DME | IKT | 2019-07-13 | 227.40 | 2019-07-13 18:30:00+03:00 | 2019-07-14 05:15:00+08:00 | 1 | 05:45:00 | Moscow | Irkutsk | Moscow✈️Irkutsk |
4 | IKT | PEK | 2019-07-20 | 209.11 | 2019-07-20 01:50:00+08:00 | 2019-07-20 04:40:00+08:00 | 1 | 02:50:00 | Irkutsk | Beijing | Irkutsk✈️Beijing |
To have a brief overview of prices I’ve made a scatterplot. If I was a machine learning algorithm I would exclude Tokyo from the adventure:
Itinerary
At this stage I’ve got all the data I want, so I can begin building the itinerary. I’ve calculated all possible city/date combination of flights. Job interviews questions prepared me for that:
next_flight_origin_city = dict(zip(places_df.city.iloc[:-2], places_df.city.iloc[1:-1]))
place_min_days = dict(zip(places_df.city.iloc[:-1], places_df.min_days.iloc[:-1]))
place_max_days = dict(zip(places_df.city.iloc[:-1], places_df.max_days.iloc[:-1]))
def build_itinerary(place, date):
if place is None:
return
next_place = next_flight_origin_city.get(place)
for days in range(place_min_days[place], place_max_days[place] + 1):
flight_date = date + timedelta(days=days)
for rest_flights in build_itinerary(next_place, flight_date):
yield [(place, flight_date), *rest_flights]
if next_place is None:
yield [(place, flight_date)]
itinerary = [*build_itinerary('Amsterdam', min_start)]
itinerary[:3]
[[('Amsterdam', datetime.date(2019, 7, 10)),
('Moscow', datetime.date(2019, 7, 13)),
('Irkutsk', datetime.date(2019, 7, 20)),
('Beijing', datetime.date(2019, 7, 23)),
('Shanghai', datetime.date(2019, 7, 26)),
('Tokyo', datetime.date(2019, 7, 29))],
[('Amsterdam', datetime.date(2019, 7, 10)),
('Moscow', datetime.date(2019, 7, 13)),
('Irkutsk', datetime.date(2019, 7, 20)),
('Beijing', datetime.date(2019, 7, 23)),
('Shanghai', datetime.date(2019, 7, 26)),
('Tokyo', datetime.date(2019, 7, 30))],
[('Amsterdam', datetime.date(2019, 7, 10)),
('Moscow', datetime.date(2019, 7, 13)),
('Irkutsk', datetime.date(2019, 7, 20)),
('Beijing', datetime.date(2019, 7, 23)),
('Shanghai', datetime.date(2019, 7, 26)),
('Tokyo', datetime.date(2019, 7, 31))]]
And then I’ve found all flights for those dates. As amount of possible flights
combinations didn’t fit in my RAM, I was selecting n_cheapest
flights on each stage.
The code is slow and ugly, but it worked:
def find_flights(prices_with_city_df, itinerary_route, n_cheapest):
result_df = None
for place, date in itinerary_route:
place_df = prices_with_city_df \
[(prices_with_city_df.origin_city == place) & (prices_with_city_df.date == date)] \
.sort_values('price', ascending=True) \
.head(n_cheapest) \
.add_prefix(f'{place}_')
if result_df is None:
result_df = place_df
else:
result_df = result_df \
.assign(key=1) \
.merge(place_df.assign(key=1), on="key") \
.drop("key", axis=1)
result_df['total_price'] = reduce(operator.add, (
result_df[column] for column in result_df.columns
if 'price' in column and column != 'total_price'
))
result_df = result_df \
.sort_values('total_price', ascending=True) \
.head(n_cheapest)
result_df['total_flights_duration'] = reduce(operator.add, (
result_df[column] for column in result_df.columns
if 'duration' in column
))
return result_df[['total_price', 'total_flights_duration'] + [
column for column in result_df.columns
if 'total_' not in column]]
find_flights(prices_with_city_df, itinerary[0], 100).head(5)
total_price | total_flights_duration | Amsterdam_origin | Amsterdam_destination | Amsterdam_date | Amsterdam_price | Amsterdam_departure_at | Amsterdam_arrival_at | Amsterdam_segments | Amsterdam_duration | Amsterdam_origin_city | Amsterdam_destination_city | Amsterdam_route | Moscow_origin | Moscow_destination | Moscow_date | Moscow_price | Moscow_departure_at | Moscow_arrival_at | Moscow_segments | Moscow_duration | Moscow_origin_city | Moscow_destination_city | Moscow_route | Irkutsk_origin | Irkutsk_destination | Irkutsk_date | Irkutsk_price | Irkutsk_departure_at | Irkutsk_arrival_at | Irkutsk_segments | Irkutsk_duration | Irkutsk_origin_city | Irkutsk_destination_city | Irkutsk_route | Beijing_origin | Beijing_destination | Beijing_date | Beijing_price | Beijing_departure_at | Beijing_arrival_at | Beijing_segments | Beijing_duration | Beijing_origin_city | Beijing_destination_city | Beijing_route | Shanghai_origin | Shanghai_destination | Shanghai_date | Shanghai_price | Shanghai_departure_at | Shanghai_arrival_at | Shanghai_segments | Shanghai_duration | Shanghai_origin_city | Shanghai_destination_city | Shanghai_route | Tokyo_origin | Tokyo_destination | Tokyo_date | Tokyo_price | Tokyo_departure_at | Tokyo_arrival_at | Tokyo_segments | Tokyo_duration | Tokyo_origin_city | Tokyo_destination_city | Tokyo_route | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 1901.41 | 1 days 20:45:00 | AMS | SVO | 2019-07-10 | 203.07 | 2019-07-10 21:15:00+02:00 | 2019-07-11 01:30:00+03:00 | 1 | 03:15:00 | Amsterdam | Moscow | Amsterdam ✈️ Moscow | DME | IKT | 2019-07-13 | 227.40 | 2019-07-13 18:30:00+03:00 | 2019-07-14 05:15:00+08:00 | 1 | 05:45:00 | Moscow | Irkutsk | Moscow ✈️ Irkutsk | IKT | PEK | 2019-07-20 | 209.11 | 2019-07-20 01:50:00+08:00 | 2019-07-20 04:40:00+08:00 | 1 | 02:50:00 | Irkutsk | Beijing | Irkutsk ✈️ Beijing | PEK | SHA | 2019-07-23 | 171.64 | 2019-07-23 11:30:00+08:00 | 2019-07-23 14:00:00+08:00 | 1 | 02:30:00 | Beijing | Shanghai | Beijing ✈️ Shanghai | SHA | NRT | 2019-07-26 | 394.07 | 2019-07-26 14:35:00+08:00 | 2019-07-26 18:15:00+09:00 | 1 | 02:40:00 | Shanghai | Tokyo | Shanghai ✈️ Tokyo | NRT | AMS | 2019-07-29 | 696.12 | 2019-07-29 17:55:00+09:00 | 2019-07-30 14:40:00+02:00 | 2 | 1 days 03:45:00 | Tokyo | Amsterdam | Tokyo ✈️ Amsterdam |
2800 | 1901.41 | 1 days 20:30:00 | AMS | SVO | 2019-07-10 | 203.07 | 2019-07-10 11:50:00+02:00 | 2019-07-10 16:05:00+03:00 | 1 | 03:15:00 | Amsterdam | Moscow | Amsterdam ✈️ Moscow | DME | IKT | 2019-07-13 | 227.40 | 2019-07-13 18:30:00+03:00 | 2019-07-14 05:15:00+08:00 | 1 | 05:45:00 | Moscow | Irkutsk | Moscow ✈️ Irkutsk | IKT | PEK | 2019-07-20 | 209.11 | 2019-07-20 01:50:00+08:00 | 2019-07-20 04:40:00+08:00 | 1 | 02:50:00 | Irkutsk | Beijing | Irkutsk ✈️ Beijing | PEK | SHA | 2019-07-23 | 171.64 | 2019-07-23 21:30:00+08:00 | 2019-07-23 23:45:00+08:00 | 1 | 02:15:00 | Beijing | Shanghai | Beijing ✈️ Shanghai | PVG | NRT | 2019-07-26 | 394.07 | 2019-07-26 14:35:00+08:00 | 2019-07-26 18:15:00+09:00 | 1 | 02:40:00 | Shanghai | Tokyo | Shanghai ✈️ Tokyo | NRT | AMS | 2019-07-29 | 696.12 | 2019-07-29 17:55:00+09:00 | 2019-07-30 14:40:00+02:00 | 2 | 1 days 03:45:00 | Tokyo | Amsterdam | Tokyo ✈️ Amsterdam |
2900 | 1901.41 | 1 days 20:30:00 | AMS | SVO | 2019-07-10 | 203.07 | 2019-07-10 21:15:00+02:00 | 2019-07-11 01:30:00+03:00 | 1 | 03:15:00 | Amsterdam | Moscow | Amsterdam ✈️ Moscow | DME | IKT | 2019-07-13 | 227.40 | 2019-07-13 18:30:00+03:00 | 2019-07-14 05:15:00+08:00 | 1 | 05:45:00 | Moscow | Irkutsk | Moscow ✈️ Irkutsk | IKT | PEK | 2019-07-20 | 209.11 | 2019-07-20 01:50:00+08:00 | 2019-07-20 04:40:00+08:00 | 1 | 02:50:00 | Irkutsk | Beijing | Irkutsk ✈️ Beijing | PEK | SHA | 2019-07-23 | 171.64 | 2019-07-23 10:00:00+08:00 | 2019-07-23 12:15:00+08:00 | 1 | 02:15:00 | Beijing | Shanghai | Beijing ✈️ Shanghai | SHA | NRT | 2019-07-26 | 394.07 | 2019-07-26 14:35:00+08:00 | 2019-07-26 18:15:00+09:00 | 1 | 02:40:00 | Shanghai | Tokyo | Shanghai ✈️ Tokyo | NRT | AMS | 2019-07-29 | 696.12 | 2019-07-29 17:55:00+09:00 | 2019-07-30 14:40:00+02:00 | 2 | 1 days 03:45:00 | Tokyo | Amsterdam | Tokyo ✈️ Amsterdam |
3000 | 1901.41 | 1 days 20:30:00 | AMS | SVO | 2019-07-10 | 203.07 | 2019-07-10 21:15:00+02:00 | 2019-07-11 01:30:00+03:00 | 1 | 03:15:00 | Amsterdam | Moscow | Amsterdam ✈️ Moscow | DME | IKT | 2019-07-13 | 227.40 | 2019-07-13 18:30:00+03:00 | 2019-07-14 05:15:00+08:00 | 1 | 05:45:00 | Moscow | Irkutsk | Moscow ✈️ Irkutsk | IKT | PEK | 2019-07-20 | 209.11 | 2019-07-20 01:50:00+08:00 | 2019-07-20 04:40:00+08:00 | 1 | 02:50:00 | Irkutsk | Beijing | Irkutsk ✈️ Beijing | PEK | SHA | 2019-07-23 | 171.64 | 2019-07-23 10:00:00+08:00 | 2019-07-23 12:15:00+08:00 | 1 | 02:15:00 | Beijing | Shanghai | Beijing ✈️ Shanghai | PVG | NRT | 2019-07-26 | 394.07 | 2019-07-26 14:35:00+08:00 | 2019-07-26 18:15:00+09:00 | 1 | 02:40:00 | Shanghai | Tokyo | Shanghai ✈️ Tokyo | NRT | AMS | 2019-07-29 | 696.12 | 2019-07-29 17:55:00+09:00 | 2019-07-30 14:40:00+02:00 | 2 | 1 days 03:45:00 | Tokyo | Amsterdam | Tokyo ✈️ Amsterdam |
3100 | 1901.41 | 1 days 20:30:00 | AMS | SVO | 2019-07-10 | 203.07 | 2019-07-10 21:15:00+02:00 | 2019-07-11 01:30:00+03:00 | 1 | 03:15:00 | Amsterdam | Moscow | Amsterdam ✈️ Moscow | DME | IKT | 2019-07-13 | 227.40 | 2019-07-13 18:30:00+03:00 | 2019-07-14 05:15:00+08:00 | 1 | 05:45:00 | Moscow | Irkutsk | Moscow ✈️ Irkutsk | IKT | PEK | 2019-07-20 | 209.11 | 2019-07-20 01:50:00+08:00 | 2019-07-20 04:40:00+08:00 | 1 | 02:50:00 | Irkutsk | Beijing | Irkutsk ✈️ Beijing | PEK | SHA | 2019-07-23 | 171.64 | 2019-07-23 17:00:00+08:00 | 2019-07-23 19:15:00+08:00 | 1 | 02:15:00 | Beijing | Shanghai | Beijing ✈️ Shanghai | SHA | NRT | 2019-07-26 | 394.07 | 2019-07-26 14:35:00+08:00 | 2019-07-26 18:15:00+09:00 | 1 | 02:40:00 | Shanghai | Tokyo | Shanghai ✈️ Tokyo | NRT | AMS | 2019-07-29 | 696.12 | 2019-07-29 17:55:00+09:00 | 2019-07-30 14:40:00+02:00 | 2 | 1 days 03:45:00 | Tokyo | Amsterdam | Tokyo ✈️ Amsterdam |
So now it’s easy to get the cheapest flights by calling the function for all possible itineraries:
itinerary_df = reduce(pd.DataFrame.append, (find_flights(prices_with_city_df, itinerary_route, 10)
for itinerary_route in itinerary))
itinerary_df \
.sort_values(['total_price', 'total_flights_duration']) \
.head(10)
total_price | total_flights_duration | Amsterdam_origin | Amsterdam_destination | Amsterdam_date | Amsterdam_price | Amsterdam_departure_at | Amsterdam_arrival_at | Amsterdam_segments | Amsterdam_duration | Amsterdam_origin_city | Amsterdam_destination_city | Amsterdam_route | Moscow_origin | Moscow_destination | Moscow_date | Moscow_price | Moscow_departure_at | Moscow_arrival_at | Moscow_segments | Moscow_duration | Moscow_origin_city | Moscow_destination_city | Moscow_route | Irkutsk_origin | Irkutsk_destination | Irkutsk_date | Irkutsk_price | Irkutsk_departure_at | Irkutsk_arrival_at | Irkutsk_segments | Irkutsk_duration | Irkutsk_origin_city | Irkutsk_destination_city | Irkutsk_route | Beijing_origin | Beijing_destination | Beijing_date | Beijing_price | Beijing_departure_at | Beijing_arrival_at | Beijing_segments | Beijing_duration | Beijing_origin_city | Beijing_destination_city | Beijing_route | Shanghai_origin | Shanghai_destination | Shanghai_date | Shanghai_price | Shanghai_departure_at | Shanghai_arrival_at | Shanghai_segments | Shanghai_duration | Shanghai_origin_city | Shanghai_destination_city | Shanghai_route | Tokyo_origin | Tokyo_destination | Tokyo_date | Tokyo_price | Tokyo_departure_at | Tokyo_arrival_at | Tokyo_segments | Tokyo_duration | Tokyo_origin_city | Tokyo_destination_city | Tokyo_route | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
10 | 1817.04 | 1 days 19:50:00 | AMS | SVO | 2019-07-11 | 203.07 | 2019-07-11 21:15:00+02:00 | 2019-07-12 01:30:00+03:00 | 1 | 03:15:00 | Amsterdam | Moscow | Amsterdam ✈️ Moscow | DME | IKT | 2019-07-15 | 198.03 | 2019-07-15 18:35:00+03:00 | 2019-07-16 05:05:00+08:00 | 1 | 05:30:00 | Moscow | Irkutsk | Moscow ✈️ Irkutsk | IKT | PEK | 2019-07-22 | 154.11 | 2019-07-22 01:50:00+08:00 | 2019-07-22 04:40:00+08:00 | 1 | 02:50:00 | Irkutsk | Beijing | Irkutsk ✈️ Beijing | PEK | SHA | 2019-07-25 | 171.64 | 2019-07-25 21:50:00+08:00 | 2019-07-25 23:55:00+08:00 | 1 | 02:05:00 | Beijing | Shanghai | Beijing ✈️ Shanghai | PVG | NRT | 2019-07-28 | 394.07 | 2019-07-28 17:15:00+08:00 | 2019-07-28 20:40:00+09:00 | 1 | 02:25:00 | Shanghai | Tokyo | Shanghai ✈️ Tokyo | NRT | AMS | 2019-07-31 | 696.12 | 2019-07-31 17:55:00+09:00 | 2019-08-01 14:40:00+02:00 | 2 | 1 days 03:45:00 | Tokyo | Amsterdam | Tokyo ✈️ Amsterdam |
40 | 1817.04 | 1 days 19:50:00 | AMS | SVO | 2019-07-11 | 203.07 | 2019-07-11 21:15:00+02:00 | 2019-07-12 01:30:00+03:00 | 1 | 03:15:00 | Amsterdam | Moscow | Amsterdam ✈️ Moscow | DME | IKT | 2019-07-15 | 198.03 | 2019-07-15 18:35:00+03:00 | 2019-07-16 05:05:00+08:00 | 1 | 05:30:00 | Moscow | Irkutsk | Moscow ✈️ Irkutsk | IKT | PEK | 2019-07-22 | 154.11 | 2019-07-22 01:50:00+08:00 | 2019-07-22 04:40:00+08:00 | 1 | 02:50:00 | Irkutsk | Beijing | Irkutsk ✈️ Beijing | PEK | SHA | 2019-07-25 | 171.64 | 2019-07-25 21:50:00+08:00 | 2019-07-25 23:55:00+08:00 | 1 | 02:05:00 | Beijing | Shanghai | Beijing ✈️ Shanghai | SHA | NRT | 2019-07-28 | 394.07 | 2019-07-28 17:15:00+08:00 | 2019-07-28 20:40:00+09:00 | 1 | 02:25:00 | Shanghai | Tokyo | Shanghai ✈️ Tokyo | NRT | AMS | 2019-07-31 | 696.12 | 2019-07-31 17:55:00+09:00 | 2019-08-01 14:40:00+02:00 | 2 | 1 days 03:45:00 | Tokyo | Amsterdam | Tokyo ✈️ Amsterdam |
0 | 1817.04 | 1 days 20:00:00 | AMS | SVO | 2019-07-10 | 203.07 | 2019-07-10 21:15:00+02:00 | 2019-07-11 01:30:00+03:00 | 1 | 03:15:00 | Amsterdam | Moscow | Amsterdam ✈️ Moscow | DME | IKT | 2019-07-15 | 198.03 | 2019-07-15 18:35:00+03:00 | 2019-07-16 05:05:00+08:00 | 1 | 05:30:00 | Moscow | Irkutsk | Moscow ✈️ Irkutsk | IKT | PEK | 2019-07-22 | 154.11 | 2019-07-22 01:50:00+08:00 | 2019-07-22 04:40:00+08:00 | 1 | 02:50:00 | Irkutsk | Beijing | Irkutsk ✈️ Beijing | PEK | SHA | 2019-07-25 | 171.64 | 2019-07-25 13:00:00+08:00 | 2019-07-25 15:15:00+08:00 | 1 | 02:15:00 | Beijing | Shanghai | Beijing ✈️ Shanghai | PVG | NRT | 2019-07-28 | 394.07 | 2019-07-28 17:15:00+08:00 | 2019-07-28 20:40:00+09:00 | 1 | 02:25:00 | Shanghai | Tokyo | Shanghai ✈️ Tokyo | NRT | AMS | 2019-07-31 | 696.12 | 2019-07-31 17:55:00+09:00 | 2019-08-01 14:40:00+02:00 | 2 | 1 days 03:45:00 | Tokyo | Amsterdam | Tokyo ✈️ Amsterdam |
70 | 1817.04 | 1 days 20:00:00 | AMS | SVO | 2019-07-10 | 203.07 | 2019-07-10 11:50:00+02:00 | 2019-07-10 16:05:00+03:00 | 1 | 03:15:00 | Amsterdam | Moscow | Amsterdam ✈️ Moscow | DME | IKT | 2019-07-15 | 198.03 | 2019-07-15 18:35:00+03:00 | 2019-07-16 05:05:00+08:00 | 1 | 05:30:00 | Moscow | Irkutsk | Moscow ✈️ Irkutsk | IKT | PEK | 2019-07-22 | 154.11 | 2019-07-22 01:50:00+08:00 | 2019-07-22 04:40:00+08:00 | 1 | 02:50:00 | Irkutsk | Beijing | Irkutsk ✈️ Beijing | PEK | SHA | 2019-07-25 | 171.64 | 2019-07-25 18:00:00+08:00 | 2019-07-25 20:15:00+08:00 | 1 | 02:15:00 | Beijing | Shanghai | Beijing ✈️ Shanghai | PVG | NRT | 2019-07-28 | 394.07 | 2019-07-28 17:15:00+08:00 | 2019-07-28 20:40:00+09:00 | 1 | 02:25:00 | Shanghai | Tokyo | Shanghai ✈️ Tokyo | NRT | AMS | 2019-07-31 | 696.12 | 2019-07-31 17:55:00+09:00 | 2019-08-01 14:40:00+02:00 | 2 | 1 days 03:45:00 | Tokyo | Amsterdam | Tokyo ✈️ Amsterdam |
60 | 1817.04 | 1 days 20:00:00 | AMS | SVO | 2019-07-10 | 203.07 | 2019-07-10 11:50:00+02:00 | 2019-07-10 16:05:00+03:00 | 1 | 03:15:00 | Amsterdam | Moscow | Amsterdam ✈️ Moscow | DME | IKT | 2019-07-15 | 198.03 | 2019-07-15 18:35:00+03:00 | 2019-07-16 05:05:00+08:00 | 1 | 05:30:00 | Moscow | Irkutsk | Moscow ✈️ Irkutsk | IKT | PEK | 2019-07-22 | 154.11 | 2019-07-22 01:50:00+08:00 | 2019-07-22 04:40:00+08:00 | 1 | 02:50:00 | Irkutsk | Beijing | Irkutsk ✈️ Beijing | PEK | SHA | 2019-07-25 | 171.64 | 2019-07-25 13:00:00+08:00 | 2019-07-25 15:15:00+08:00 | 1 | 02:15:00 | Beijing | Shanghai | Beijing ✈️ Shanghai | PVG | NRT | 2019-07-28 | 394.07 | 2019-07-28 17:15:00+08:00 | 2019-07-28 20:40:00+09:00 | 1 | 02:25:00 | Shanghai | Tokyo | Shanghai ✈️ Tokyo | NRT | AMS | 2019-07-31 | 696.12 | 2019-07-31 17:55:00+09:00 | 2019-08-01 14:40:00+02:00 | 2 | 1 days 03:45:00 | Tokyo | Amsterdam | Tokyo ✈️ Amsterdam |
0 | 1817.04 | 1 days 20:00:00 | AMS | SVO | 2019-07-11 | 203.07 | 2019-07-11 21:15:00+02:00 | 2019-07-12 01:30:00+03:00 | 1 | 03:15:00 | Amsterdam | Moscow | Amsterdam ✈️ Moscow | DME | IKT | 2019-07-15 | 198.03 | 2019-07-15 18:35:00+03:00 | 2019-07-16 05:05:00+08:00 | 1 | 05:30:00 | Moscow | Irkutsk | Moscow ✈️ Irkutsk | IKT | PEK | 2019-07-22 | 154.11 | 2019-07-22 01:50:00+08:00 | 2019-07-22 04:40:00+08:00 | 1 | 02:50:00 | Irkutsk | Beijing | Irkutsk ✈️ Beijing | PEK | SHA | 2019-07-25 | 171.64 | 2019-07-25 13:00:00+08:00 | 2019-07-25 15:15:00+08:00 | 1 | 02:15:00 | Beijing | Shanghai | Beijing ✈️ Shanghai | PVG | NRT | 2019-07-28 | 394.07 | 2019-07-28 17:15:00+08:00 | 2019-07-28 20:40:00+09:00 | 1 | 02:25:00 | Shanghai | Tokyo | Shanghai ✈️ Tokyo | NRT | AMS | 2019-07-31 | 696.12 | 2019-07-31 17:55:00+09:00 | 2019-08-01 14:40:00+02:00 | 2 | 1 days 03:45:00 | Tokyo | Amsterdam | Tokyo ✈️ Amsterdam |
10 | 1817.04 | 1 days 20:05:00 | AMS | SVO | 2019-07-10 | 203.07 | 2019-07-10 11:50:00+02:00 | 2019-07-10 16:05:00+03:00 | 1 | 03:15:00 | Amsterdam | Moscow | Amsterdam ✈️ Moscow | DME | IKT | 2019-07-15 | 198.03 | 2019-07-15 18:35:00+03:00 | 2019-07-16 05:05:00+08:00 | 1 | 05:30:00 | Moscow | Irkutsk | Moscow ✈️ Irkutsk | IKT | PEK | 2019-07-22 | 154.11 | 2019-07-22 01:50:00+08:00 | 2019-07-22 04:40:00+08:00 | 1 | 02:50:00 | Irkutsk | Beijing | Irkutsk ✈️ Beijing | PEK | SHA | 2019-07-25 | 171.64 | 2019-07-25 12:00:00+08:00 | 2019-07-25 14:20:00+08:00 | 1 | 02:20:00 | Beijing | Shanghai | Beijing ✈️ Shanghai | PVG | NRT | 2019-07-28 | 394.07 | 2019-07-28 17:15:00+08:00 | 2019-07-28 20:40:00+09:00 | 1 | 02:25:00 | Shanghai | Tokyo | Shanghai ✈️ Tokyo | NRT | AMS | 2019-07-31 | 696.12 | 2019-07-31 17:55:00+09:00 | 2019-08-01 14:40:00+02:00 | 2 | 1 days 03:45:00 | Tokyo | Amsterdam | Tokyo ✈️ Amsterdam |
40 | 1817.04 | 1 days 20:05:00 | AMS | SVO | 2019-07-10 | 203.07 | 2019-07-10 11:50:00+02:00 | 2019-07-10 16:05:00+03:00 | 1 | 03:15:00 | Amsterdam | Moscow | Amsterdam ✈️ Moscow | DME | IKT | 2019-07-15 | 198.03 | 2019-07-15 18:35:00+03:00 | 2019-07-16 05:05:00+08:00 | 1 | 05:30:00 | Moscow | Irkutsk | Moscow ✈️ Irkutsk | IKT | PEK | 2019-07-22 | 154.11 | 2019-07-22 01:50:00+08:00 | 2019-07-22 04:40:00+08:00 | 1 | 02:50:00 | Irkutsk | Beijing | Irkutsk ✈️ Beijing | PEK | SHA | 2019-07-25 | 171.64 | 2019-07-25 12:00:00+08:00 | 2019-07-25 14:20:00+08:00 | 1 | 02:20:00 | Beijing | Shanghai | Beijing ✈️ Shanghai | SHA | NRT | 2019-07-28 | 394.07 | 2019-07-28 17:15:00+08:00 | 2019-07-28 20:40:00+09:00 | 1 | 02:25:00 | Shanghai | Tokyo | Shanghai ✈️ Tokyo | NRT | AMS | 2019-07-31 | 696.12 | 2019-07-31 17:55:00+09:00 | 2019-08-01 14:40:00+02:00 | 2 | 1 days 03:45:00 | Tokyo | Amsterdam | Tokyo ✈️ Amsterdam |
70 | 1817.04 | 1 days 20:05:00 | AMS | SVO | 2019-07-11 | 203.07 | 2019-07-11 21:15:00+02:00 | 2019-07-12 01:30:00+03:00 | 1 | 03:15:00 | Amsterdam | Moscow | Amsterdam ✈️ Moscow | DME | IKT | 2019-07-15 | 198.03 | 2019-07-15 18:35:00+03:00 | 2019-07-16 05:05:00+08:00 | 1 | 05:30:00 | Moscow | Irkutsk | Moscow ✈️ Irkutsk | IKT | PEK | 2019-07-22 | 154.11 | 2019-07-22 01:50:00+08:00 | 2019-07-22 04:40:00+08:00 | 1 | 02:50:00 | Irkutsk | Beijing | Irkutsk ✈️ Beijing | PEK | SHA | 2019-07-25 | 171.64 | 2019-07-25 18:30:00+08:00 | 2019-07-25 20:50:00+08:00 | 1 | 02:20:00 | Beijing | Shanghai | Beijing ✈️ Shanghai | PVG | NRT | 2019-07-28 | 394.07 | 2019-07-28 17:15:00+08:00 | 2019-07-28 20:40:00+09:00 | 1 | 02:25:00 | Shanghai | Tokyo | Shanghai ✈️ Tokyo | NRT | AMS | 2019-07-31 | 696.12 | 2019-07-31 17:55:00+09:00 | 2019-08-01 14:40:00+02:00 | 2 | 1 days 03:45:00 | Tokyo | Amsterdam | Tokyo ✈️ Amsterdam |
60 | 1817.04 | 1 days 20:05:00 | AMS | SVO | 2019-07-11 | 203.07 | 2019-07-11 21:15:00+02:00 | 2019-07-12 01:30:00+03:00 | 1 | 03:15:00 | Amsterdam | Moscow | Amsterdam ✈️ Moscow | DME | IKT | 2019-07-15 | 198.03 | 2019-07-15 18:35:00+03:00 | 2019-07-16 05:05:00+08:00 | 1 | 05:30:00 | Moscow | Irkutsk | Moscow ✈️ Irkutsk | IKT | PEK | 2019-07-22 | 154.11 | 2019-07-22 01:50:00+08:00 | 2019-07-22 04:40:00+08:00 | 1 | 02:50:00 | Irkutsk | Beijing | Irkutsk ✈️ Beijing | PEK | SHA | 2019-07-25 | 171.64 | 2019-07-25 11:00:00+08:00 | 2019-07-25 13:20:00+08:00 | 1 | 02:20:00 | Beijing | Shanghai | Beijing ✈️ Shanghai | PVG | NRT | 2019-07-28 | 394.07 | 2019-07-28 17:15:00+08:00 | 2019-07-28 20:40:00+09:00 | 1 | 02:25:00 | Shanghai | Tokyo | Shanghai ✈️ Tokyo | NRT | AMS | 2019-07-31 | 696.12 | 2019-07-31 17:55:00+09:00 | 2019-08-01 14:40:00+02:00 | 2 | 1 days 03:45:00 | Tokyo | Amsterdam | Tokyo ✈️ Amsterdam |
Conclusion
I was able to find the cheapest flights with the minimal duration and the resulting prices were almost the same as on Google Flights.
As a side-note I’ll probably reconsider my trip.
Links: