kestrel actors › Hotel & flight prices
Agoda Prices Scraper — Hotel Rates & Availability by Date
Agoda prices scraper for real stays: give a destination or a list of hotels, a check-in date, nights and guests, and get every property's cheapest rate as clean rows — agoda hotel prices per night and for the whole stay, with and without taxes, the cancellation term, rooms left, stars, guest score and coordinates. It reads the same search answer agoda.com's own results page reads, so the numbers match what a traveller sees — a hotel rate scraper that returns Agoda's structured data rather than a rendered page. Use it as an agoda scraper for market research, as a hotel price comparison feed next to the Google Hotels and Booking.com actors below, or as the agoda api you were looking for: no key, no browser, pay per priced hotel.
Run Agoda Prices Scraper on Apify →
Agoda prices scraper pricing: what one row costs
| Event | What it is | Price | Charged for |
|---|---|---|---|
hotel | Priced hotel row | $0.004 | One property with its cheapest rate for your dates: nightly and total price with and without taxes, cancellation terms, available rooms, stars, guest score and location. Sold-out properties and hotels a filter removed are free. |
calendar_day | Calendar day row | $0.002 | One check-in date of a property's price calendar with its per-night price and Agoda's low/normal/high trend flag. Dates without a price are free. |
Pay per event, only for what is delivered:
| Event | Price | What it is |
|---|---|---|
hotel | $0.004 | One priced property for the stay |
calendar_day | $0.002 | One priced check-in date of a property's calendar |
Sold-out properties, filtered hotels and status rows are free. Every property is billed once per run however many inputs reach it. Examples at list price: 100 hotels in a city, $0.40; a comp set of 20 named hotels every morning, $0.08 a day; the same 20 with a 30-day calendar, roughly $0.08 + 20 × (priced dates × $0.002). Runs use Apify Proxy datacenter sessions, which are included in your Apify plan; the endpoint tolerates about one request per second per IP, and a 45-property page is under 100 KB.
Agoda prices scraper input reference
Every field of Agoda Prices, straight from the actor's input schema.
| Field | Type | Default | What it does |
|---|---|---|---|
locationQueries | array | [] | Cities or areas as you would type them into Agoda ("Lisbon", "Bangkok", "Bali"), an Agoda city id ("16364"), or a search URL from agoda.com. Each becomes one search returning up to maxHotelsPerQuery properties in sortBy order. |
startUrls | array | [] | Agoda property pages (agoda.com/<slug>/hotel/<city>.html) or search-result pages (agoda.com/search?city=…). A property URL is priced on its own; a search URL is treated like a destination. |
hotelIds | array | [] | Agoda's numeric property ids, e.g. "63820". The fastest way to price a known list of properties: one small request each, no page read. |
hotelNames | array | [] | Property names, e.g. "Tivoli Avenida Liberdade Lisbon". Resolved through Agoda's own autocomplete; the best match is priced. |
checkIn | string | "30 days" | YYYY-MM-DD, or relative so schedules never go stale: "30 days", "6 weeks", "tomorrow". Past dates are refused. |
nights | integer | 1 | Length of stay, 1-30. Totals are for the whole stay; nightly prices are per room per night. Range 1-30. |
adults | integer | 2 | Adults per booking, 1-9. Agoda prices the cheapest room that fits everyone. Range 1-9. |
children | integer | 0 | Children per booking, 0-9. Give their ages in childAges for exact child rates. Range 0-9. |
childAges | array | [] | One age (0-17) per child, in order. Leave empty when there are no children. |
rooms | integer | 1 | Rooms per booking, 1-9. Prices are per room; totals cover all rooms for the stay. Range 1-9. |
currency | string | "USD" | ISO code for every price, e.g. USD, EUR, GBP, THB, JPY. Agoda converts at its own rate. |
sortBy | string (enum) | "best_match" | Order Agoda returns a destination's properties in, which is also the order the rank column follows and the order maxHotelsPerQuery cuts at. Property inputs ignore this. One of: best_match, price_low, guest_rating, secret_deals. |
maxHotelsPerQuery | integer | 45 | 0 = every property Agoda lists for the stay (a big city runs to a few thousand). N = the first N in sortBy order — the main cost control. Agoda pages 45 at a time. Range 0-2000. |
maxPrice | number | 0 | 0 = keep every priced hotel. N = keep only hotels whose cheapest nightly rate (before taxes, in currency) is at or under N. Filtered hotels are never billed — a daily "anything under 80" alert costs nothing on days without a match. Minimum 0. |
minStars | number | 0 | 0 = any. 1-5 = keep only properties with at least this many stars. Unrated properties are filtered when this is set. Range 0-5. |
minReviewScore | number | 0 | 0 = any. 1-10 = keep only properties whose guest review score is at least this. Unreviewed properties are filtered when this is set. Range 0-10. |
freeCancellationOnly | boolean | false | Keep only hotels whose cheapest rate is free to cancel. Filtered hotels are never billed. |
includeSoldOut | boolean | false | Also write a row (free) for properties Agoda lists but cannot price for the stay, with sold_out: true and every price empty. Off by default so the dataset stays a price list. |
calendarDays | integer | 0 | 0 = off. N = for each named property also read Agoda's price-trend calendar for N check-in dates from checkIn (up to 60): one calendar row per date that has a price, with Agoda's low/normal/high flag and the cheapest date ranked first. Dates Agoda has no cached price for are skipped and free. Range 0-60. |
locale | string | "en-us" | Agoda locale for names and areas, e.g. en-us, de-de, ja-jp, th-th. Prices are not affected. |
sessions | integer | 4 | How many proxy sessions (egress IPs) run in parallel. More is faster; each is paced separately. Range 1-16. |
perIp | number | 1 | Pace for each session. Agoda tolerates about one request per second per IP; faster causes retries, not speed. Range 0.1-3. |
proxyConfiguration | object | {"useApifyProxy": true} | Apify Proxy is required: Agoda throttles a single busy IP within a few pages. Datacenter proxies are enough for this endpoint. |
Example input
{
"locationQueries": ["Lisbon", "Bangkok"],
"checkIn": "30 days",
"nights": 2,
"adults": 2,
"currency": "USD",
"sortBy": "price_low",
"maxHotelsPerQuery": 100
}
{
"hotelIds": ["6988894", "63820"],
"hotelNames": ["Tivoli Avenida Liberdade Lisbon"],
"checkIn": "2026-10-10",
"nights": 3,
"adults": 2,
"rooms": 1,
"currency": "EUR",
"calendarDays": 30
}
Agoda prices scraper output fields
Three row types share one dataset: hotel (one property with its cheapest rate for the stay, charged when priced), calendar (one check-in date of a property's price calendar, charged when priced) and status (per destination or property, free).
Dataset views in the Apify Console: Hotels, Cheapest first, Price calendar, Status.
| Field | Type | What it holds |
|---|---|---|
type | string | Row type: hotel, calendar or status. |
mode | string | destination (from a location search) or property (a named hotel). |
query | string | The input this row came from: the destination, URL, id or name. |
target | string | Status rows: the input the row reports on. |
locale | string | Agoda locale the request used. |
hotel_id | string | Agoda's numeric property id. |
hotel_name | string | Property name in the requested locale. |
url | string | The Agoda property page. |
property_type | string | Hotel, Apartment, Hostel, Resort… |
stars | number | Star rating, 1-5; null when Agoda shows none. |
review_score | number | Combined guest score across every review source Agoda aggregates, 0-10. |
review_count | integer | Reviews behind review_score. |
agoda_score | number | Guest score from Agoda's own reviews only, 0-10. |
agoda_review_count | integer | Agoda's own review count. |
city | string | City name. |
city_id | integer | Agoda city id (reusable in locationQueries). |
area | string | Neighbourhood or area. |
country | string | Country name. |
country_code | string | ISO 3166-1 alpha-2 country code. |
latitude | number | Property latitude. |
longitude | number | Property longitude. |
check_in | string | Check-in date the price is quoted for (YYYY-MM-DD). |
check_out | string | Check-out date. |
nights | integer | Length of stay. |
adults | integer | Adults the price is quoted for. |
children | integer | Children the price is quoted for. |
rooms | integer | Rooms the price is quoted for. |
currency | string | ISO currency of every price in the row. |
price_nightly | number | Cheapest rate per room per night before taxes and fees — Agoda's default display price. |
price_nightly_with_taxes | number | The same nightly rate with taxes and fees included. |
price_total | number | Total for the whole booking (all rooms, all nights) before taxes and fees. Calendar rows: nightly price × nights. |
price_total_with_taxes | number | Total for the whole booking with taxes and fees included. |
crossed_out_price | number | The struck-through nightly price when Agoda shows a discount; null otherwise. |
available_rooms | integer | Rooms left at the cheapest rate, as Agoda reports it. |
cancellation | string | Agoda's cancellation term for the cheapest rate: FreeCancellation, NonRefundable or SpecialConditions. |
free_cancellation | boolean | True when the cheapest rate is free to cancel. |
free_cancellation_until | string | Deadline for free cancellation when Agoda states one. |
breakfast_included | boolean | True when Agoda flags breakfast in the cheapest rate's benefits. |
benefit_ids | array | Agoda's benefit ids attached to the cheapest rate (breakfast, free WiFi, parking…), raw. |
supplier_id | integer | Agoda's id for the supplier of the cheapest rate. |
payment_model | string | How the cheapest rate is paid, e.g. MerchantCommission. |
sold_out | boolean | True when Agoda lists the property but has no rate for the stay (row is free). |
rank | integer | Position in the destination's results under sort (1 = first). Null for named properties. |
sort | string | The sort the destination search used. |
price_source | string | search — the price came from Agoda's search results for the stay. |
trend | string | Calendar rows: Agoda's Low, Normal or High flag for the date. |
rank_in_window | integer | Calendar rows: 1 = the cheapest check-in date in the window. |
cheapest_in_window | boolean | Calendar rows: true on the cheapest date of the window. |
status | string | Status rows: ok, no_results, sold_out, not_found or error. |
hotels | integer | Status rows: priced hotel rows delivered for this input. |
sold_out_hotels | integer | Status rows: properties listed but unpriced for the stay (free, written only with includeSoldOut). |
filtered | integer | Status rows: priced hotels a filter removed (never billed). |
pages | integer | Status rows: result pages read for a destination. |
calendar_days | integer | Status rows: calendar rows delivered for a property. |
duplicates | integer | Status rows: properties already delivered by an earlier input in this run. |
error | string | Status rows: why an input failed. |
fetched_at | string | UTC time the row was collected. |
Example row from a real run
Taken from the actor's README — this is the shape your dataset holds.
{
"type": "hotel",
"mode": "destination",
"query": "Lisbon",
"locale": "en-us",
"hotel_id": "13505436",
"hotel_name": "Help Yourself Hostels - Restelo",
"url": "https://www.agoda.com/help-yourself-hostels-restelo/hotel/all/lisbon-pt.html",
"property_type": "Hotel",
"stars": 2.0,
"review_score": 6.8,
"review_count": 120,
"agoda_score": 6.5,
"agoda_review_count": 31,
"city": "Lisbon",
"city_id": 16364,
"area": "Belem",
"country": "Portugal",
"country_code": "PT",
"latitude": 38.6997,
"longitude": -9.2079,
"check_in": "2026-09-28",
"check_out": "2026-09-29",
"nights": 1,
"adults": 2,
"children": 0,
"rooms": 1,
"currency": "USD",
"price_nightly": 44.71,
"price_nightly_with_taxes": 60.67,
"price_total": 44.71,
"price_total_with_taxes": 60.67,
"crossed_out_price": null,
"available_rooms": 1,
"cancellation": "FreeCancellation",
"free_cancellation": true,
"free_cancellation_until": null,
"breakfast_included": false,
"benefit_ids": [6, 95, 230, 231],
"supplier_id": 332,
"payment_model": "MerchantCommission",
"sold_out": false,
"rank": 46,
"sort": "price_low",
"price_source": "search",
"fetched_at": "2026-08-29T11:20:04+00:00"
}
Agoda prices scraper in Python, JavaScript and curl
Python
from apify_client import ApifyClient
client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("kestrel/agoda-prices-scraper").call(run_input={
"locationQueries": ["Lisbon"],
"checkIn": "30 days", "nights": 2, "adults": 2, "currency": "EUR",
"sortBy": "price_low", "maxHotelsPerQuery": 50, "freeCancellationOnly": True,
})
rows = [r for r in client.dataset(run["defaultDatasetId"]).iterate_items() if r["type"] == "hotel"]
for r in rows[:5]:
print(r["rank"], r["hotel_name"], r["price_nightly"], r["currency"], r["cancellation"])
JavaScript (Node)
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });
const run = await client.actor('kestrel/agoda-prices-scraper').call({
hotelIds: ['6988894', '63820'], checkIn: '2026-10-10', nights: 3, adults: 2, currency: 'USD', calendarDays: 30,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
const cheapest = items.filter(r => r.type === 'calendar' && r.cheapest_in_window);
console.log(cheapest.map(r => [r.hotel_name, r.check_in, r.price_nightly]));
curl
curl -X POST "https://api.apify.com/v2/acts/kestrel~agoda-prices-scraper/run-sync-get-dataset-items?token=<YOUR_APIFY_TOKEN>&timeout=300" \
-H "Content-Type: application/json" \
-d '{"locationQueries": ["Bangkok"], "checkIn": "45 days", "nights": 1, "adults": 2, "currency": "THB", "maxHotelsPerQuery": 45, "minReviewScore": 8}'
The same three calls are runnable files in the examples repository: examples/agoda-prices-scraper.py|.js|.sh.
Limits and honest notes
- Sold-out is not a price. A property Agoda lists but cannot price for your stay comes back with
sold_out: trueand every price empty. Those rows are free and hidden unlessincludeSoldOutasks for them. - A throttle is never a "no results". A refused session is rotated and retried; if every attempt is refused the input is reported as an
error, not as an empty destination. An unknown destination id fails in one call with Agoda's own message. - Sort only applies to destination searches — named properties are priced on their own, and Agoda only sorts price ascending.
- The price calendar is for named properties only,
calendarDaysreading 0-60 check-in dates. - Past check-in dates are refused.
checkIntakesYYYY-MM-DDor a relative value ("30 days", "6 weeks", "tomorrow") so a schedule never goes stale.
FAQ
Are these the prices a guest sees on Agoda?
Yes — for the same stay, currency and occupancy. price_nightly is Agoda's default display price (per room per night, before taxes and fees); price_total_with_taxes is what the booking would cost. Agoda personalises some discounts for logged-in members and app users; this actor prices as a logged-out visitor, the same view Google and metasearch engines get.
How many hotels can one run collect?
As many as Agoda lists for the stay: a large city returns one to three thousand properties at 45 per page. maxHotelsPerQuery caps that per destination (its default is 45, one page) and status.pages tells you how many pages were read. Runs are paced at about one request per second per session; four sessions read roughly 180 properties a second of wall time.
Can I get room-level rates?
The row carries the cheapest rate Agoda would show on the property card — one room type, one supplier — with its cancellation term, benefits and rooms left. Agoda room prices by room type (the full grid) are not part of this actor; ask on the Issues tab if you need them and say how many properties you price a day.
Does it work for apartments, hostels and resorts?
Yes: property_type tells them apart, and stars are empty for unrated properties. Filters like minStars drop unrated ones by design; use minReviewScore to rank hostels and apartments instead.
Why is a property sold out in one run and priced in the next?
Availability is per stay. Change the dates, the number of nights or the occupancy and Agoda's answer changes with it. The Tivoli Avenida Liberdade, for example, has no rate for one specific night but a price calendar full of other dates — that is exactly what calendarDays is for.
Which currencies and locales are supported?
Any ISO currency Agoda quotes in (USD, EUR, GBP, THB, JPY, AUD, SGD…), converted at Agoda's own rate. locale changes names and area labels (de-de, ja-jp, th-th…), not the prices.
What happens when Agoda throttles the run?
The session's IP is rotated and the request retried with back-off; the input is only reported as an error when every attempt was refused. Nothing is billed for an errored input, and the run only fails outright when every input failed.
Is scraping Agoda prices legal?
The actor reads public, logged-out price listings — the same data Agoda publishes to search engines and metasearch partners — and stores no personal data. Using that data for price comparison, market research and revenue management is a normal business use; check Agoda's terms and your local law before republishing prices commercially, respect the site with sensible pacing, and do not use the data to interfere with Agoda's service.
Related scrapers
Run Agoda Prices Scraper
The actor lives on the Apify Store, where the pricing tab carries the live per-row price and the input form has every field above: apify.com/kestrel/agoda-prices-scraper