Agoda lists thousands of properties in a large city: about 6,800 in Paris and 4,300 in Lisbon for a typical search. Its site shows them 45 at a time, and the prices it advertises leave the taxes out. This tutorial scrapes Agoda hotel prices with Python: one city, dates relative to today so the script never expires, the cheapest hotels first, a short pandas analysis by neighbourhood, then a daily price tracker that flags every drop. The data comes from the Agoda Hotels Scraper, an Apify Actor called through the official Python client.
What you need
- An Apify account (the free plan is enough to try this) and its API token, from Apify Console → Settings → API & Integrations, exported as an environment variable:
export APIFY_TOKEN=... - Python 3 and two packages:
pip install "apify-client>=3,<4" pandas
What one row contains
One row per property of the search, with the same columns as the other travel scrapers of the studio:
- prices in
currency:pricePerNightandpriceTotal(the whole stay, one room) of the cheapest offer, taxes and fees included by default,taxesAndFeesfor the stay, andpriceBeforeDiscount, the strike-through price per night of a deal; - the property:
title,propertyType,starRating(half stars possible),ratingon 10,reviewCount,region(the neighbourhood),latitude,longitudeandbadges("Insider deal", "Sponsored"…); - the search:
checkIn,checkOut,nights,guests,searchRank, and aurlthat opens the property on the same stay.
isAvailable is false when a property is sold out for your dates (or Agoda had not computed its price yet); its prices are then empty, and the row is kept.
Step 1: one city, relative dates, cheapest first
This is the input of the public task "Track Agoda hotel prices in Tokyo daily", without the tracking for now:
run_input = {
"location": "Tokyo",
"sortBy": "PRICE_ASC",
"checkInDaysFromNow": 30,
"nights": 3,
"adults": 2,
"currency": "USD",
"maxResults": 60,
}
-
locationis the city as typed in the Agoda search box; the run log shows the city Agoda retained.cityId(thecityparameter of an Agoda search URL) is the precise alternative. -
checkInDaysFromNowandnightsdescribe the stay relative to today: check-in in 30 days, 3 nights. Every run searches new dates, which is what you want for a schedule. For one precise stay, use"checkIn"and"checkOut"instead (YYYY-MM-DD). -
sortByPRICE_ASCreturns the cheapest first. Agoda sorts on its price before taxes, whilepriceTotalincludes them by default, so the order ofpriceTotalcan differ slightly fromsearchRank.REVIEW_SCORE_DESCsorts by guest score instead. -
maxResultsis the number of properties saved and charged. One request returns up to 100, so 60 properties take a few seconds.
To follow several cities with the same stay, list them in locations ("Tokyo", "Osaka", "Kyoto"): each is its own search, maxResults applies to each, and searchLocation tells the rows apart.
Step 2: run it
Save this as agoda_prices.py:
import os
import pandas as pd
from apify_client import ApifyClient
run_input = {
"location": "Tokyo",
"sortBy": "PRICE_ASC",
"checkInDaysFromNow": 30,
"nights": 3,
"adults": 2,
"currency": "USD",
"maxResults": 60,
}
client = ApifyClient(os.environ["APIFY_TOKEN"])
run = client.actor("myth84/agoda-hotels-scraper").call(run_input=run_input)
rows = list(client.dataset(run.default_dataset_id).iterate_items())
for item in rows[:10]:
print(item["searchRank"], item["title"], item["region"], item["pricePerNight"], item["priceTotal"], item["currency"])
df = pd.DataFrame(rows)
df.to_csv("agoda-tokyo.csv", index=False)
print(f'{len(df)} properties from {df["checkIn"].iloc[0]} to {df["checkOut"].iloc[0]} saved to agoda-tokyo.csv')
A run that finds nothing fails with an explicit message in its log (city not recognised, dates in the past), so an empty file never passes for a quiet market.
Step 3: a quick analysis with pandas
Which neighbourhoods are cheapest, and which hotels give the best score for the price? Add this to the script:
priced = df[df["priceTotal"].notna()].copy()
priced["sponsored"] = priced["badges"].map(lambda badges: "Sponsored" in (badges or []))
priced = priced[~priced["sponsored"]]
by_area = priced.groupby("region").agg(
properties=("title", "count"),
medianPerNight=("pricePerNight", "median"),
medianRating=("rating", "median"),
)
print(by_area.sort_values("medianPerNight").round(1).head(10))
well_rated = priced[(priced["rating"] >= 8.5) & (priced["reviewCount"] >= 100)]
print(well_rated.sort_values("pricePerNight")[["title", "region", "starRating", "rating", "reviewCount", "pricePerNight", "url"]].head(10))
The first table gives the median nightly price and score of each neighbourhood among the cheapest properties of the city. The second keeps the properties rated 8.5 or more by at least 100 guests, cheapest first: usually the list people actually want. Paid placements are left out with the "Sponsored" badge.
taxesAndFees is the difference between Agoda's price with and without taxes for the stay. With "priceView": "exclusive", the price columns hold the prices before taxes, the ones Agoda advertises.
Step 4: a daily price tracker
Add "monitorPrices": True and the Actor compares the priceTotal of each property with your previous run of the same search, kept in a key-value store of your Apify account (agoda-price-monitor by default, set with monitorStoreName). Save this as track_agoda.py:
import os
import pandas as pd
from apify_client import ApifyClient
HISTORY = "agoda-tokyo-history.csv"
client = ApifyClient(os.environ["APIFY_TOKEN"])
run = client.actor("myth84/agoda-hotels-scraper").call(run_input={
"location": "Tokyo",
"sortBy": "PRICE_ASC",
"checkInDaysFromNow": 30,
"nights": 3,
"adults": 2,
"currency": "USD",
"maxResults": 60,
"monitorPrices": True,
})
rows = list(client.dataset(run.default_dataset_id).iterate_items())
drops = [item for item in rows if item["changeType"] == "price_changed" and item["priceDelta"] < 0]
for item in sorted(drops, key=lambda item: item["priceDelta"]):
print(f'{item["title"]}: {item["previousPrice"]} -> {item["priceTotal"]} {item["currency"]} ({item["priceDeltaPercent"]} %)')
pd.DataFrame(rows).to_csv(HISTORY, mode="a", header=not os.path.exists(HISTORY), index=False)
The first run is the reference: every property comes back as new. From the second run on, each row is unchanged or price_changed with previousPrice, priceDelta and priceDeltaPercent; the script prints the drops, biggest first, and appends the day to a CSV history. With "onlyChanges": True, only the new properties and the changes are saved and charged: a price drop alert that costs nothing on a quiet day.
Run it every morning with cron (with APIFY_TOKEN set in the cron environment):
0 7 * * * cd /path/to/tracker && python3 track_agoda.py >> tracker.log 2>&1
Without code: open the public task in Apify Console, save it as your own task and add a daily schedule; the dataset of each run downloads as CSV or Excel.
With relative dates, the tracker follows the price of "a 3-night stay in 30 days": each run searches new dates, so a change mixes the hotel's pricing with the calendar (weekday, season). That is the right view of a market. To follow one stay until it happens, give fixed checkIn and checkOut dates.
Good to know
-
Cheapest first is stable.
PRICE_ASCandREVIEW_SCORE_DESCare exact across pages; Agoda's ownRANKINGorder changes between requests, so the compared set drifts. - A property that drops out and comes back is not reported as new: its last price is kept for 90 days.
- Same currency only. A price is compared only with a previous price in the same currency.
- Search results show a subset. Amenities, room details and the full photo gallery are not in Agoda's search results; the rows hold up to ten photos and the "easy cancellation" flag.
- No personal data: property facts, the average score and the number of reviews, nothing about guests or reviewers.
Pricing is pay-per-event: $2.00 per 1,000 properties on Apify's Free plan, down to $1.29 on the higher plans (this run of 60 properties costs about $0.12). Use the data for analysis, respect the platform's terms and local law, and do not republish listings.
Disclosure: I build these Actors and may earn a commission if you sign up to Apify through these links.
Top comments (0)