# Agoda Hotels Scraper & Price Tracker (`myth84/agoda-hotels-scraper`) Actor

Agoda hotels scraper and Agoda price tracker: search results for any city and dates — nightly and total price with and without taxes, guest rating, reviews, stars, photos. Schedule it for hotel price tracking: price drops and rises flagged between runs. One flat row per property, no personal data.

- **URL**: https://apify.com/myth84/agoda-hotels-scraper.md
- **Developed by:** [Flatrow Data Studio](https://apify.com/myth84) (community)
- **Categories:** Travel, Automation, AI
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.29 / 1,000 hotels

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

### Agoda Hotels Scraper & Price Tracker – hotels, prices and ratings, no personal data

Agoda hotels scraper and Agoda price tracker: get Agoda hotel prices per night and for the whole stay, with and without taxes, guest ratings, review counts and stars for any city and dates, as a clean table or through an API — no login. Use it as an Agoda scraper for rate shopping and hotel price comparison with other travel platforms, or as an Agoda hotel price monitor: schedule the same search for hotel price tracking, and with price monitoring on get price drop alerts (and rises) since your previous run.

**No login, no cookies, no personal data:** nothing to sign in to, no cookies to paste, nothing about guests, reviewers or hosts in the output.

- **What you get:** one flat row per Agoda property for any city and dates: price per night and for the stay with and without taxes and fees, strike-through price, guest rating on 10, review count, stars, type, neighbourhood, coordinates, photos and link; a price-monitoring mode that returns only what changed since your previous run. No personal data.
- **For whom:** hoteliers and revenue managers (rate shopping), travel data and BI teams, deal finders and price-alert tools, AI agents through MCP.
- **How much:** from $2.00 per 1,000 properties on the Free plan, lower on paid plans; Apify's free monthly credit of $5 covers about 2,499 properties. A fixed price per property saved and nothing else (compute and proxies included, see the Pricing tab); unchanged prices are never charged with `onlyChanges`; `maxResults` caps every run.

**Sample output**: one real row from a Lisbon search, key columns only (the full row is under *Output example*):

```json
{
  "platformListingId": "39627106",
  "title": "Costa by the Beach",
  "city": "Lisbon",
  "checkIn": "2026-11-20",
  "checkOut": "2026-11-23",
  "currency": "USD",
  "pricePerNight": 28.02,
  "priceTotal": 84.06,
  "taxesAndFees": 17.43,
  "priceTotalAllIn": 84.06,
  "rating": 6.4,
  "reviewCount": 10
}
```

Scrape Agoda search results for any city and dates and get **one flat row per property**: hotels, hostels, guest houses, serviced apartments and rentals listed by Agoda, with the price per night and the total price of the stay **with and without taxes and fees** (Agoda shows both; you choose which fills the price columns, the difference is always given), the strike-through price of deals, the guest rating on 10 and the number of reviews, the official stars, the property type, the neighbourhood, the coordinates, up to ten photos and the link to the property page for those dates. Search **up to 50 cities in one run** with `locations`. This Agoda scraper reads the same search API as the Agoda website, without logging in, 45 or 100 properties per request, removes duplicates and stops exactly at the number of properties you ask for.

**No personal data, by design.** Nothing about guests, reviewers or hosts as persons is collected: no names, no review texts, no profile photos, no ids of persons. The output only contains property-level facts (business names), the average rating and the number of reviews. The dataset is safe to store, share and feed to AI agents.

### What can you do with Agoda data?

- **Track hotel prices** for a city and dates, taxes included or not: rate shopping for hoteliers and revenue managers, deal finders, seasonal price studies, price alerts with a scheduled run.
- **Build accommodation datasets** for a city with coordinates ready for mapping, stars, property types, neighbourhoods, ratings and review counts for segmentation.
- **Compare platforms**: the same stay on Agoda, Booking.com, Expedia and Airbnb with the same columns (this travel studio shares one output schema), so nightly and total prices line up in one spreadsheet, and `priceTotalAllIn` compares them taxes included.
- **Feed AI agents, spreadsheets and BI tools** with a stable, documented schema: every row has every column, prices are numbers, links open the property with your dates.

### What data does Agoda Scraper extract?

Every row has every column: a value Agoda does not show in search results is `null` (or `[]` for lists), so CSV and Excel exports keep the same columns from run to run.

| Group | Columns |
|---|---|
| Identity | `schemaVersion`, `platform`, `platformListingId`, `url`, `title`, `propertyType`, `roomType` |
| Location | `city`, `region`, `country`, `countryCode`, `latitude`, `longitude` |
| Stay | `checkIn`, `checkOut`, `nights`, `guests`, `isAvailable` |
| Prices | `currency`, `pricePerNight`, `priceTotal`, `priceBeforeDiscount`, `taxesAndFees`, `taxesIncluded`, `priceTotalAllIn`, `cleaningFee`, `serviceFee` |
| Quality | `rating`, `ratingMax`, `reviewCount`, `starRating`, `badges`, `isSuperhost`, `isNew`, `listedBy`, `cancellationPolicy`, `instantBook` |
| Size | `bedrooms`, `beds`, `bathrooms`, `maxGuests`, `amenities` |
| Photos | `imageUrls`, `imageCount` |
| Search context | `searchLocation`, `searchRank`, `searchPage`, `viewCountryCode`, `scrapedAt` |
| Price monitoring (`monitorPrices` runs only, otherwise `null`) | `changeType`, `previousPrice`, `priceDelta`, `priceDeltaPercent` |

`pricePerNight` and `priceTotal` are the prices of the cheapest offer for one room, taxes and fees included by default (`priceView` `inclusive`) or before taxes and fees (`exclusive`, the price Agoda advertises and sorts by); `taxesAndFees` is the difference for the whole stay in both cases. `priceBeforeDiscount` is the strike-through price per night of a deal. `rating` is on a 10-point scale (`ratingMax` is `10`), `starRating` the official stars (half stars possible, `null` when unrated). `propertyType` is Agoda's accommodation type (`Hotel`, `Hostel`, `Guest house`, `Apartment`, `Serviced apartment`…), `roomType` `hotel_room` for hotel-type properties and `entire_place` for Agoda's non-hotel rentals, which also fill `bedrooms`, `beds` and `bathrooms`. `city`, `region`, `country`, `countryCode`, `latitude` and `longitude` come from the property itself, in the requested language. Columns that Agoda never shows on search results stay empty: `cleaningFee`, `serviceFee`, `maxGuests`, `instantBook`, `isNew`, `isSuperhost`, `listedBy` (always `null`) and `amenities` (always `[]`).

**Which price to compare.** On Agoda, `priceTotal` follows `priceView`: taxes and fees **included** with `inclusive`, the default (`taxesIncluded` is `true`), **before** them with `exclusive` (`taxesIncluded` is `false`). `priceTotalAllIn` is the total of the stay with Agoda's taxes and fees in both views, `priceTotal` or `priceTotal` + `taxesAndFees` ($84.06 in the sample above, whatever the view), and `null` when a property has no price: it is the column to compare stays across platforms, filled with the same rule by every travel scraper of the studio, as each platform shows the taxes. Agoda's taxes and fees can leave out a city tax collected at the property (seen on some properties in Paris and Lisbon, where the difference is the VAT alone), so a few Agoda totals may sit slightly below those of other platforms.

### How to scrape Agoda hotels

1. **Enter a city** as you would type it on Agoda ("Lisbon", "Paris", "Bangkok"). Agoda's own suggestion service resolves it (the log shows the city and country retained). Put **a list of cities** in `locations` to compare several destinations in one run. Power users can give the Agoda `cityId` or paste search URLs copied from agoda.com instead.
2. **Choose the stay**: fixed dates (`checkIn` / `checkOut`) for a precise quote, or relative dates (check-in in *N* days for *M* nights, the default) for schedules and recurring price checks that must never expire. Set the adults and, for a family search, the ages of the children: prices and the properties offered depend on them.
3. **Pick the currency and the language** (`USD` and `EUR`, `en-us` and `fr-fr` are validated), the sort order (cheapest first by default), whether prices include taxes, the maximum number of properties and, if needed, the page size.
4. **Run** and open the **Output** tab: the *Overview*, *Prices* and *Location* views show the main columns; export everything as JSON, CSV, Excel or through the API. To track prices, tick `monitorPrices` and schedule the run (see *Price monitoring* below).

### Input

Field names and defaults are those of the input form. Give a list of `locations` (one or more cities), a `cityId` or search URLs; `location` (a single city) is still accepted. `checkIn` and `checkOut` go together, otherwise `checkInDaysFromNow` and `nights` apply. `searchUrls` takes precedence over `cityId`, which takes precedence over `location`; the cities of `locations` are always searched.

The search of our validation run, written with `locations`: the 60 cheapest properties in Lisbon for a 3-night stay starting in 30 days, 2 adults, USD, taxes and fees included (every other field keeps its default):

```json
{
  "locations": ["Lisbon"],
  "checkInDaysFromNow": 30,
  "nights": 3,
  "adults": 2,
  "currency": "USD",
  "locale": "en-us",
  "sortBy": "PRICE_ASC",
  "maxResults": 60
}
```

Every field, with its type and default:

| Field | Type | Default | Description |
|---|---|---|---|
| `locations` | string\[] | `[]` | One or more cities as typed on Agoda, one per line, resolved by Agoda's suggestion service and searched one after the other with the same dates, occupancy, currency and sort (up to 50, with `location` or `cityId`), together with the search URLs. When set, `maxResults` applies to each destination and each search URL. Prefilled with `["Lisbon"]` in the form (clear it to search a `cityId` or search URLs only); with no `locations`, `location`, `cityId` or `searchUrls` at all, the Actor searches Lisbon. |
| `location` | string | — | Single city (kept for compatibility; prefer `locations`), as typed on Agoda, resolved by Agoda's suggestion service and searched before the cities of `locations`. |
| `cityId` | integer | — | Advanced: Agoda's numeric city id (the `city` parameter of a search URL; Lisbon = 16364, Paris = 15470). |
| `searchUrls` | array | — | Advanced: search URLs copied from agoda.com (http(s), with a `city` parameter), one search each; their city, dates and occupancy override the fields below when present. |
| `checkIn` | string | — | Check-in date, YYYY-MM-DD. Leave both dates empty to use relative dates. |
| `checkOut` | string | — | Check-out date, YYYY-MM-DD, after check-in. Required when `checkIn` is set. |
| `checkInDaysFromNow` | integer | `30` | Without fixed dates: check-in this many days from today (UTC). |
| `nights` | integer | `3` | Without fixed dates: length of the stay (1 to 28). |
| `adults` | integer | `2` | Number of adults in one room (1 to 9). |
| `childAges` | array | `[]` | Ages of the children in the room (0 to 17, up to 6), e.g. `[8]`; a family search when not empty. |
| `maxResults` | integer | `100` | Stop after this many unique properties (2,000 at most): for the whole run, or for each destination and each search URL when `locations` is set; with `monitorPrices`, the number of properties compared. You are never charged for more. Prefilled with `20` in the form (a quick first run). |
| `sortBy` | string | `"PRICE_ASC"` | `PRICE_ASC` (cheapest first, by the price before taxes) and `REVIEW_SCORE_DESC` are validated; `RANKING` is Agoda's own order (variable from one request to the next, duplicates removed); `STAR_RATING_DESC` and `DISTANCE_ASC` are passed to the API but not yet verified. |
| `priceView` | string | `"inclusive"` | `inclusive`: price columns include taxes and fees; `exclusive`: before taxes and fees. `taxesAndFees` is filled either way. |
| `resultsPerPage` | string | `"100"` | Properties per API request: `"100"` (the server maximum) or `"45"` (Agoda's page size), both validated. |
| `currency` | string | `"USD"` | ISO 4217 code of every price. `USD` and `EUR` validated; other codes are passed to the API, not yet verified. |
| `locale` | string | `"en-us"` | Agoda locale of the city, neighbourhood and country names and of the badges: `en-us` and `fr-fr` validated, other locales of the site (`en-gb`, `de-de`, `es-es`…) not yet verified (pick one from the list or type any locale). |
| `monitorPrices` | boolean | `false` | Price monitoring: compare every property with your previous run of the same search and fill `changeType`, `previousPrice`, `priceDelta` and `priceDeltaPercent` (see *Price monitoring*). `maxResults` is then the number of properties compared. |
| `monitorStoreName` | string | `"agoda-price-monitor"` | Key-value store of your account that keeps the previous prices, one record per search. Letters, digits and dashes. |
| `onlyChanges` | boolean | `false` | With `monitorPrices`: save and charge only the new properties and the price changes; a run without any change succeeds with an empty dataset. |
| `proxyConfiguration` | object | `{"useApifyProxy":true}` | Apify datacenter proxies by default, included in the price; keep them, or bring your own proxy URLs. A proxy country is recorded in `viewCountryCode`. |
| `maxRequests` | integer | `100` | Safety cap on API requests per run, retries and the place lookup included. |
| `delayMs` | integer | `1000` | Pause between two page requests of the same search, in milliseconds. |
| `debugStoreHtml` | boolean | `false` | Debugging only: store every API response (JSON, refused attempts included) in the key-value store. |

**Several destinations in one run.** List them in `locations` (with or without `location`): each city is searched on its own with the same dates, occupancy, currency and sort, and `maxResults` applies to each of them. This input returns up to 50 properties for each of the three cities, 150 at most:

```json
{
  "locations": ["Paris", "Rome", "Barcelona"],
  "checkInDaysFromNow": 30,
  "nights": 3,
  "adults": 2,
  "currency": "EUR",
  "maxResults": 50
}
```

The `searchLocation` column tells which city found each property (the city and country Agoda resolved, e.g. "Paris, France"). A property found by two destinations (a city and one of its areas) is saved and charged once, under the first destination of the list. Without `locations`, `maxResults` keeps capping the whole run.

### Price monitoring

Use the Actor as an **Agoda price tracker**: tick `monitorPrices`, then **schedule the run** (daily is typical). The scraper keeps the last price of every property it compared in a key-value store of your account named `monitorStoreName` (`agoda-price-monitor` by default), with one record per search (same city, city id or search URL, stay, adults and children's ages, sort, currency, language and `priceView`). The first run of a search is the reference: every property comes back with `changeType` = `"new"` (and `RUN_STATS.monitorBaseline` is true). From the second run on, every property is compared with the recorded one: `price_changed` rows carry `previousPrice`, `priceDelta` and `priceDeltaPercent`, the others are `unchanged`. With `onlyChanges` the dataset holds **only the new properties and the price changes** (hotel price drop alerts and rises), and **only those rows are charged**; a run where nothing changed succeeds with an empty dataset. In both cases `maxResults` is the number of properties compared by the run.

**The compared price is `priceTotal`**, the total of the stay for one room, with taxes and fees (`priceView` `inclusive`, the default) or before them (`exclusive`); for a property that comes with only a nightly price, `pricePerNight` is compared instead. A total is never compared with a nightly price: when a property switches from one to the other, it is reported `unchanged` and the new price becomes the reference.

A scheduled input that watches the 100 cheapest hotels of Bangkok for a 2-night stay two weeks ahead:

```json
{
  "location": "Bangkok",
  "checkInDaysFromNow": 14,
  "nights": 2,
  "adults": 2,
  "currency": "USD",
  "sortBy": "PRICE_ASC",
  "maxResults": 100,
  "monitorPrices": true,
  "onlyChanges": true
}
```

A `price_changed` row, as the Actor produces it from the row of the output example when the previous run had recorded a total of $95 (the other columns are the same as below):

```json
{
  "platformListingId": "39627106",
  "title": "Costa by the Beach",
  "checkIn": "2026-11-20",
  "checkOut": "2026-11-23",
  "currency": "USD",
  "priceTotal": 84.06,
  "changeType": "price_changed",
  "previousPrice": 95,
  "priceDelta": -10.94,
  "priceDeltaPercent": -11.5
}
```

What to know before relying on it:

- **Fixed dates compare one stay; relative dates compare a lead time.** With `checkIn` / `checkOut` (or a search URL with its own dates), the record follows the price of that stay until it passes. With relative dates (the default), the record follows the price of the stay *N days ahead for M nights*: each run searches new dates, so a change mixes the hotel's pricing and the calendar (weekday, season). Both are useful; just know which one you track. Changing `checkInDaysFromNow` or `nights` starts a new reference.
- **The record is written only after a clean run.** A city that is not recognised, an abandoned page, a charge limit reached in the middle of a run or an interrupted run never overwrites the reference, so a bad day cannot erase your prices; the log says when a record was kept unchanged. A store that cannot be read stops the run before its first request.
- **Properties without a price keep their last one.** A property sold out for the dates, or whose price Agoda had not computed yet (`isAvailable` `false`), is reported `unchanged` and keeps its last recorded price: the next priced run is compared with it.
- **Same currency only.** A price is compared only with a previous price of the same kind in the same currency; otherwise the property is reported `unchanged` and the new price becomes the reference.
- **Rankings move.** Agoda's `RANKING` order changes from one request to the next, so the set of compared properties (the first `maxResults` of the run) drifts from run to run; a property that drops out of the results and comes back later is not reported as new (its last price is kept for 90 days). `PRICE_ASC` (the default) and `REVIEW_SCORE_DESC` give a steadier set.
- **One record per search.** Runs sharing a `monitorStoreName` share the records; changing the city, stay, occupancy, sort, currency, language or `priceView` of a search starts a new reference for it. A city typed by name is identified by its text (up to case and spacing), a `cityId` or a search URL by the city id. Each destination of `locations` and each search URL has its own record; keep the same list, in the same order, from run to run, since a property found by two destinations is compared under the first one. Delete the key-value store in Apify Console to start over.

### Output example

One real row, produced by the Actor from a Lisbon search sorted by price (USD, taxes and fees included, 20–23 November 2026, 2 adults). All columns are present, in this order, for every property:

```json
{
  "schemaVersion": "travel-listing/1.5",
  "platform": "agoda",
  "platformListingId": "39627106",
  "url": "https://www.agoda.com/costa-by-the-beach/hotel/all/lisbon-pt.html?checkIn=2026-11-20&los=3&rooms=1&adults=2&children=0",
  "title": "Costa by the Beach",
  "propertyType": "Guest house",
  "roomType": "hotel_room",
  "city": "Lisbon",
  "region": "Costa da Caparica",
  "country": "Portugal",
  "countryCode": "PT",
  "latitude": 38.643768310546875,
  "longitude": -9.237600326538086,
  "checkIn": "2026-11-20",
  "checkOut": "2026-11-23",
  "nights": 3,
  "guests": 2,
  "currency": "USD",
  "pricePerNight": 28.02,
  "priceTotal": 84.06,
  "priceBeforeDiscount": null,
  "cleaningFee": null,
  "serviceFee": null,
  "taxesAndFees": 17.43,
  "taxesIncluded": true,
  "priceTotalAllIn": 84.06,
  "rating": 6.4,
  "ratingMax": 10,
  "reviewCount": 10,
  "starRating": 3,
  "bedrooms": null,
  "beds": null,
  "bathrooms": null,
  "maxGuests": null,
  "amenities": [],
  "badges": [],
  "isSuperhost": null,
  "listedBy": null,
  "instantBook": null,
  "isNew": null,
  "isAvailable": true,
  "cancellationPolicy": null,
  "imageUrls": [
    "https://q-xx.bstatic.com/xdata/images/hotel/max500/454040021.jpg?k=29ec8113112113fbc94a681d5d4ae279525474509fca38517af12b8c88fd6111&o=",
    "https://q-xx.bstatic.com/xdata/images/hotel/max500/463977815.jpg?k=40a5319e3332559255413df4d7abec218852ee901ff47aaf826a637b59d07ceb&o=",
    "https://q-xx.bstatic.com/xdata/images/hotel/max500/454040047.jpg?k=5f234ae04d5abf7d52fa04ca94dd84172899eb06b4cbbb21a5507e47e4a434f0&o=",
    "https://q-xx.bstatic.com/xdata/images/hotel/max500/486105799.jpg?k=166115449cf1dfbb89073aa3917a6d4c1d33e84337e9318cfe6875d9445882e6&o=",
    "https://q-xx.bstatic.com/xdata/images/hotel/max500/486105837.jpg?k=0c9452c981811042c1a0232adaf8ba943ddbb7330a1d679fe76eb06d59e0fba3&o=",
    "https://q-xx.bstatic.com/xdata/images/hotel/max500/463977813.jpg?k=ecacfc8334863189dcebba75d3e795e2325ed3c7616fde572dc3c3d646c7412f&o=",
    "https://q-xx.bstatic.com/xdata/images/hotel/max500/463977818.jpg?k=2b75debd8d9252a1f3511939d258606e3d0a6e91e0023d77117c392910bccc24&o=",
    "https://q-xx.bstatic.com/xdata/images/hotel/max500/486106321.jpg?k=fa759acaa386f80c7c7674008bf4dc1b7f30d4d66b2cb290a5561fafd9d413f1&o=",
    "https://q-xx.bstatic.com/xdata/images/hotel/max500/486106322.jpg?k=50b46e9683fefb36a5da302404124e4e054f60837fa776dfb86b33255b0b0842&o=",
    "https://q-xx.bstatic.com/xdata/images/hotel/max500/463977829.jpg?k=04123d3aad7646172b97e49e8148684af93fac257be611fbf39796b1ce29ab3e&o="
  ],
  "imageCount": 10,
  "searchLocation": "Lisbon, Portugal",
  "searchRank": 3,
  "searchPage": 1,
  "viewCountryCode": null,
  "scrapedAt": "2026-10-20T08:00:00.000Z",
  "changeType": null,
  "previousPrice": null,
  "priceDelta": null,
  "priceDeltaPercent": null
}
```

A discounted property has `priceBeforeDiscount` set to the strike-through price per night; a sponsored placement carries the `Sponsored` badge; an apartment or house listed in Agoda's non-hotel inventory has `roomType` `entire_place` with its rooms, beds and bathrooms; a property sold out for the dates (or whose price Agoda had not computed yet) has `isAvailable` `false` and every price column `null`; with `locale` `fr-fr` the city, neighbourhood, country and badges are in French.

The **Output** tab offers four views of the same rows: **Overview** (name, stars, neighbourhood, dates, prices, rating, reviews, link, photos), **Prices** (nights, nightly price, total, strike-through price, currency, cancellation, badges), **Location** (city, neighbourhood, country, coordinates, rank) and **Changes** (for price-monitoring runs: change, total, previous total, difference in amount and percent).

### How much does it cost to scrape Agoda?

This Actor uses **pay-per-event pricing: you pay a fixed price per property** saved to your dataset, and nothing else. On the Free plan, properties cost $2.00 per 1,000, so Apify's free monthly credit of $5 covers about 2,499 properties; paid plans pay less per property, and the price of every plan tier is on the **Pricing tab** of this page.

| What you pay for | How it is counted | Price |
|---|---|---|
| `result` event: one property saved to your dataset | once per unique property of the run, capped by `maxResults` (per destination with `locations`); with `onlyChanges`, only the new properties and the price changes | Pricing tab, per plan tier |
| Run start | a symbolic amount, once per run | Pricing tab |
| Compute, proxies, storage and data transfer | included: no platform usage is passed on to you | — |

The cost of a run is known before you start: 1,000 properties cost 1,000 times the `result` price of your tier, whatever the number of pages fetched. One request returns up to 100 properties in one to two seconds, so a run of several hundred properties takes well under a minute.

### Limits and behaviour worth knowing

- **One stay per run.** Every row of a run shares the same `checkIn`, `checkOut`, `guests` and `currency`, which is what makes prices comparable. One run can cover several cities through `locations` or `searchUrls` (`searchLocation` tells them apart); otherwise schedule several tasks.
- **Thousands of properties per city.** Agoda announces the total in the search results (logged at the start of the run: about 4,300 in Lisbon, 6,800 in Paris); the scraper stops there, at `maxResults` (2,000 at most) or after two pages without a new property.
- **Two prices for every offer.** Agoda returns the price with and without taxes and fees for the cheapest offer, per night and for the stay. `priceView` decides which one fills `pricePerNight`, `priceTotal` and `priceBeforeDiscount`; `taxesAndFees` always gives the difference for the stay, and `priceTotalAllIn` the total with taxes and fees in both views. Agoda's cheapest-first order and the prices it advertises are the ones before taxes.
- **Properties without a price.** A property sold out for the dates, or whose price Agoda had not finished computing when the page was served (a handful per thousand), is returned with `isAvailable` `false` and `null` prices rather than dropped, so that your dataset still lists it; run again later to get its price.
- **Agoda's own ranking moves.** The `RANKING` order changes from one request to the next, so pages overlap: duplicates are removed by id and the final order is indicative. `PRICE_ASC` and `REVIEW_SCORE_DESC` are stable and exact across pages.
- **Currency and language.** `USD` / `EUR` and `en-us` / `fr-fr` are validated. Other ISO currency codes and other Agoda locales are passed to the API as they are, without having been verified: check the first rows. Property names keep their own language; city, neighbourhood, country and badges follow the locale.
- **Search results show a subset of the property.** Amenities, room details, the detailed cancellation policy (only Agoda's "easy cancellation" flag is read), guest capacity and the full photo gallery are not in search results and are therefore `null`, empty or limited to ten photos. Property pages are not covered by this Actor.
- **Retries and failures.** An unusable response (refused request, non-JSON body, response without data) is retried up to three times on a new proxy session with a growing pause (1, 2, 4 seconds), then the page is abandoned and reported. If Agoda rejects the request itself (its API changed), the run fails at once with the server's own list of what is wrong, so that the Actor can be updated quickly.
- **No empty successes.** A run that finds no property ends as failed with an explicit message (city not recognised, dates in the past, every request refused), so that your schedules and integrations notice. The one exception is a price-monitoring run with `onlyChanges` that compared properties and found no change: it succeeds with an empty dataset.

### Integrations and API

Start the Actor from Apify Console, schedule it, or call it from the [Apify API](https://docs.apify.com/api/v2) with any language (the **API** tab shows ready-made code for Node.js, Python and curl). Webhooks notify your systems when a run finishes; the dataset can be pulled as JSON, CSV, Excel, XML or RSS from the run's Output tab, or pushed to Google Sheets, Make, Zapier, n8n, Slack, Airtable, Google Drive and more through the Apify integrations. AI agents can call the Actor through the Apify MCP server.

### Use it from your AI assistant

Add this Actor as a tool to Claude, ChatGPT, Cursor, VS Code or any MCP client with the Apify MCP server URL below (sign in with your Apify account when asked):

`https://mcp.apify.com?tools=myth84/agoda-hotels-scraper`

Then ask in plain words. Copy this prompt and change the details:

```text
Use myth84/agoda-hotels-scraper to find the 20 cheapest hotels in Bangkok for 2 adults, 3 nights starting 30 days from today, prices in USD with taxes and fees included. Input: {"location": "Bangkok", "checkInDaysFromNow": 30, "nights": 3, "adults": 2, "currency": "USD", "sortBy": "PRICE_ASC", "priceView": "inclusive", "maxResults": 20}. Show the name, price per night, total price, taxes and fees, guest rating, number of reviews and link of each hotel.
```

### For AI agents (MCP)

Through the [Apify MCP server](https://docs.apify.com/mcp) this Actor is a tool that answers questions such as *"what do hotels in Lisbon cost for two adults from 20 to 23 November, taxes included?"*, *"list the best-rated hotels in Bangkok under 100 dollars a night"* or *"compare the cheapest hotels in Bangkok and Singapore next month"*.

**Minimal input** (one or more cities in `locations`, `maxResults` per city; every other field keeps its default, raise `maxResults` only when the question needs more rows):

```json
{
  "locations": ["Bangkok", "Singapore"],
  "checkInDaysFromNow": 30,
  "nights": 3,
  "adults": 2,
  "currency": "USD",
  "maxResults": 20
}
```

- **Key output fields**: `platformListingId`, `title`, `url`, `pricePerNight`, `priceTotal`, `taxesAndFees`, `currency`, `rating` (on 10), `reviewCount`, `starRating`, `propertyType`, `region`, `latitude` / `longitude`, `isAvailable`. Every column is always present, in a fixed order; `null` means Agoda did not show the value. The full list is in *What data does Agoda Scraper extract?* above.
- **Use it when** you need current Agoda search results for a city and a stay: nightly and total prices with and without taxes, guest ratings, review counts, stars, property types, neighbourhoods, coordinates, photos, links. Pass `locations` (one or more city names, searched one by one, `maxResults` each) or `location`, the dates (`checkIn` / `checkOut`, or `checkInDaysFromNow` + `nights`), `adults` and `childAges`, `currency` and `locale`, optionally `sortBy` and `priceView`, and a small `maxResults` (10–50 is enough for an answer).
- **Track prices** by scheduling the same input with `monitorPrices: true` (and `onlyChanges: true` for changes only): rows carry `changeType`, `previousPrice`, `priceDelta`, `priceDeltaPercent`.
- **Do not use it for** guest reviews, property descriptions and amenities, room-level availability, booking or anything behind a login: it does not collect personal data and only reads public search results.
- **Cost per 1,000 results**: 1,000 `result` events, $2.00 per 1,000 on the Free plan and less on paid plans (Pricing tab); nothing else is charged.
- **Typical time**: one to two seconds per request of up to 100 properties: 20 properties in a few seconds, 500 in well under a minute.

### Alternatives and switching

- **Official API.** Agoda's APIs are made for its affiliate and connectivity partners and need an approved partner account; if you have one and need bookings or contracted rates, use it. This Actor reads what any visitor sees in Agoda search results (public prices with and without taxes, guest ratings, stars) with no account, no keys and no approval.
- **Your own scraper.** Nothing to build or maintain: sessions, retries, pagination, deduplication and Agoda API changes are handled for you. The columns stay the same from run to run, a run that finds nothing fails with an explicit message instead of succeeding empty, and a scheduled test run catches Agoda changes early.
- **Another scraper from the Store.** What you can count on here: `maxResults` is a hard cap (per destination with `locations`), the run stops at that number of properties and you are never charged for more; with `monitorPrices` and `onlyChanges`, a scheduled run returns and charges only the new properties and the price changes; `priceTotalAllIn` gives one total, taxes included, to compare with the other platforms. The input names differ from one Actor to the next; the usual ones map to this Actor as follows:

| Usual input | Here | Note |
|---|---|---|
| `search`, `query`, `destination`, `city` | `location`, `locations` for up to 50 cities, or `cityId` | City name as typed on Agoda, or Agoda's numeric city id |
| `startUrls` | `searchUrls` | Agoda search URLs, one search each |
| `maxItems`, `limit` | `maxResults` | Unique properties per run, never charged beyond |
| `checkin`, `checkout` | `checkIn`, `checkOut` | YYYY-MM-DD; or `checkInDaysFromNow` + `nights` for schedules |
| `adults`, `children` | `adults`, `childAges` | One room; children are given by their ages |
| `sort` | `sortBy` | `PRICE_ASC`, `REVIEW_SCORE_DESC` or `RANKING` |
| `includeTaxes` | `priceView` | `inclusive` or `exclusive`; `taxesAndFees` is filled either way |
| `currency`, `language` | `currency`, `locale` | ISO 4217 code, Agoda locale such as `en-us` |
| `proxy` | `proxyConfiguration` | Apify proxies, included in the price |
| `monitor`, `trackPrices`, `alerts` | `monitorPrices`, `onlyChanges` | Compares with your previous run of the same search; only changes when asked |

### FAQ

#### Does Agoda Scraper collect personal data?

No. The scraper reads search results only and keeps nothing about guests, reviewers or hosts as persons: no names, photos, profile ids, contact details or review texts. It outputs property facts (business names and neighbourhoods), the average guest rating and the number of reviews. Every row goes through a privacy filter before it is saved.

#### Is it legal to scrape Agoda?

The Actor only reads search results that Agoda serves to any visitor without an account, and it stores no personal data. Scraping public data is generally lawful, but you are responsible for how you use the results and for complying with Agoda's terms of service and the laws that apply to you (including copyright on property texts and photos). Use the data for analysis, not to republish listings.

#### Why are there two prices and which one do I get?

Agoda shows prices before taxes and fees by default on its site and the final price at checkout. The Actor reads both for the cheapest offer: with `priceView` `inclusive` (the default) the price columns are the final prices, with `exclusive` they are the advertised ones; `taxesAndFees` always gives the difference for the whole stay, so you can rebuild either.

#### Why are the prices different from what I see in my browser?

Prices depend on the stay (dates, adults, children), the currency and the moment: Agoda runs time-limited deals and member prices (never used here: no login). Compare rows obtained with the same input, and mind the `priceView` setting (taxes included or not), or compare `priceTotalAllIn`, the total with taxes and fees in both views.

#### Do I need proxies?

No extra setup: the default Apify datacenter proxies passed every request of our tests and are included in the price. If Agoda starts refusing requests (the run log shows retries and abandoned pages), raise `delayMs`, try again later or bring your own proxy URLs, and open an issue with your run id: blocking changes are handled as a priority.

#### Can I search several cities in one run?

Yes. Put up to 50 city names in `locations`, one per line ("Paris", "Rome", "Barcelona"…), alongside or instead of `location`. Each city is a separate search with the same dates, occupancy, currency and sort, and `maxResults` applies to each city: 3 cities with `maxResults` 50 give up to 150 properties. Every row says in `searchLocation` which city found it, and a property found by two destinations is saved and charged once. Large lists need more requests: raise `maxRequests` if the log says that some destinations were not searched.

#### Which cities, currencies and languages are supported?

Any city Agoda's search box recognises (the log shows the city retained; give `cityId` or a search URL to be exact). `USD` / `EUR` and `en-us` / `fr-fr` are validated; other currencies and locales of the site are passed through without having been verified.

#### What are relative dates?

Leave `checkIn` and `checkOut` empty and the Actor searches a stay starting `checkInDaysFromNow` days from today for `nights` nights, so that a scheduled run or an example task never expires. Give fixed dates when you need a quote for precise dates.

#### How often should I run it, and how do I get only price changes?

Every run fetches live search results. Prices and availability change daily, so schedule the Actor as often as your use case needs (daily is typical for price tracking) and compare runs by `platformListingId`, `checkIn` and `checkOut`, or let the Actor compare them for you: tick `monitorPrices` (and `onlyChanges`) and schedule the run. The first run records every property of the search and returns it as `new`; each later run flags the properties that appeared since and the properties whose price changed, with `previousPrice`, `priceDelta` and `priceDeltaPercent`, and with `onlyChanges` returns and charges only those rows. A run without any change succeeds with an empty dataset. The previous prices live in the key-value store `monitorStoreName` of your account; keep the same city, stay, occupancy, sort, currency, language and `priceView` from run to run. With relative dates you follow the price of a stay at a constant lead time; with fixed dates, the price of one stay.

#### Where do I get support?

Open an issue in the **Issues** tab of this Actor with your run id and input. Bugs and API changes are fixed as a priority; feature requests (filters, property details, other currencies and languages, other platforms of the travel studio) are welcome.

### More scrapers by Flatrow Data Studio

The travel scrapers of the studio share the same core columns, so stays from several platforms land in one table; compare their prices on `priceTotalAllIn`, the total of the stay with taxes and fees, filled with the same rule by each scraper (`null` when a platform does not show the taxes):

- [Booking.com Hotels Scraper & Price Tracker](https://apify.com/myth84/booking-hotels-scraper): Booking.com hotel prices for your dates with the excluded taxes, review scores and stars, search URLs and price monitoring.
- [Expedia Hotels Scraper & Price Tracker](https://apify.com/myth84/expedia-hotels-scraper): Expedia hotel prices per night and for the whole stay, guest ratings and review counts, with price monitoring, to compare with the other platforms.
- [Airbnb Listings Scraper & Price Tracker](https://apify.com/myth84/airbnb-listings-scraper): Airbnb prices, ratings, review counts and locations for any place and dates, up to 50 destinations per run, with price monitoring.
- [Airbnb Listing Details & Availability Calendar](https://apify.com/myth84/airbnb-listing-details-scraper): amenities, house rules, six category ratings and a 1–12 month availability calendar for the Airbnb listing URLs you give.

Did this scraper give you the data you needed? A rating on its Store page helps other people find it. For a bug or a missing field, open an issue in the **Issues** tab.

### Legal note

This Actor reads publicly available Agoda search results without logging in and collects no personal data. You are responsible for your use of the data and for complying with Agoda's terms and the laws that apply to you.

Not affiliated with, endorsed or sponsored by Agoda or Booking Holdings. Agoda is a trademark of Agoda Company Pte. Ltd.

# Changelog

This Actor's version history is a separate document: https://apify.com/myth84/agoda-hotels-scraper/changelog.md

# Actor input Schema

## `locations` (type: `array`):

One or more cities, one per line, as typed in the Agoda search box, e.g. <code>Lisbon</code>, <code>Paris</code>, <code>Bangkok</code>. Each is its own search with the same dates, occupancy, currency and sort, and <b>maxResults applies to each destination</b> (and to each search URL). A property found twice is saved and charged once. Always searched, also with cityId or searchUrls: clear the list to search those only. Up to 50. Default empty; with no city at all, the Actor searches Lisbon.

## `cityId` (type: `integer`):

Advanced: Agoda's numeric city id, a positive integer, the <code>city</code> parameter of a search URL, e.g. <code>15470</code> for Paris (Lisbon = 16364). Takes precedence over the city name, which then only labels the rows; ignored when searchUrls is given. Leave empty to search by name.

## `searchUrls` (type: `array`):

Advanced: search URLs copied from agoda.com, one per line, starting with http(s) and with a <code>city</code> parameter, e.g. <code>https://www.agoda.com/search?city=16364\&checkIn=2026-11-20\&los=3\&adults=2</code>; one search each, their city, dates and occupancy win over the fields. They take precedence over location and cityId; locations are still searched. A URL that is not an Agoda city search stops the run with a clear message. Leave empty to search by name.

## `location` (type: `string`):

Single destination (kept for compatibility; prefer <code>locations</code>): one city as typed in the Agoda search box, e.g. <code>Bangkok</code>, resolved by Agoda's suggestion service and searched before the cities of <code>locations</code> (up to 50 together). Ignored when searchUrls is given; with cityId it only labels the rows. Leave empty when you use locations.

## `checkIn` (type: `string`):

Check-in date, YYYY-MM-DD, e.g. <code>2026-12-01</code>, given with checkOut. Leave both empty for a stay relative to today (checkInDaysFromNow and nights), better for schedules: fixed dates expire.

## `checkOut` (type: `string`):

Check-out date, YYYY-MM-DD, after checkIn, e.g. <code>2026-12-04</code>. Required when checkIn is set.

## `checkInDaysFromNow` (type: `integer`):

Without fixed dates: check-in this many days after today (UTC), e.g. <code>7</code> for next week. Default 30: a month ahead, a typical booking window, and the stay moves with each scheduled run so it never expires.

## `nights` (type: `integer`):

Without fixed dates: length of the stay in nights, 1 to 28. Default 3: a typical city break.

## `adults` (type: `integer`):

Number of adults in one room, 1 to 9; prices depend on it. Default 2: the most searched occupancy.

## `childAges` (type: `array`):

Ages of the children in the room, one integer per child (0 to 17, up to 6), e.g. <code>\[8]</code> or <code>\[4, 10]</code>: the search is then sent as an Agoda family search with these children in the occupancy, so prices are for the whole party. Default empty: adults only.

## `maxResults` (type: `integer`):

Maximum number of properties saved and charged, 1 to 2,000 (Agoda lists thousands per capital; one request returns up to 100). With <code>locations</code> it applies to each destination; with price monitoring it is the number compared. Default 100: enough to compare a city, about $0.20 on the Free plan. The form prefills 20 for a quick first run (our daily check).

## `sortBy` (type: `string`):

Order of the results: <code>PRICE_ASC</code> (cheapest first, by the price before taxes) and <code>REVIEW_SCORE_DESC</code> are validated; <code>RANKING</code> is Agoda's own order, variable between requests; stars and distance are passed as they are. Default PRICE_ASC: exact and stable across pages.

## `priceView` (type: `string`):

Which Agoda price fills pricePerNight, priceTotal and priceBeforeDiscount: <code>inclusive</code> (taxes and fees included, what you pay) or <code>exclusive</code> (before taxes and fees, what Agoda advertises); taxesAndFees gives the difference either way. Default inclusive: the final price.

## `resultsPerPage` (type: `string`):

Properties fetched by one request: <code>100</code> (the server maximum) or <code>45</code> (the site's page size), both validated. Default 100: fewer requests.

## `currency` (type: `string`):

ISO 4217 code of every price, e.g. <code>USD</code>, <code>EUR</code> or <code>GBP</code>; pick one or type any code Agoda supports (USD and EUR validated). Default USD: the currency of our validation runs.

## `locale` (type: `string`):

Agoda language of the city, neighbourhood and country names and of the badges, e.g. <code>en-us</code>, <code>en-gb</code>, <code>fr-fr</code>, <code>de-de</code>, <code>es-es</code>; pick one or type any locale of the site (en-us and fr-fr validated). Property names keep their own language. Default en-us: English (US), validated.

## `monitorPrices` (type: `boolean`):

Compare the total price of each property (the price per night when Agoda gives no total; taxes included or not as in priceView) with the one recorded by your previous run of the same search, and fill changeType, previousPrice and priceDelta. maxResults is then the number of properties compared. Default off: a plain search.

## `monitorStoreName` (type: `string`):

Key-value store of your account that keeps the previous prices, one record per search (each city and each search URL has its own), e.g. <code>agoda-bangkok-daily</code>. Letters, digits and dashes, up to 63 characters. Default agoda-price-monitor: one store for all your monitoring runs.

## `onlyChanges` (type: `boolean`):

With monitorPrices: save and charge only the new properties and the price changes (price drop alerts); a run without any change succeeds with an empty dataset. Default off: every compared property is saved with its changeType.

## `proxyConfiguration` (type: `object`):

Proxies of the requests. Default: Apify datacenter proxies, included in the price, which passed every request of our tests. A proxy country you select is recorded in viewCountryCode; you can also give your own proxy URLs.

## `maxRequests` (type: `integer`):

Safety cap on API requests for the whole run, retries and place lookups included: one per page of up to 100 properties, plus one lookup per city name. Default 100: enough for 50 cities of 100 properties.

## `delayMs` (type: `integer`):

Pause between two page requests of the same search, in milliseconds (retries have their own growing pause); 0 disables it. Default 1000: one second, the pacing of our tests.

## `debugStoreHtml` (type: `boolean`):

Debugging only: save every API response (JSON, refused attempts and place lookups included) in the run's key-value store. Default off: it uses storage.

## Actor input object example

```json
{
  "locations": [
    "Lisbon"
  ],
  "checkInDaysFromNow": 30,
  "nights": 3,
  "adults": 2,
  "childAges": [],
  "maxResults": 20,
  "sortBy": "PRICE_ASC",
  "priceView": "inclusive",
  "resultsPerPage": "100",
  "currency": "USD",
  "locale": "en-us",
  "monitorPrices": false,
  "monitorStoreName": "agoda-price-monitor",
  "onlyChanges": false,
  "proxyConfiguration": {
    "useApifyProxy": true
  },
  "maxRequests": 100,
  "delayMs": 1000,
  "debugStoreHtml": false
}
```

# Actor output Schema

## `listings` (type: `string`):

All extracted properties as dataset items, one flat row per property. Append ?format=csv or ?format=xlsx for spreadsheets. The Overview view is selected by default.

## `runStats` (type: `string`):

Requests and retries made, bytes transferred, pages, duplicates, properties without a price, places resolved, properties seen and pushed, and the estimated platform cost of the run.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "locations": [
        "Lisbon"
    ],
    "maxResults": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("myth84/agoda-hotels-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "locations": ["Lisbon"],
    "maxResults": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("myth84/agoda-hotels-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "locations": [
    "Lisbon"
  ],
  "maxResults": 20
}' |
apify call myth84/agoda-hotels-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,myth84/agoda-hotels-scraper"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/d6NIZtdvf7PHvE1Li/builds/4xElio0g9YfZSjAg9/openapi.json
