3. API Reference
HotelList
POST /api/search/hotelList
Returns hotels matching a set of search criteria — destination, stay dates and guest configuration — with optional per-hotel room availability and pricing.
Three ways to drive the search are supported:
- By specific SupplierHotelList — pass explicit hotel IDs to fetch detail for exactly those properties.
- By destination — search within a region, addressed either by
destinationIdor bydestinationName. - By location — return hotels near a point, ranked by distance.
Required parameters
checkIn/checkOut— the stay dates.roomOccupancies— one entry per room, each carryingadultCountand optionalchildrenAges. At least one entry is required.- at least one of
destinationId,destinationNameorhotelIds— how the search is scoped. A body carrying none of the three is rejected.
Optional parameters
destinationId— region identifier for a location-scoped search.destinationName— region name; the server resolves it to an ID.hotelIds— explicit hotel IDs to retrieve.maxRatesPerHotel— cap on room rates returned per hotel; 0 (the default) is uncapped.sortBy— result ordering (price-asc, price-desc, rating-desc, distance-asc).pageNum/pageSize/cursor— pagination controls. They are flat top-level fields on this endpoint; the nestedpageobject belongs to HotelsMetadata, not here.pageSizedefaults to 50 and is capped at 200; a larger value is rejected rather than clamped.
What comes back
- A hotel list carrying core property information.
- Room availability and pricing, when requested.
- Pagination metadata for large result sets.
- A session identifier that ties the search context together.
basic.statusRemark/basic.notices/basic.emptyReason— why a result is empty or incomplete. See "Empty results" below.
Empty results
{ "list": [], "total": 0 } has two causes that need opposite responses, and on its own it cannot tell you which you have hit:
- The suppliers were asked and had nothing. Genuine zero availability — try other dates or another property.
basic.emptyReasonis ABSENT. - No supplier answered, or none was asked at all. A configuration state on our side. Retrying the same request, or changing the dates or the destination, cannot change it.
basic.emptyReason.codeis present and says which.
basic.emptyReason.code is one of:
NO_SUPPLIER_ENABLED— no supplier is currently enabled for hotel search.NO_SUPPLIER_PERMITTED— suppliers are enabled, but none may serve your account on this search.ALL_SUPPLIERS_EXCLUDED— every candidate was excluded before the search ran, by a market, currency, language or inventory restriction.NO_SUPPLIER_ANSWERED— suppliers were called and every one of them failed. This is the only one of the four worth retrying.
basic.statusRemark carries the same thing as one human-readable sentence, suitable for a log line or an operator-facing message.
basic.emptyReason.suppliers — WHICH supplier failed and why — is disclosed only to accounts entitled to the supplier breakdown. Most integrations will never see it, and its absence is not an error: the code and the statusRemark are complete without it.
basic.notices is separate and belongs to a search that WORKED: it lists what the response does not contain when the result is real but not complete (a destination expansion that hit its cap, properties dropped because they could not be priced in the requested currency). It is not emitted alongside emptyReason — on an empty response there is no partial result for it to qualify, and statusRemark already carries the explanation.
All four fields are additive and optional. A client that ignores them behaves exactly as it did before they existed.
Typical uses
- Powering a hotel booking search UI.
- Browsing the inventory of a destination.
- Looking up one or more known properties.
- Suggesting hotels near a given point.
Request headers
| Header | Required | Description |
|---|---|---|
Client-Request-Timestamp |
No | Unix timestamp recorded by the client at the moment it begins sending the request. |
Currency |
No | Currency the rates should be quoted in, expressed as an ISO 4217 code (https://en.wikipedia.org/wiki/ISO_4217). |
IP |
No | Client IP address; both IPv4 and IPv6 are accepted. |
Language |
No | IETF BCP 47 language tag (https://masonreview.com/posts/ietf-bcp-47-language-tags/), for example "en" or "en-US". Defaults to "en-US". |
Market |
No | Point-of-sale market for this call, as an ISO 3166-1 alpha-2 country code. Required only when your account carries a market restriction: an allow-list cannot admit a market you have not stated, so such an account refuses calls that omit it. |
Request-Id |
No | Identifies this individual request; values must not repeat. |
Session-Id |
Yes | Recommended to be supplied by the client on every call, and mandatory throughout the booking flow. |
Test |
No | Test switches, given as key-value pairs — for example "hotel=HC1&scenario=priceChange". A value the server does not recognise is ignored, and the call proceeds exactly as if no "Test" header had been sent. |
Timeout-Milliseconds |
No | Client-imposed timeout; the server makes a best effort to respond within it. |
Trace-Id |
No | Correlates a group of related requests for tracing. |
User-Ref |
No | Your own identifier for the person on whose behalf this call is made. Required only once named users have been authorised on your account, after which every call must identify one and is authorised against that user's scopes as well as the key's. |
Authorization |
Yes | Must begin with "Bearer ". The token is obtained from the Ticket response; the Ticket call itself does not require this header. |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
hotelIds[] |
array |
No | Explicit hotel IDs to search for; at most 50 entries. A longer array is rejected with code 100000400 — split it across several requests. |
maxRatesPerHotel |
integer | No | How many room rates to return for each hotel, ordered by price with the cheapest first. 0 (the default) returns every rate uncapped; use 1 to return only the cheapest rate per hotel, which suits listing pages. |
checkIn |
string | Yes | Check-in date; defaults to the current date. |
checkOut |
string | Yes | Check-out date; defaults to seven days from now. |
countryCode |
string | No | Country code of the booker's point of sale, in ISO 3166-1 alpha-2 form (e.g. "US"). |
residencyCode |
string | No | Booker's residency code, in ISO 3166-1 alpha-2 form (e.g. "US"). |
nationalityCode |
string | No | Booker's nationality code, in ISO 3166-1 alpha-2 form (e.g. "US"). |
roomOccupancies[] |
array<hotel.supplier.domain.GuestPerRoom> | Yes | |
roomOccupancies[].adultCount |
integer | Yes | AdultCount is the number of adults |
roomOccupancies[].childrenAges[] |
array |
No | ChildrenAges contains ages of children |
destinationId |
string | No | Region identifier. Required when destinationName is not supplied. |
destinationName |
string | No | Region name. Required when destinationId is not supplied; the system automatically looks up cities matching this name. |
pageNum |
integer | No | Page number. The first page is 1; values below 1 are rejected. |
pageSize |
integer | No | Page size. Defaults to 50 when omitted, and may not exceed 200 — a larger value is rejected with code 100000400 rather than being silently clamped. |
cursor |
integer | No | Cursor pointing at the next page. |
sortBy |
string | No | Result ordering. Documented values: price-asc, price-desc, rating-desc, distance-asc. (The source types this as a free-form string — no enum constraint is declared.) |
Example request
{
"hotelIds": [
"461850557",
"118062388"
],
"maxRatesPerHotel": 3,
"checkIn": "2026-01-01",
"checkOut": "2026-01-03",
"countryCode": "US",
"residencyCode": "US",
"nationalityCode": "US",
"roomOccupancies": [
{
"adultCount": 2,
"childrenAges": [
2
]
}
],
"destinationId": "804028047",
"destinationName": "Dubai",
"pageNum": 1,
"pageSize": 50,
"cursor": 0,
"sortBy": "price-desc"
}Response
Successful calls return the standard envelope. code is 0 on success — check it rather than the HTTP status alone, because some business failures are delivered with HTTP 200.
| Field | Type | Required | Description |
|---|---|---|---|
code |
integer | No | |
msg |
string | No | |
data |
hotel.search.protocol.HotelListResp | No | |
data.list[] |
array |
Yes | The hotels matched by the search. |
data.list[].id |
string | No | |
data.list[].destinationId |
string | No | index |
data.list[].name |
I18N | No | Hotel name. |
data.list[].name.en |
string | No | English; the default system language. |
data.list[].name.zh |
string | No | Chinese (中文). |
data.list[].name.ar |
string | No | Arabic (اللغة العربية). |
data.list[].rating |
number | No | Hotel rating, on a scale from 0.0 to 5.0. (The range is documented in prose only; the source declares no minimum/maximum keywords.) |
data.list[].address |
I18N | No | Localised string set. Keys are fixed language codes. |
data.list[].address.en |
string | No | English; the default system language. |
data.list[].address.zh |
string | No | Chinese (中文). |
data.list[].address.ar |
string | No | Arabic (اللغة العربية). |
data.list[].latlngCoordinator |
LatlngCoordinator | No | |
data.list[].latlngCoordinator.google |
Latlng | Yes | |
data.list[].latlngCoordinator.gaode |
Latlng | No | |
data.list[].logoURL |
string | No | |
data.list[].minPrice |
Money | No | The lowest price satisfying the search criteria. |
data.list[].minPrice.currency |
string | Yes | Currency code, e.g. "USD". |
data.list[].minPrice.amount |
number | Yes | Monetary amount, e.g. "14.50". |
data.list[].minPricesByCurrency[] |
array |
No | Lowest prices expressed in a range of currencies. |
data.list[].minPricesByCurrency[].currency |
string | Yes | Currency code, e.g. "USD". |
data.list[].minPricesByCurrency[].amount |
number | Yes | Monetary amount, e.g. "14.50". |
data.list[].isAvailable |
boolean | No | |
data.list[].rooms[] |
array |
No | |
data.list[].rooms[].roomTypeId |
string | Yes | Standardized room type ID, e.g. "R001". |
data.list[].rooms[].roomTypeName |
I18N | Yes | Standardized room type name. When the supplier's type cannot be recognised, a fallback such as "Standard Room" is returned. |
data.list[].rooms[].hotelId |
string | Yes | Hotel ID. Repeats the parent hotel's ID so the room can be identified on its own. |
data.list[].rooms[].rates[] |
array<hotel.supplier.domain.RoomRatePkg> | No | The core structure — the room offers available for this room. |
data.basic |
HotelListBasicInfo | Yes | Contextual information about the search itself. |
data.basic.destination |
Destination | No | Details of the region the search covered. |
data.basic.destination.id |
string | No | |
data.basic.destination.type |
hotel.geography.domain.DestinationType | No | Classification of a destination record. - DestinationType_Unknown (0): no description supplied in the source. - DestinationType_Continent (1): a large landmass such as Europe, Asia or South America. The region summary section lists the continents. - DestinationType_Country (2): a nation-state such as the United States, Australia or Germany. Countries/regions here are drawn geographically rather than politically - Guadeloupe, for instance, is a French department in the Caribbean that belongs politically to France yet forms its own geographic entity. The region summary section lists the countries/regions. - DestinationType_ProvinceState (3): an administrative division inside a country, such as California or New South Wales. Lower-level administrative divisions - counties and provinces within a country/region - fall under this type too. - DestinationType_HighLevelRegion (4): a tourist region, such as the Upper Rhine in Germany or the wine country in California. - DestinationType_MultiCityVicinity (5): a large metropolitan area covering a major city together with its surrounding suburbs, such as Cairo and the areas around it. Polygon boundaries for these are drawn broadly according to tourist value. - DestinationType_City (6): an urban area, such as Bellevue and Washington. A large metropolitan area can contain a city - London, for example - in which case the city covers a smaller footprint than the corresponding multi_city_vicinity. Each accommodation contracted with Expedia must be tied to a city, since the city is the base unit of geographic coverage. City polygon boundaries follow official administrative sources. - DestinationType_Neighborhood (7): a geographic area smaller than a city, such as the old town in Bonn, Germany. Neighborhood polygon boundaries are drawn by tourist value rather than by administrative definition. - DestinationType_Airport (8): an airport, such as LHR (London Heathrow). - DestinationType_PointOfInterest (9): a place such as a museum, convention center, beach or park. The list covers not only well-known tourist attractions but a broader set of venues, such as the Shanghai Exhibition Center, the Texas State Capitol or the Prague Zoo. - DestinationType_TrainStation (10): a train station, such as Grand Central Terminal in New York. - DestinationType_MetroStation (11): a subway station, such as Avtovo Station in St. Petersburg, Russia. - DestinationType_BusStation (12): a bus station, such as the Port Authority Bus Terminal in New York State, USA. Allowed: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12. |
data.basic.destination.name |
I18N | No | Localised name. |
data.basic.destination.nameFull |
I18N | No | Fully qualified localised name, for example "Springfield, Missouri, United States of America". |
data.basic.destination.countryCode |
string | No | ISO 3166-1 alpha-2 country code. Reference: https://www.iso.org/obp/ui/#search |
data.basic.destination.coordinates |
Coordinates | No | Location descriptor. |
data.basic.destination.extra |
hotel.geography.domain.DestinationExtra | No | |
data.basic.destination.countryName |
I18N | No | Localised string set. Keys are fixed language codes. |
data.basic.destination.destinationName |
I18N | No | Localised string set. Keys are fixed language codes. |
data.basic.destination.parentDestinationName |
I18N | No | Localised string set. Keys are fixed language codes. |
data.basic.destination.parentDestinationId |
string | No | Code of the parent region: for a city this is the province code, for a province it is the country code. |
data.basic.sessionId |
string | No | The session ID. |
data.basic.statusRemark |
string | No | One human-readable sentence explaining a result that is empty or incomplete. Uses the same field name the contract already gives the advisory text beside status on CheckAvail and on every order. Absent when there is nothing to explain. Additive and optional. |
data.basic.notices[] |
array |
No | What this response does NOT contain, when the search succeeded but is not complete — a destination expansion that hit its cap, properties dropped because they could not be priced in the requested currency. Belongs to a result that HAS content; never emitted alongside emptyReason. Additive and optional. |
data.basic.emptyReason |
HotelListEmptyReason | No | Machine-readable diagnosis of an empty list. Present ONLY when the emptiness is a platform or configuration state; absent when the suppliers were asked and simply had nothing. Additive and optional. |
data.basic.emptyReason.code |
enum | Yes | NO_SUPPLIER_ENABLED — nothing is enabled for hotel search. NO_SUPPLIER_PERMITTED — suppliers exist, but none may serve your account on this search. ALL_SUPPLIERS_EXCLUDED — every candidate was excluded before the search ran. NO_SUPPLIER_ANSWERED — suppliers were called and all of them failed; this is the only one of the four worth retrying. Published to every caller — it names no supplier. Allowed: NO_SUPPLIER_ENABLED, NO_SUPPLIER_PERMITTED, ALL_SUPPLIERS_EXCLUDED, NO_SUPPLIER_ANSWERED. |
data.basic.emptyReason.suppliers[] |
array |
No | Per-supplier detail — which supplier produced nothing, and why. Disclosed ONLY to accounts entitled to the supplier breakdown; most integrations will never receive this property, and its absence is not an error. code and statusRemark are complete without it. |
data.total |
integer | No | Total number of items. A value of 0 means either that there are no items or that totals are not supported for this query. |
data.hasMore |
boolean | No | Whether further items remain to be fetched. |
Failure responses
| HTTP | Meaning |
|---|---|
| 400 | Bad Request |
| 401 | Unauthorized |
| 403 | Forbidden |
| 429 | Too Many Requests |
Every failure uses the { code, msg } envelope. See Error handling for the full business-code table.
Code samples
curl -X POST https://api-test.ttdbooking.com/api/search/hotelList \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $TICKET" \
-H "Request-Id: $(uuidgen)" \
-d '{
"hotelIds": [
"461850557",
"118062388"
],
"maxRatesPerHotel": 3,
"checkIn": "2026-01-01",
"checkOut": "2026-01-03",
"countryCode": "US",
"residencyCode": "US",
"nationalityCode": "US",
"roomOccupancies": [
{
"adultCount": 2,
"childrenAges": [
2
]
}
],
"destinationId": "804028047",
"destinationName": "Dubai",
"pageNum": 1,
"pageSize": 50,
"cursor": 0,
"sortBy": "price-desc"
}'Try it
Try it
POST /api/search/hotelList
