# Agoda All-in-One API (`romy/agoda-all-in-one-api`) Actor

Unofficial always-on REST API for live Agoda data: hotel search, property detail, rooms & rates, guest reviews, destination discovery, and flight search. No account, app or device needed.

- **URL**: https://apify.com/romy/agoda-all-in-one-api.md
- **Developed by:** [Romy](https://apify.com/romy) (community)
- **Categories:** Travel
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.47 / 1,000 destination search results

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.
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 All-in-One API

Unofficial, always-on REST API for live Agoda data: hotel search, property detail,
rooms & rates, guest reviews, destination discovery and flight search. Wraps
Agoda's own Android-app API (`searchapi.agoda.com`), reverse-engineered from the
real app and verified live against the real API — including from Apify's own
IPs. No account, login, app or device needed for any endpoint here.

### Why

Agoda's public app API needs no login and no request signing for reads — the
only quirks are a public per-app client key (`AG-Initiator-Api-Key`, baked
into every install, not a secret) and, for the legacy destination-autocomplete
endpoint, a specific `User-Agent` shape the app itself sends. Every parameter
below was live-tested and classified as a real filter, a shaping/ordering
flag, or inert (accepted but ignored) before being exposed here — inert
params found during research are *not* exposed as if they worked.

- **Search filters and sorts are honest.** Server-side flight filters (stops,
  airlines, times, price range) and most flight sort options are inert on
  Agoda's own API — they validate but don't change the result. This API does
  not pretend otherwise: those aren't exposed as server params. `/flights/search`
  offers a client-side `sort` instead, clearly documented as sorting only the
  returned page.
- **Deep pagination is honestly unstable.** Agoda's own hotel-search paging
  can return duplicate hotels across pages for large destinations. This API
  passes `page`/`pageSize` straight through rather than silently promising a
  complete, deduped list it can't deliver.
- **Prices can differ between identical calls** (Agoda's own pricing engine,
  not a bug here) — expect small variance, not a bitwise-identical price on
  every call for the same room.
- **PII is opt-in.** Reviewer display names/photos and host profile fields
  are excluded by default and only returned when explicitly requested
  (`includeReviewerName` / `includePii`).

### Endpoints

#### Destinations

##### `GET /destinations/search`

Free-text search for cities, areas, landmarks, properties and regions (Agoda app textsearch). Any language; matches prefixes, substrings, IATA codes, hotel names and country names. Fixed cap of 15 results, no pagination.

| Param | Required | Description |
| --- | --- | --- |
| `q` | yes | Search text: place/hotel name, IATA code, or country name. |
| `type` | no | Restrict results to one place type. (one of `city`, `area`, `landmark`, `property`, `region`) |
| `country` | no | Agoda countryId filter (see /destinations/reference?type=countries). |
| `excludeCountries` | no | Comma-separated Agoda countryIds to exclude from the results. (comma-separated) |
| `locale` | no | Response language as an Agoda locale, e.g. `th-th`, `ja-jp`, `zh-cn`, `en-us` (default). Not every locale is translated server-side; unsupported ones silently fall back to English. |

Billed per item returned (`destination-search-result` event); an empty result is free.

##### `GET /destinations/:cityId/explore`

Combined city + top areas + top landmarks view for one city (PlaceRecommendationSearch.filterByCity), chosen over the plainer CityTopAreaSearch because it is the richer shape the app itself uses. `items` mixes areas and landmarks (see `kind`).

| Param | Required | Description |
| --- | --- | --- |
| `cityId` | yes | Agoda cityId (from /destinations/search). |
| `limit` | no | Number of areas and landmarks to return, 1-20 (default 15). |
| `locale` | no | Response language as an Agoda locale, e.g. `th-th`, `ja-jp`, `zh-cn`, `en-us` (default). Not every locale is translated server-side; unsupported ones silently fall back to English. |

Billed per item returned (`destination-explore-result` event); an empty result is free.

##### `GET /destinations/countries/:countryId/cities`

Every city of a country (DomesticStateCitySearch), flattened from the upstream's state->cities tree; each row carries its state. States are capped at the top 20 by popularity server-side (no way around it). Large countries return thousands of cities (e.g. US: 5338) with no upstream pagination, so this actor paginates the flattened list itself with `page`/`limit`.

| Param | Required | Description |
| --- | --- | --- |
| `countryId` | yes | Agoda countryId (see /destinations/reference?type=countries). |
| `touristRegions` | no | Include per-city tourist-region breakdown (default true). Set false to omit them (slightly smaller response). |
| `page` | no | 1-based page number (default 1). |
| `limit` | no | Number of cities to return, 1-1000 (default 200). |
| `locale` | no | Response language as an Agoda locale, e.g. `th-th`, `ja-jp`, `zh-cn`, `en-us` (default). Not every locale is translated server-side; unsupported ones silently fall back to English. |

Billed per item returned (`destination-country-city` event); an empty result is free.

##### `GET /destinations/:cityId/price-trend`

Cheapest nightly price per day for a city (priceTrendSearch), one row per day in the requested window. The upstream horizon is ~180 days out; later dates are silently trimmed. `searchType` is fixed to `CitySearch` (the only value verified to return data) and `supportedRateChannelIds` fixed to `[1]` (verified inert when varied) -- neither is exposed as a param since no other value was proven working.

| Param | Required | Description |
| --- | --- | --- |
| `cityId` | yes | Agoda cityId (from /destinations/search). |
| `from` | yes | Start of the price-trend window. Format `YYYY-MM-DD`. |
| `to` | yes | End of the price-trend window (later dates beyond the ~180-day upstream horizon are silently dropped). Format `YYYY-MM-DD`. |
| `los` | no | Length of stay in nights; changes the returned nightly price (default 1). |
| `occ` | no | Occupancy (number of guests, default 2). Some values (e.g. 4) return zero rows for some cities -- the calendar does not exist for every occupancy. |
| `currency` | no | Currency for the returned prices. One of: AED, AFN, AMD, ARS, AUD, AWG, AZN, BBD, BGN, BHD, BIF, BMD, BND, BOB, BRL, BSD, BZD, CAD, CHF, CNY, CVE, CZK, DKK, DZD, EUR, FJD, GBP, GHS, HKD, IDR, INR, JPY, KRW, KYD, MYR, NOK, NZD, OMR, PAB, PHP, PLN, SEK, SGD, THB, TWD, USD, XOF, ZAR. (one of `AED`, `AFN`, `AMD`, `ARS`, `AUD`, `AWG`, `AZN`, `BBD`, `BGN`, `BHD`, `BIF`, `BMD`, `BND`, `BOB`, `BRL`, `BSD`, `BZD`, `CAD`, `CHF`, `CNY`, `CVE`, `CZK`, `DKK`, `DZD`, `EUR`, `FJD`, `GBP`, `GHS`, `HKD`, `IDR`, `INR`, `JPY`, `KRW`, `KYD`, `MYR`, `NOK`, `NZD`, `OMR`, `PAB`, `PHP`, `PLN`, `SEK`, `SGD`, `THB`, `TWD`, `USD`, `XOF`, `ZAR`) |
| `allInclusive` | no | true = tax/fee-inclusive price, false (default) = exclusive headline price. |
| `locale` | no | Response language as an Agoda locale, e.g. `th-th`, `ja-jp`, `zh-cn`, `en-us` (default). Not every locale is translated server-side; unsupported ones silently fall back to English. |

Billed per item returned (`destination-price-trend-day` event); an empty result is free.

##### `GET /destinations/reference`

Agoda's own lookup tables (mapi metadata()): languages, currencies and countries, with the numeric ids used by every other endpoint's `country`/`currency`/`locale` params. Omit `type` to get all three under separate keys; billed once per request regardless of how many rows come back.

| Param | Required | Description |
| --- | --- | --- |
| `type` | no | Limit the response to one dataset; omit to get all three. (one of `languages`, `currencies`, `countries`) |
| `locale` | no | Response language as an Agoda locale, e.g. `th-th`, `ja-jp`, `zh-cn`, `en-us` (default). Not every locale is translated server-side; unsupported ones silently fall back to English. |

Billed once per successful request (`destination-reference` event).

#### Hotels

##### `GET /hotels/search`

The main hotel-search endpoint: all 7 live-verified Agoda search types (city/area/region/radius/box/landmark/propertySummary), with sort, pagination and the filters proven to actually narrow results (see each param's description for its evidence). searchInfo totals (totalActiveHotels/totalFilteredHotels/totalAvailableHotelsWithoutFilter) jitter +-30 between identical requests seconds apart and totalFilteredHotels is unreliable once a price filter is set -- prefer counting the items array. Deep paging is unstable (see pageSize) and is passed through faithfully, not deduped.

| Param | Required | Description |
| --- | --- | --- |
| `searchType` | yes | Which Agoda destination-search variant to run (all 7 verified live). Each needs its own destination params: city->cityId, area->areaId+cityId, region->regionId, radius->lat+lon+distanceKm, box->topLeftLat/topLeftLon/bottomRightLat/bottomRightLon, landmark->landmarkId, propertySummary->propertyIds. Sending the wrong combination for the chosen type returns HTTP 400. (one of `city`, `area`, `region`, `radius`, `box`, `landmark`, `propertySummary`) |
| `cityId` | no | Agoda city id. Required for searchType=city; also required (together with areaId) for searchType=area. |
| `areaId` | no | Agoda area id. Required for searchType=area (together with cityId). |
| `regionId` | no | Agoda region id (its own id namespace, distinct from city/state ids). Required for searchType=region. |
| `landmarkId` | no | Agoda landmark id. Required for searchType=landmark. |
| `lat` | no | Latitude, decimal degrees. Required for searchType=radius. |
| `lon` | no | Longitude, decimal degrees. Required for searchType=radius. |
| `distanceKm` | no | Search radius in kilometres, 0-150 (Agoda rejects >150 with HTTP 400). Required for searchType=radius. |
| `topLeftLat` | no | Bounding-box top-left latitude. Required for searchType=box. |
| `topLeftLon` | no | Bounding-box top-left longitude. Required for searchType=box. |
| `bottomRightLat` | no | Bounding-box bottom-right latitude. Required for searchType=box. |
| `bottomRightLon` | no | Bounding-box bottom-right longitude. Required for searchType=box. |
| `propertyIds` | no | Property ids to fetch (searchType=propertySummary). (comma-separated) |
| `checkin` | yes | Check-in date. Format `YYYY-MM-DD`. |
| `checkout` | no | Check-out date. Alternative to los: if given, los is computed as checkout-checkin (los is ignored when checkout is present). Format `YYYY-MM-DD`. |
| `los` | no | Length of stay in nights, 1-30 (default 1). Ignored if checkout is given. Agoda accepts los>30 or los=0 but silently returns 0 hotels instead of erroring, so this is enforced here as a real HTTP 400. |
| `adults` | no | Adults per room (default 2). |
| `rooms` | no | Number of rooms (default 1). |
| `children` | no | Children per room (default 0). |
| `childAges` | no | Ages of each child, comma-separated. Padded with age 8 / truncated to match `children` if the count differs. (comma-separated) |
| `currency` | no | ISO currency code for prices. One of: AED, AFN, AMD, ARS, AUD, AWG, AZN, BBD, BGN, BHD, BIF, BMD, BND, BOB, BRL, BSD, BZD, CAD, CHF, CNY, CVE, CZK, DKK, DZD, EUR, FJD, GBP, GHS, HKD, IDR, INR, JPY, KRW, KYD, MYR, NOK, NZD, OMR, PAB, PHP, PLN, SEK, SGD, THB, TWD, USD, XOF, ZAR. (one of `AED`, `AFN`, `AMD`, `ARS`, `AUD`, `AWG`, `AZN`, `BBD`, `BGN`, `BHD`, `BIF`, `BMD`, `BND`, `BOB`, `BRL`, `BSD`, `BZD`, `CAD`, `CHF`, `CNY`, `CVE`, `CZK`, `DKK`, `DZD`, `EUR`, `FJD`, `GBP`, `GHS`, `HKD`, `IDR`, `INR`, `JPY`, `KRW`, `KYD`, `MYR`, `NOK`, `NZD`, `OMR`, `PAB`, `PHP`, `PLN`, `SEK`, `SGD`, `THB`, `TWD`, `USD`, `XOF`, `ZAR`) |
| `locale` | no | Response language as an Agoda locale, e.g. `th-th`, `ja-jp`, `zh-cn`, `en-us` (default). Not every locale is translated server-side; unsupported ones silently fall back to English. |
| `sortField` | no | Sort field (default Ranking = server "best match"). Verified per search.md: Price and StarRating are strictly monotone both directions (Price Asc puts unpriced/sold-out hotels first). AllGuestsReviewScore and the \*ReviewScore segment fields (Groups/FamiliesWithYoung/FamiliesWithTeen/SoloTravllers/Couples/BusinessTraveller) are only meaningful Desc, using a non-public segment/Bayesian score, not the visible cumulative review score; Asc has no clear meaning there. ReviewCount Desc reorders but is NOT strictly monotone in the visible reviewCount (16/49 violations) -- treat as a rough signal only. ReviewAndPrice/InsiderDeal/SecretDeal/PackageDeal ignore sortOrder server-side (Asc==Desc): a deal/blended-value ranking. DistanceFromLandmark only honours sortOrder when sortLandmarkId is also given (otherwise it is a fixed order). Left out entirely (found INERT/aliased to default, or too weakly verified to expose): Distance, Personalize, CuratedCollectionRank, NoSorting, ASD, HotelId, DistanceFromCityCenter. (one of `Ranking`, `Price`, `StarRating`, `AllGuestsReviewScore`, `GroupsReviewScore`, `FamiliesWithYoungReviewScore`, `FamiliesWithTeenReviewScore`, `SoloTravllersReviewScore`, `CouplesReviewScore`, `BusinessTravellerReviewScore`, `ReviewCount`, `ReviewAndPrice`, `InsiderDeal`, `SecretDeal`, `PackageDeal`, `DistanceFromLandmark`) |
| `sortOrder` | no | Sort direction (default Desc). Ignored by some sortField values -- see the sortField description. (one of `Asc`, `Desc`) |
| `sortLandmarkId` | no | Landmark id to sort by distance from. Only affects sortField=DistanceFromLandmark, and is what makes its sortOrder actually take effect (verified). |
| `page` | no | 1-based page number (default 1). |
| `pageSize` | no | Results per page, 1-100 (default 30, Agoda's own default). Deep paging is UNSTABLE: adjacent pages can overlap or skip hotels for a single destination (verified: 57 pages of 100 for one city returned 5671 rows but only 4504 unique propertyIds). This endpoint passes page/pageSize through faithfully -- it does not dedupe or promise completeness across pages. To enumerate a place completely, partition by area/star/price filters until each partition is small and dedupe by propertyId yourself. |
| `stars` | no | Star-rating buckets to include (1-5; 1 means <2 stars, the app's own convention), OR'd together. FILTER, verified in-range on every returned item. (comma-separated) |
| `reviewScoreMin` | no | Minimum guest review score, 0-10 scale. FILTER, verified. |
| `reviewScoreMax` | no | Maximum guest review score, 0-10 scale. FILTER, verified. |
| `priceMin` | no | Minimum per-night price in `currency`. FILTER on the returned items, but searchInfo.totalFilteredHotels can be stale/unreliable once this is set (verified: a (1000,5000) USD filter returned 0 items while totalFilteredHotels still said 5436) -- trust the items list, not the totals, whenever a price filter is active. |
| `priceMax` | no | Maximum per-night price in `currency`. Same totalFilteredHotels caveat as priceMin. |
| `name` | no | Filter by hotel name (server-side match). FILTER, verified ("Hilton" -> 10 hotels in Bangkok). |
| `freeCancellation` | no | Only hotels with a free-cancellation rate available. FILTER, verified. |
| `breakfastIncluded` | no | Only hotels with a breakfast-included room offer (maps to the verified roomBenefits id 78322). FILTER, verified. |
| `paymentOptions` | no | Payment-option ids, OR'd together (verified ids: 84640 pay now, 49499 free cancellation, 52770 book now pay later, 56649 pay at hotel, 80031 no credit card needed). FILTER, verified. (comma-separated) |
| `facilitiesAny` | no | Hotel-facility ids, matches hotels with ANY of them (OR; sample ids: 93 pool, 92 gym, 90 internet, 80 car park, 17 airport transfer, 116 24h front desk). FILTER, verified. (comma-separated) |
| `facilitiesAll` | no | Hotel-facility ids, matches hotels with ALL of them (AND). FILTER, verified (pool AND gym is narrower than pool OR gym). (comma-separated) |
| `accommodationTypes` | no | Accommodation-type ids (sample: 34 Hotel, 29 Apartment/Flat, 33 Hostel, 120 Serviced apartment, 131 Entire House, 32 Guesthouse/B\&B). FILTER, verified. (comma-separated) |
| `areaIds` | no | Restrict results to these area ids within the searched place (independent of searchType=area). FILTER, verified. (comma-separated) |
| `landmarkIds` | no | Restrict results to hotels near these landmark ids (independent of searchType=landmark). FILTER, verified. (comma-separated) |
| `affordableCategory` | no | Affordable-category ids (1 Budget, 2 Luxury). FILTER, verified. (comma-separated) |
| `bedrooms` | no | Number-of-bedrooms buckets (1, 2, 3=3+). FILTER, verified. (comma-separated) |
| `familyFacilities` | no | Family-facility ids. FILTER, verified. (comma-separated) |
| `hostLevel` | no | Host-level ids. FILTER, verified. (comma-separated) |
| `characteristics` | no | Property "characteristic" ids (sample: 1 Backpacker Favorite, 9 Transit, 10 Value For Money, 13 Shopping, 16 Sightseeing). FILTER, verified. (comma-separated) |
| `tripPurpose` | no | Trip-purpose ids. FILTER, verified. (comma-separated) |
| `curatedCollections` | no | Curated-collection ids. FILTER, verified. (comma-separated) |
| `deals` | no | Deal ids (sample: 81337 secret deals, 81485 50%+ discount, 119520 2X AgodaCash, 105046 AgodaVIP Deals). FILTER, verified. (comma-separated) |
| `hotelChainIds` | no | Hotel-chain ids (sample: 3 Accor). FILTER, verified via elasticFilters.terms; the alternate idsFilters(filterKey=HotelChainId) form reproducibly 502s server-side and is deliberately not used here. (comma-separated) |
| `productType` | no | Product-type ids (-1 all, 1 hotels, 2 homes). FILTER, verified. (comma-separated) |
| `bookingDuration` | no | Booking-duration type, lowercase only (the server 400s on uppercase). FILTER, verified ("hourly" -> 18 hotels in Bangkok). (one of `hourly`, `nightly`) |

Billed per item returned (`hotel-search-result` event); an empty result is free.

##### `GET /hotels/count`

Uses Agoda's own pageSize=0 trick (verified: HTTP 200, 0 items, totals still returned) to get just the counts for a destination/filter combination without paying for hotel items. Same totalFilteredHotels reliability caveats as /hotels/search apply.

| Param | Required | Description |
| --- | --- | --- |
| `searchType` | yes | Which Agoda destination-search variant to run (all 7 verified live). Each needs its own destination params: city->cityId, area->areaId+cityId, region->regionId, radius->lat+lon+distanceKm, box->topLeftLat/topLeftLon/bottomRightLat/bottomRightLon, landmark->landmarkId, propertySummary->propertyIds. Sending the wrong combination for the chosen type returns HTTP 400. (one of `city`, `area`, `region`, `radius`, `box`, `landmark`, `propertySummary`) |
| `cityId` | no | Agoda city id. Required for searchType=city; also required (together with areaId) for searchType=area. |
| `areaId` | no | Agoda area id. Required for searchType=area (together with cityId). |
| `regionId` | no | Agoda region id (its own id namespace, distinct from city/state ids). Required for searchType=region. |
| `landmarkId` | no | Agoda landmark id. Required for searchType=landmark. |
| `lat` | no | Latitude, decimal degrees. Required for searchType=radius. |
| `lon` | no | Longitude, decimal degrees. Required for searchType=radius. |
| `distanceKm` | no | Search radius in kilometres, 0-150 (Agoda rejects >150 with HTTP 400). Required for searchType=radius. |
| `topLeftLat` | no | Bounding-box top-left latitude. Required for searchType=box. |
| `topLeftLon` | no | Bounding-box top-left longitude. Required for searchType=box. |
| `bottomRightLat` | no | Bounding-box bottom-right latitude. Required for searchType=box. |
| `bottomRightLon` | no | Bounding-box bottom-right longitude. Required for searchType=box. |
| `propertyIds` | no | Property ids (searchType=propertySummary). (comma-separated) |
| `checkin` | yes | Check-in date. Format `YYYY-MM-DD`. |
| `checkout` | no | Check-out date. Alternative to los: if given, los is computed as checkout-checkin (los is ignored when checkout is present). Format `YYYY-MM-DD`. |
| `los` | no | Length of stay in nights, 1-30 (default 1). Ignored if checkout is given. Agoda accepts los>30 or los=0 but silently returns 0 hotels instead of erroring, so this is enforced here as a real HTTP 400. |
| `adults` | no | Adults per room (default 2). |
| `rooms` | no | Number of rooms (default 1). |
| `children` | no | Children per room (default 0). |
| `childAges` | no | Ages of each child, comma-separated. Padded with age 8 / truncated to match `children` if the count differs. (comma-separated) |
| `currency` | no | ISO currency code for prices. One of: AED, AFN, AMD, ARS, AUD, AWG, AZN, BBD, BGN, BHD, BIF, BMD, BND, BOB, BRL, BSD, BZD, CAD, CHF, CNY, CVE, CZK, DKK, DZD, EUR, FJD, GBP, GHS, HKD, IDR, INR, JPY, KRW, KYD, MYR, NOK, NZD, OMR, PAB, PHP, PLN, SEK, SGD, THB, TWD, USD, XOF, ZAR. (one of `AED`, `AFN`, `AMD`, `ARS`, `AUD`, `AWG`, `AZN`, `BBD`, `BGN`, `BHD`, `BIF`, `BMD`, `BND`, `BOB`, `BRL`, `BSD`, `BZD`, `CAD`, `CHF`, `CNY`, `CVE`, `CZK`, `DKK`, `DZD`, `EUR`, `FJD`, `GBP`, `GHS`, `HKD`, `IDR`, `INR`, `JPY`, `KRW`, `KYD`, `MYR`, `NOK`, `NZD`, `OMR`, `PAB`, `PHP`, `PLN`, `SEK`, `SGD`, `THB`, `TWD`, `USD`, `XOF`, `ZAR`) |
| `locale` | no | Response language as an Agoda locale, e.g. `th-th`, `ja-jp`, `zh-cn`, `en-us` (default). Not every locale is translated server-side; unsupported ones silently fall back to English. |
| `stars` | no | Star-rating buckets to include (1-5; 1 means <2 stars, the app's own convention), OR'd together. FILTER, verified in-range on every returned item. (comma-separated) |
| `reviewScoreMin` | no | Minimum guest review score, 0-10 scale. FILTER, verified. |
| `reviewScoreMax` | no | Maximum guest review score, 0-10 scale. FILTER, verified. |
| `priceMin` | no | Minimum per-night price in `currency`. FILTER on the returned items, but searchInfo.totalFilteredHotels can be stale/unreliable once this is set (verified: a (1000,5000) USD filter returned 0 items while totalFilteredHotels still said 5436) -- trust the items list, not the totals, whenever a price filter is active. |
| `priceMax` | no | Maximum per-night price in `currency`. Same totalFilteredHotels caveat as priceMin. |
| `name` | no | Filter by hotel name (server-side match). FILTER, verified ("Hilton" -> 10 hotels in Bangkok). |
| `freeCancellation` | no | Only hotels with a free-cancellation rate available. FILTER, verified. |
| `breakfastIncluded` | no | Only hotels with a breakfast-included room offer (maps to the verified roomBenefits id 78322). FILTER, verified. |
| `paymentOptions` | no | Payment-option ids, OR'd together (verified ids: 84640 pay now, 49499 free cancellation, 52770 book now pay later, 56649 pay at hotel, 80031 no credit card needed). FILTER, verified. (comma-separated) |
| `facilitiesAny` | no | Hotel-facility ids, matches hotels with ANY of them (OR; sample ids: 93 pool, 92 gym, 90 internet, 80 car park, 17 airport transfer, 116 24h front desk). FILTER, verified. (comma-separated) |
| `facilitiesAll` | no | Hotel-facility ids, matches hotels with ALL of them (AND). FILTER, verified (pool AND gym is narrower than pool OR gym). (comma-separated) |
| `accommodationTypes` | no | Accommodation-type ids (sample: 34 Hotel, 29 Apartment/Flat, 33 Hostel, 120 Serviced apartment, 131 Entire House, 32 Guesthouse/B\&B). FILTER, verified. (comma-separated) |
| `areaIds` | no | Restrict results to these area ids within the searched place (independent of searchType=area). FILTER, verified. (comma-separated) |
| `landmarkIds` | no | Restrict results to hotels near these landmark ids (independent of searchType=landmark). FILTER, verified. (comma-separated) |
| `affordableCategory` | no | Affordable-category ids (1 Budget, 2 Luxury). FILTER, verified. (comma-separated) |
| `bedrooms` | no | Number-of-bedrooms buckets (1, 2, 3=3+). FILTER, verified. (comma-separated) |
| `familyFacilities` | no | Family-facility ids. FILTER, verified. (comma-separated) |
| `hostLevel` | no | Host-level ids. FILTER, verified. (comma-separated) |
| `characteristics` | no | Property "characteristic" ids (sample: 1 Backpacker Favorite, 9 Transit, 10 Value For Money, 13 Shopping, 16 Sightseeing). FILTER, verified. (comma-separated) |
| `tripPurpose` | no | Trip-purpose ids. FILTER, verified. (comma-separated) |
| `curatedCollections` | no | Curated-collection ids. FILTER, verified. (comma-separated) |
| `deals` | no | Deal ids (sample: 81337 secret deals, 81485 50%+ discount, 119520 2X AgodaCash, 105046 AgodaVIP Deals). FILTER, verified. (comma-separated) |
| `hotelChainIds` | no | Hotel-chain ids (sample: 3 Accor). FILTER, verified via elasticFilters.terms; the alternate idsFilters(filterKey=HotelChainId) form reproducibly 502s server-side and is deliberately not used here. (comma-separated) |
| `productType` | no | Product-type ids (-1 all, 1 hotels, 2 homes). FILTER, verified. (comma-separated) |
| `bookingDuration` | no | Booking-duration type, lowercase only (the server 400s on uppercase). FILTER, verified ("hourly" -> 18 hotels in Bangkok). (one of `hourly`, `nightly`) |

Billed once per successful request (`hotel-count-request` event).

##### `GET /hotels/filters`

Fetches Agoda's aggregation.matrixGroupResults (the same data that powers the app's filter menu): per-group option ids/names/counts, optionally narrowed by the same filters as /hotels/search (so you can get "remaining options after applying these filters" counts). Uses pageSize=0 since hotel items are not needed.

| Param | Required | Description |
| --- | --- | --- |
| `searchType` | yes | Which Agoda destination-search variant to run (all 7 verified live). Each needs its own destination params: city->cityId, area->areaId+cityId, region->regionId, radius->lat+lon+distanceKm, box->topLeftLat/topLeftLon/bottomRightLat/bottomRightLon, landmark->landmarkId, propertySummary->propertyIds. Sending the wrong combination for the chosen type returns HTTP 400. (one of `city`, `area`, `region`, `radius`, `box`, `landmark`, `propertySummary`) |
| `cityId` | no | Agoda city id. Required for searchType=city; also required (together with areaId) for searchType=area. |
| `areaId` | no | Agoda area id. Required for searchType=area (together with cityId). |
| `regionId` | no | Agoda region id (its own id namespace, distinct from city/state ids). Required for searchType=region. |
| `landmarkId` | no | Agoda landmark id. Required for searchType=landmark. |
| `lat` | no | Latitude, decimal degrees. Required for searchType=radius. |
| `lon` | no | Longitude, decimal degrees. Required for searchType=radius. |
| `distanceKm` | no | Search radius in kilometres, 0-150 (Agoda rejects >150 with HTTP 400). Required for searchType=radius. |
| `topLeftLat` | no | Bounding-box top-left latitude. Required for searchType=box. |
| `topLeftLon` | no | Bounding-box top-left longitude. Required for searchType=box. |
| `bottomRightLat` | no | Bounding-box bottom-right latitude. Required for searchType=box. |
| `bottomRightLon` | no | Bounding-box bottom-right longitude. Required for searchType=box. |
| `propertyIds` | no | Property ids (searchType=propertySummary). (comma-separated) |
| `checkin` | yes | Check-in date. Format `YYYY-MM-DD`. |
| `checkout` | no | Check-out date. Alternative to los: if given, los is computed as checkout-checkin (los is ignored when checkout is present). Format `YYYY-MM-DD`. |
| `los` | no | Length of stay in nights, 1-30 (default 1). Ignored if checkout is given. Agoda accepts los>30 or los=0 but silently returns 0 hotels instead of erroring, so this is enforced here as a real HTTP 400. |
| `adults` | no | Adults per room (default 2). |
| `rooms` | no | Number of rooms (default 1). |
| `children` | no | Children per room (default 0). |
| `childAges` | no | Ages of each child, comma-separated. Padded with age 8 / truncated to match `children` if the count differs. (comma-separated) |
| `currency` | no | ISO currency code for prices. One of: AED, AFN, AMD, ARS, AUD, AWG, AZN, BBD, BGN, BHD, BIF, BMD, BND, BOB, BRL, BSD, BZD, CAD, CHF, CNY, CVE, CZK, DKK, DZD, EUR, FJD, GBP, GHS, HKD, IDR, INR, JPY, KRW, KYD, MYR, NOK, NZD, OMR, PAB, PHP, PLN, SEK, SGD, THB, TWD, USD, XOF, ZAR. (one of `AED`, `AFN`, `AMD`, `ARS`, `AUD`, `AWG`, `AZN`, `BBD`, `BGN`, `BHD`, `BIF`, `BMD`, `BND`, `BOB`, `BRL`, `BSD`, `BZD`, `CAD`, `CHF`, `CNY`, `CVE`, `CZK`, `DKK`, `DZD`, `EUR`, `FJD`, `GBP`, `GHS`, `HKD`, `IDR`, `INR`, `JPY`, `KRW`, `KYD`, `MYR`, `NOK`, `NZD`, `OMR`, `PAB`, `PHP`, `PLN`, `SEK`, `SGD`, `THB`, `TWD`, `USD`, `XOF`, `ZAR`) |
| `locale` | no | Response language as an Agoda locale, e.g. `th-th`, `ja-jp`, `zh-cn`, `en-us` (default). Not every locale is translated server-side; unsupported ones silently fall back to English. |
| `stars` | no | Star-rating buckets to include (1-5; 1 means <2 stars, the app's own convention), OR'd together. FILTER, verified in-range on every returned item. (comma-separated) |
| `reviewScoreMin` | no | Minimum guest review score, 0-10 scale. FILTER, verified. |
| `reviewScoreMax` | no | Maximum guest review score, 0-10 scale. FILTER, verified. |
| `priceMin` | no | Minimum per-night price in `currency`. FILTER on the returned items, but searchInfo.totalFilteredHotels can be stale/unreliable once this is set (verified: a (1000,5000) USD filter returned 0 items while totalFilteredHotels still said 5436) -- trust the items list, not the totals, whenever a price filter is active. |
| `priceMax` | no | Maximum per-night price in `currency`. Same totalFilteredHotels caveat as priceMin. |
| `name` | no | Filter by hotel name (server-side match). FILTER, verified ("Hilton" -> 10 hotels in Bangkok). |
| `freeCancellation` | no | Only hotels with a free-cancellation rate available. FILTER, verified. |
| `breakfastIncluded` | no | Only hotels with a breakfast-included room offer (maps to the verified roomBenefits id 78322). FILTER, verified. |
| `paymentOptions` | no | Payment-option ids, OR'd together (verified ids: 84640 pay now, 49499 free cancellation, 52770 book now pay later, 56649 pay at hotel, 80031 no credit card needed). FILTER, verified. (comma-separated) |
| `facilitiesAny` | no | Hotel-facility ids, matches hotels with ANY of them (OR; sample ids: 93 pool, 92 gym, 90 internet, 80 car park, 17 airport transfer, 116 24h front desk). FILTER, verified. (comma-separated) |
| `facilitiesAll` | no | Hotel-facility ids, matches hotels with ALL of them (AND). FILTER, verified (pool AND gym is narrower than pool OR gym). (comma-separated) |
| `accommodationTypes` | no | Accommodation-type ids (sample: 34 Hotel, 29 Apartment/Flat, 33 Hostel, 120 Serviced apartment, 131 Entire House, 32 Guesthouse/B\&B). FILTER, verified. (comma-separated) |
| `areaIds` | no | Restrict results to these area ids within the searched place (independent of searchType=area). FILTER, verified. (comma-separated) |
| `landmarkIds` | no | Restrict results to hotels near these landmark ids (independent of searchType=landmark). FILTER, verified. (comma-separated) |
| `affordableCategory` | no | Affordable-category ids (1 Budget, 2 Luxury). FILTER, verified. (comma-separated) |
| `bedrooms` | no | Number-of-bedrooms buckets (1, 2, 3=3+). FILTER, verified. (comma-separated) |
| `familyFacilities` | no | Family-facility ids. FILTER, verified. (comma-separated) |
| `hostLevel` | no | Host-level ids. FILTER, verified. (comma-separated) |
| `characteristics` | no | Property "characteristic" ids (sample: 1 Backpacker Favorite, 9 Transit, 10 Value For Money, 13 Shopping, 16 Sightseeing). FILTER, verified. (comma-separated) |
| `tripPurpose` | no | Trip-purpose ids. FILTER, verified. (comma-separated) |
| `curatedCollections` | no | Curated-collection ids. FILTER, verified. (comma-separated) |
| `deals` | no | Deal ids (sample: 81337 secret deals, 81485 50%+ discount, 119520 2X AgodaCash, 105046 AgodaVIP Deals). FILTER, verified. (comma-separated) |
| `hotelChainIds` | no | Hotel-chain ids (sample: 3 Accor). FILTER, verified via elasticFilters.terms; the alternate idsFilters(filterKey=HotelChainId) form reproducibly 502s server-side and is deliberately not used here. (comma-separated) |
| `productType` | no | Product-type ids (-1 all, 1 hotels, 2 homes). FILTER, verified. (comma-separated) |
| `bookingDuration` | no | Booking-duration type, lowercase only (the server 400s on uppercase). FILTER, verified ("hourly" -> 18 hotels in Bangkok). (one of `hourly`, `nightly`) |
| `groups` | no | Which matrix (filter-option) groups to fetch, comma-separated. Default (all verified-working groups): AccommodationType, AffordableCategory, AllGuestReviewBreakdown, AtmosphereIds, Characteristics, CuratedCollectionIds, Deals, CityCenterDistance, FamilyFacilities, GroupedBedTypes, HostLevel, HotelAreaId, HotelChainId, HotelCityId, HotelFacilities, IsSustainableTravel, KidsStayForFree, LandmarkIds, NoOfBathrooms, NoOfBedrooms, NumberOfBedrooms, StarRating, ReviewScore, RoomBenefits, PaymentOptions, RoomAmenities, TripPurpose, ReviewTopicIds, ReviewLocationScore, ProductType. (comma-separated) |
| `size` | no | Max options returned per group (default 50). The 200 cap is a client-side safety limit, not a documented Agoda limit. |

Billed once per successful request (`hotel-filters-request` event).

##### `GET /hotels/prices`

Looks up current prices for specific hotels via propertySummarySearch (verified: N ids -> N items; searchResult/totals are null for this search type, so no searchInfo is returned here).

| Param | Required | Description |
| --- | --- | --- |
| `propertyIds` | yes | Property ids to fetch prices for (required). (comma-separated) |
| `checkin` | yes | Check-in date. Format `YYYY-MM-DD`. |
| `checkout` | no | Check-out date. Alternative to los: if given, los is computed as checkout-checkin (los is ignored when checkout is present). Format `YYYY-MM-DD`. |
| `los` | no | Length of stay in nights, 1-30 (default 1). Ignored if checkout is given. Agoda accepts los>30 or los=0 but silently returns 0 hotels instead of erroring, so this is enforced here as a real HTTP 400. |
| `adults` | no | Adults per room (default 2). |
| `rooms` | no | Number of rooms (default 1). |
| `children` | no | Children per room (default 0). |
| `childAges` | no | Ages of each child, comma-separated. Padded with age 8 / truncated to match `children` if the count differs. (comma-separated) |
| `currency` | no | ISO currency code for prices. One of: AED, AFN, AMD, ARS, AUD, AWG, AZN, BBD, BGN, BHD, BIF, BMD, BND, BOB, BRL, BSD, BZD, CAD, CHF, CNY, CVE, CZK, DKK, DZD, EUR, FJD, GBP, GHS, HKD, IDR, INR, JPY, KRW, KYD, MYR, NOK, NZD, OMR, PAB, PHP, PLN, SEK, SGD, THB, TWD, USD, XOF, ZAR. (one of `AED`, `AFN`, `AMD`, `ARS`, `AUD`, `AWG`, `AZN`, `BBD`, `BGN`, `BHD`, `BIF`, `BMD`, `BND`, `BOB`, `BRL`, `BSD`, `BZD`, `CAD`, `CHF`, `CNY`, `CVE`, `CZK`, `DKK`, `DZD`, `EUR`, `FJD`, `GBP`, `GHS`, `HKD`, `IDR`, `INR`, `JPY`, `KRW`, `KYD`, `MYR`, `NOK`, `NZD`, `OMR`, `PAB`, `PHP`, `PLN`, `SEK`, `SGD`, `THB`, `TWD`, `USD`, `XOF`, `ZAR`) |
| `locale` | no | Response language as an Agoda locale, e.g. `th-th`, `ja-jp`, `zh-cn`, `en-us` (default). Not every locale is translated server-side; unsupported ones silently fall back to English. |

Billed per item returned (`hotel-price-result` event); an empty result is free.

#### Property

##### `GET /properties/details`

Full content detail for up to 32 properties in one call, via Agoda's propertyDetailsSearch GraphQL op. Verified live: content does NOT depend on dates or currency (OccupancyRequest is only echoed back, never used to filter or price content -- there are no prices in this operation at all); checkin/los/adults/children/rooms/currency are accepted only for request-signature symmetry with /properties/:id/rooms.

| Param | Required | Description |
| --- | --- | --- |
| `propertyIds` | yes | Comma-separated Agoda property ids, max 32 per request (33+ is rejected -- verified Agoda API limit). (comma-separated) |
| `checkin` | no | Format `YYYY-MM-DD`. Echoed only, INERT for content; accepted for symmetry with /rooms. |
| `los` | no | Length of stay in nights. Echoed only, INERT for content. Default 1. |
| `adults` | no | Adults. Echoed only, INERT for content. Default 2. |
| `children` | no | Children count. Echoed only, INERT for content. Default 0. |
| `rooms` | no | Room count. Accepted for symmetry with /rooms; unused by this operation. Default 1. |
| `currency` | no | Currency code. Accepted for symmetry with /rooms; this operation returns no prices so it is currently inert. One of: AED, AFN, AMD, ARS, AUD, AWG, AZN, BBD, BGN, BHD, BIF, BMD, BND, BOB, BRL, BSD, BZD, CAD, CHF, CNY, CVE, CZK, DKK, DZD, EUR, FJD, GBP, GHS, HKD, IDR, INR, JPY, KRW, KYD, MYR, NOK, NZD, OMR, PAB, PHP, PLN, SEK, SGD, THB, TWD, USD, XOF, ZAR. (one of `AED`, `AFN`, `AMD`, `ARS`, `AUD`, `AWG`, `AZN`, `BBD`, `BGN`, `BHD`, `BIF`, `BMD`, `BND`, `BOB`, `BRL`, `BSD`, `BZD`, `CAD`, `CHF`, `CNY`, `CVE`, `CZK`, `DKK`, `DZD`, `EUR`, `FJD`, `GBP`, `GHS`, `HKD`, `IDR`, `INR`, `JPY`, `KRW`, `KYD`, `MYR`, `NOK`, `NZD`, `OMR`, `PAB`, `PHP`, `PLN`, `SEK`, `SGD`, `THB`, `TWD`, `USD`, `XOF`, `ZAR`) |
| `locale` | no | Response language as an Agoda locale, e.g. `th-th`, `ja-jp`, `zh-cn`, `en-us` (default). Not every locale is translated server-side; unsupported ones silently fall back to English. |
| `blocks` | no | Comma-separated content blocks to include. One of: summary, information, features, images, highlights, localInformation, experiences, engagement, reviewScore, reviewSummaries (default: all of these). `hostProfile` is NOT selectable here -- it is PII-gated, see `includePii`. (comma-separated) |
| `includePii` | no | Opt-in for personally-identifying fields: hostProfile (name, photo, joined date, city/state -- only ever populated for home/NHA listings, always null on hotels per the verified contract), contentReviewSummaries snippet reviewer first names, and hotel-image uploader member names. Default false (all excluded). |
| `imagesLimit` | no | Max rows requested via the images block's page size. Agoda always appends ~5 placeholder "nearby attraction" rows, so the real image count returned is up to imagesLimit-5. Only applies when the `images` block is requested (default blocks). Default 50. |

Billed per item returned (`property-detail` event); an empty result is free.

##### `GET /properties/:id/rooms`

Room types and their bookable rate plans ("offers") for one property/stay, via the room-grid REST API (the backend of the app's room-grid screen). One dataset item per offer/rate-plan, not per room type. IMPORTANT: prices are verified NOT deterministic between identical calls (server-side sampled quote, seen to vary offer count and price within seconds of the same request) -- treat each response as a point-in-time snapshot, not a stable price to diff against a previous call.

| Param | Required | Description |
| --- | --- | --- |
| `id` | yes | Agoda property id. Verified-live examples: 14654101, 1254412, 10629, 70837, 546811, 10636, 48488. |
| `checkin` | yes | Stay start date. Format `YYYY-MM-DD`. |
| `los` | no | Length of stay in nights. Default 1. Note: some properties cap los at a per-property max stay (verified: los>=7 returns 0 rooms on some hotels, not others). |
| `adults` | no | Adults per room. Default 2. |
| `rooms` | no | Number of rooms to book. Default 1. |
| `currency` | no | Currency for prices. One of: AED, AFN, AMD, ARS, AUD, AWG, AZN, BBD, BGN, BHD, BIF, BMD, BND, BOB, BRL, BSD, BZD, CAD, CHF, CNY, CVE, CZK, DKK, DZD, EUR, FJD, GBP, GHS, HKD, IDR, INR, JPY, KRW, KYD, MYR, NOK, NZD, OMR, PAB, PHP, PLN, SEK, SGD, THB, TWD, USD, XOF, ZAR. (one of `AED`, `AFN`, `AMD`, `ARS`, `AUD`, `AWG`, `AZN`, `BBD`, `BGN`, `BHD`, `BIF`, `BMD`, `BND`, `BOB`, `BRL`, `BSD`, `BZD`, `CAD`, `CHF`, `CNY`, `CVE`, `CZK`, `DKK`, `DZD`, `EUR`, `FJD`, `GBP`, `GHS`, `HKD`, `IDR`, `INR`, `JPY`, `KRW`, `KYD`, `MYR`, `NOK`, `NZD`, `OMR`, `PAB`, `PHP`, `PLN`, `SEK`, `SGD`, `THB`, `TWD`, `USD`, `XOF`, `ZAR`) |
| `locale` | no | Response language as an Agoda locale, e.g. `th-th`, `ja-jp`, `zh-cn`, `en-us` (default). Not every locale is translated server-side; unsupported ones silently fall back to English. |
| `childrenAges` | no | Comma-separated ages of children (e.g. 1,5,12,17), affects offers/pricing. (comma-separated) |

Billed per item returned (`property-room-offer` event); an empty result is free.

##### `GET /properties/:id/reviews`

Guest reviews via the cronos ReviewComments REST API (Agoda + Booking.com providers merged unless `provider` is set). Two server modes: "simple" (no language/room filter) honours `pageSize` per provider (up to 100 Agoda + 100 Booking.com per page); "filter" mode (auto-enabled by `languageIds`/`roomTypeIds`) ignores `pageSize` and returns a server-fixed ~50-70 items per page. VERIFIED PAGINATION QUIRK: the server reports a total review count that undercounts the real end of the list by roughly 10%, and pages past the real end return stale/repeated/out-of-order items instead of an error -- callers must stop paging on an empty page or a page whose review ids were already seen, not by trusting the reported total.

| Param | Required | Description |
| --- | --- | --- |
| `id` | yes | Agoda property id. Verified-live examples: 14654101, 1254412, 10629, 70837, 546811, 10636, 48488. |
| `page` | no | 1-based page number (default 1). |
| `pageSize` | no | Reviews per provider per page (simple mode only; ignored in filter mode). Max 100 (>=200 returns 0 items). Default 20. |
| `sort` | no | Sort order. Default recent. (one of `recent`, `highest`, `lowest`, `helpful`) |
| `traveler` | no | Traveler-group demographic filter. Default all. (one of `all`, `business`, `couple`, `solo`, `family_young_children`, `family_teens`, `group`) |
| `languageIds` | no | Filter to these review-language ids (ids come from this endpoint's own filtersAvailable.Language). Switches to filter mode (see description). (comma-separated) |
| `roomTypeIds` | no | Filter to these room-type ids (ids match /properties/:id/rooms' roomTypeId). Switches to filter mode (see description). (comma-separated) |
| `provider` | no | Keep only this provider's reviews (client-side filter on the merged page). Omit for both. (one of `agoda`, `booking.com`) |
| `locale` | no | Response language as an Agoda locale, e.g. `th-th`, `ja-jp`, `zh-cn`, `en-us` (default). Not every locale is translated server-side; unsupported ones silently fall back to English. |
| `includeReviewerName` | no | Opt-in for PII: reviewer display name, reviewer-uploaded photos, and the hotel's response text (which sometimes greets the guest by name, e.g. "Dear Mr/Ms Doreen"). Default false (all three excluded). |

Billed per item returned (`property-review` event); an empty result is free.

##### `GET /properties/:id/review-scores`

Aggregate review score: combined score, per-provider score with category grades (cleanliness, location, staff, value, facilities) broken down by traveler-group demographic, plus recommendation snippets. Same GraphQL op as /properties/details, restricted to the two review blocks. Billed per request (unit `request`), not per item, since the response is a single score object rather than a list.

| Param | Required | Description |
| --- | --- | --- |
| `id` | yes | Agoda property id. Verified-live examples: 14654101, 1254412, 10629, 70837, 546811, 10636, 48488. |
| `locale` | no | Response language as an Agoda locale, e.g. `th-th`, `ja-jp`, `zh-cn`, `en-us` (default). Not every locale is translated server-side; unsupported ones silently fall back to English. |
| `providerIds` | no | Restrict to these review provider ids: 332 = Agoda (default provider), 3038 = Booking.com. Omit for all providers. (comma-separated) |
| `includePii` | no | Opt-in for PII: contentReviewSummaries snippet reviewer first names. Default false (excluded). Not part of the original endpoint plan but required by this project's standing PII opt-in rule. |

Billed once per successful request (`property-review-scores` event).

#### Flights

##### `GET /flights/airports`

Autocomplete airports and cities by name or IATA code, for use as `origin`/`destination` on the other flight endpoints. Up to 10 suggestions, empty list for unknown text (never an error).

| Param | Required | Description |
| --- | --- | --- |
| `q` | yes | Search text: city/airport name or IATA code. |
| `locale` | no | Response language as an Agoda locale, e.g. `th-th`, `ja-jp`, `zh-cn`, `en-us` (default). Not every locale is translated server-side; unsupported ones silently fall back to English. |

Billed per item returned (`flight-airport-suggestion` event); an empty result is free.

##### `GET /flights/search`

Search flights between two airports/cities. Each call polls the upstream (same request body) up to 5 times until results are complete, then returns one page (up to 20 itineraries); use `page` for more pages. A bad/unresolvable route never completes upstream -- after 5 polls the call returns whatever partial data exists with `is_completed: false`, rather than hanging or erroring. Upstream result filters (stops, airlines, departure/arrival time, duration, price) and upstream sort are validated but NOT applied server-side (confirmed by live diff testing) and are therefore not exposed here. `sort` is applied client-side on the returned page only -- it does NOT re-sort the full result set across pages.

| Param | Required | Description |
| --- | --- | --- |
| `origin` | yes | Origin. 3-letter IATA airport or city code, e.g. `CGK`. |
| `destination` | yes | Destination. 3-letter IATA airport or city code, e.g. `SIN`. |
| `departDate` | yes | Departure date. Format `YYYY-MM-DD`. |
| `returnDate` | no | Return date -- adds a return slice (round trip) when given. Format `YYYY-MM-DD`. |
| `adults` | no | Adult passengers (default 1). |
| `children` | no | Child passengers (default 0). Children-only searches return 0 results. |
| `infants` | no | Lap-infant passengers (default 0). |
| `cabin` | no | Cabin class. One of: Economy, PremiumEconomy, Business, First. Default Economy. (one of `Economy`, `PremiumEconomy`, `Business`, `First`) |
| `currency` | no | Prices are returned only in this currency. One of: AED, AFN, AMD, ARS, AUD, AWG, AZN, BBD, BGN, BHD, BIF, BMD, BND, BOB, BRL, BSD, BZD, CAD, CHF, CNY, CVE, CZK, DKK, DZD, EUR, FJD, GBP, GHS, HKD, IDR, INR, JPY, KRW, KYD, MYR, NOK, NZD, OMR, PAB, PHP, PLN, SEK, SGD, THB, TWD, USD, XOF, ZAR. (one of `AED`, `AFN`, `AMD`, `ARS`, `AUD`, `AWG`, `AZN`, `BBD`, `BGN`, `BHD`, `BIF`, `BMD`, `BND`, `BOB`, `BRL`, `BSD`, `BZD`, `CAD`, `CHF`, `CNY`, `CVE`, `CZK`, `DKK`, `DZD`, `EUR`, `FJD`, `GBP`, `GHS`, `HKD`, `IDR`, `INR`, `JPY`, `KRW`, `KYD`, `MYR`, `NOK`, `NZD`, `OMR`, `PAB`, `PHP`, `PLN`, `SEK`, `SGD`, `THB`, `TWD`, `USD`, `XOF`, `ZAR`) |
| `locale` | no | Response language as an Agoda locale, e.g. `th-th`, `ja-jp`, `zh-cn`, `en-us` (default). Not every locale is translated server-side; unsupported ones silently fall back to English. |
| `page` | no | 1-based page number (default 1). |
| `sort` | no | Client-side sort of the returned page only (see description above -- upstream sort is ignored). One of: price\_asc, price\_desc, duration\_asc, duration\_desc. (one of `price_asc`, `price_desc`, `duration_asc`, `duration_desc`) |

Billed per item returned (`flight-itinerary` event); an empty result is free.

##### `GET /flights/price-calendar`

Cheapest cached price per departure date, one call returns up to ~352 day rows (about a year ahead of today). Prices are cached "cheapest seen" values, not live availability.

| Param | Required | Description |
| --- | --- | --- |
| `origin` | yes | Origin. 3-letter IATA airport or city code, e.g. `CGK`. |
| `destination` | yes | Destination. 3-letter IATA airport or city code, e.g. `SIN`. |
| `cabin` | no | Cabin class. One of: Economy, PremiumEconomy, Business, First. Default Economy. (one of `Economy`, `PremiumEconomy`, `Business`, `First`) |
| `tripType` | no | One of: OneWay, RoundTrip. Default OneWay. (one of `OneWay`, `RoundTrip`) |
| `tripDuration` | no | Round-trip length in nights. Only meaningful when tripType=RoundTrip (changes which days are returned). |
| `currency` | no | ISO currency code for prices. One of: AED, AFN, AMD, ARS, AUD, AWG, AZN, BBD, BGN, BHD, BIF, BMD, BND, BOB, BRL, BSD, BZD, CAD, CHF, CNY, CVE, CZK, DKK, DZD, EUR, FJD, GBP, GHS, HKD, IDR, INR, JPY, KRW, KYD, MYR, NOK, NZD, OMR, PAB, PHP, PLN, SEK, SGD, THB, TWD, USD, XOF, ZAR. (one of `AED`, `AFN`, `AMD`, `ARS`, `AUD`, `AWG`, `AZN`, `BBD`, `BGN`, `BHD`, `BIF`, `BMD`, `BND`, `BOB`, `BRL`, `BSD`, `BZD`, `CAD`, `CHF`, `CNY`, `CVE`, `CZK`, `DKK`, `DZD`, `EUR`, `FJD`, `GBP`, `GHS`, `HKD`, `IDR`, `INR`, `JPY`, `KRW`, `KYD`, `MYR`, `NOK`, `NZD`, `OMR`, `PAB`, `PHP`, `PLN`, `SEK`, `SGD`, `THB`, `TWD`, `USD`, `XOF`, `ZAR`) |

Billed per item returned (`flight-calendar-day` event); an empty result is free.

##### `GET /flights/flexible-dates`

Cheapest cached price for the given departure date and the 2 days before/after (5 rows). For round trips the return date shifts by the same offset as the departure date.

| Param | Required | Description |
| --- | --- | --- |
| `origin` | yes | Origin. 3-letter IATA airport or city code, e.g. `CGK`. |
| `destination` | yes | Destination. 3-letter IATA airport or city code, e.g. `SIN`. |
| `departDate` | yes | Departure date (center of the +/-2 day window). Format `YYYY-MM-DD`. |
| `returnDate` | no | Return date -- round trip when given (shifts with departDate). Format `YYYY-MM-DD`. |
| `cabin` | no | Cabin class. One of: Economy, PremiumEconomy, Business, First. Default Economy. (one of `Economy`, `PremiumEconomy`, `Business`, `First`) |
| `currency` | no | ISO currency code for prices. One of: AED, AFN, AMD, ARS, AUD, AWG, AZN, BBD, BGN, BHD, BIF, BMD, BND, BOB, BRL, BSD, BZD, CAD, CHF, CNY, CVE, CZK, DKK, DZD, EUR, FJD, GBP, GHS, HKD, IDR, INR, JPY, KRW, KYD, MYR, NOK, NZD, OMR, PAB, PHP, PLN, SEK, SGD, THB, TWD, USD, XOF, ZAR. (one of `AED`, `AFN`, `AMD`, `ARS`, `AUD`, `AWG`, `AZN`, `BBD`, `BGN`, `BHD`, `BIF`, `BMD`, `BND`, `BOB`, `BRL`, `BSD`, `BZD`, `CAD`, `CHF`, `CNY`, `CVE`, `CZK`, `DKK`, `DZD`, `EUR`, `FJD`, `GBP`, `GHS`, `HKD`, `IDR`, `INR`, `JPY`, `KRW`, `KYD`, `MYR`, `NOK`, `NZD`, `OMR`, `PAB`, `PHP`, `PLN`, `SEK`, `SGD`, `THB`, `TWD`, `USD`, `XOF`, `ZAR`) |

Billed per item returned (`flight-flex-date` event); an empty result is free.

##### `GET /flights/amenities`

Amenities (aircraft, wifi, power, entertainment, food/beverage, seat layout) for one flight segment. Optional enrichment endpoint -- feed it a segment from a `/flights/search` result (its `segments[]` entries give origin/destination/depart/flight\_number/carrier\_code/cabin\_code). For a multi-segment itinerary, call this once per segment.

| Param | Required | Description |
| --- | --- | --- |
| `origin` | yes | Segment origin airport. 3-letter IATA airport or city code, e.g. `CGK`. |
| `destination` | yes | Segment destination airport. 3-letter IATA airport or city code, e.g. `DPS`. |
| `departDateTime` | yes | Local departure date-time, no timezone, `YYYY-MM-DDTHH:mm:ss` (as returned by /flights/search segments\[].depart). |
| `flightNumber` | yes | Digits-only flight number (no carrier prefix). |
| `carrierCode` | yes | 2-letter IATA carrier code. |
| `cabinClass` | yes | Cabin class code as returned by /flights/search segments\[].cabin\_code. (one of `ECO`, `PEO`, `BIZ`, `FST`) |
| `locale` | no | Response language as an Agoda locale, e.g. `th-th`, `ja-jp`, `zh-cn`, `en-us` (default). Not every locale is translated server-side; unsupported ones silently fall back to English. |

Billed per item returned (`flight-amenity-segment` event); an empty result is free.

### Pricing

Pay-per-event (PPE), tiered by Apify plan (FREE/BRONZE/SILVER/GOLD/PLATINUM/DIAMOND
\= 100/92/85/78/72/68% of the FREE price). See the Actor's Pricing tab for exact
current prices.

### Known limitations

- Some `/hotels/search` sort fields and most server-side price filters are
  unreliable on Agoda's own API and are documented per-endpoint rather than
  silently exposed.
- `/properties/details` accepts at most 32 property ids per call (Agoda's own
  limit).
- `/properties/:id/reviews` pagination cycles after roughly
  `total / pageSize + 10%` pages — stop when a page comes back empty or
  repeats a review id already seen.
- `/flights/search` polls Agoda's own async search up to 5 times; a route
  with no real flights (bad IATA code, etc.) returns `is_completed: false`
  with whatever partial data exists rather than hanging.

### Getting started

```bash
npm install
npm run start:dev
```

# Actor input Schema

## Actor input object example

```json
{}
```

# Actor output Schema

## `info` (type: `string`):

No description

# 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 = {};

// Run the Actor and wait for it to finish
const run = await client.actor("romy/agoda-all-in-one-api").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 = {}

# Run the Actor and wait for it to finish
run = client.actor("romy/agoda-all-in-one-api").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 '{}' |
apify call romy/agoda-all-in-one-api --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,romy/agoda-all-in-one-api"
        }
    }
}
```

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/rTTer9hgyUjIWsocc/builds/BDyMrIPlOlYdBLy63/openapi.json
