FindHotel Search API
npm install @findhotel/sapiSDK provides a high level TypeScript/JavaScript API for searching
hotels, hotels\' offers and rooms.
- SAPI SDK
- Table of contents
- Release process
- GitHub Token
- NPM Registry
- New release
- New release from a development branch
- Tutorials
- Getting Started
- React Native
- Playground
- Usage
- Search
- Get hotel's rooms offers
- Get hotel
- Get hotels
- Get hotel offers
- Get multiple hotels offers
- Get hotel(s) availability (aka price calendar)
- API Reference
- SAPI client
- Supported options
- Available client callbacks
- Response
- search() method
- Search parameters
- Split Booking Configuration
- Filters
- Facilities
- Property types
- Amenities
- Callbacks
- Response
- Load more results and load more offers for a hotel
- suggest() method
- Suggest parameters
- Response
- freeTextSuggest() method
- Suggest parameters
- Response
- rooms() method
- Rooms parameters
- Response
- Rooms long-polling
- hotel() method
- Hotel parameters
- Response
- hotels() method
- Hotels parameters
- Response
- offers() method
- Offers parameters
- Response
- hotelsOffers() method
- Offers parameters
- Response
- hotelAvailability() method
- Hotel(s) availability search parameters
- Callbacks
- Response
- reviews() method
- Reviews parameters
- Response
- Reviews V2
- Reviews V2 parameters
- Reviews V2 filters
- Reviews V2 response
- Rooms configuration
- Examples
- How to generate searchId?
- Example
There is no continuous integration/delivery in SAP SDK. Merging a PR
into main branch doesn\'t automatically trigger the release process. For
releases SAPI SDK uses Ship.js
tool. It gives more control over the process. Ship.js automatically
creates a PR before publishing every release, so this allows:
- Confirm the next version is correct.
- Confirm which commits are going to be released and discuss them with
colleagues.
- Edit the automatically generated changelog for clarity &
readability.
NPM account is required for pre-release (beta) process:
- Go to NPM sign up
- Create an account and ask SAPI to give you access to organization
- Activate your Two Factor Authentication using Authenticator/1Password
GitHub token is required for the release process:
- Go to your GitHub account
- Check \"repo(Full control of private repositories)\"
- Generate/copy the token
- Add it in your command line like: GITHUB_TOKEN=xxx
To create and deploy a new release of SAPI SDK please follow the next
steps:
- Merge your changes. There can be multiple changes in one release
- Checkout the latest version of the main branch
- In the root directory run make release command - this
will start the release process
- Review and confirm (or change) the new release version number
- After installing dependencies and running deployment scripts a new
pull request will be automatically created
- Review/update CHANGELOG.md in the root directory. Write down
changes with descriptions using [keep a changelog] guiding
principles and commit your changes to the newly created release
PR
- Review the PR/ask SAPI engineer(s) for approval
- Squash and merge the PR. This will trigger automated
release process using GitHub actions and after some time the new
release will be published to NMP
- Check if the new version has been published to NPM
[keep a changelog]: https://keepachangelog.com/
Sometimes You might want to test your changes on the website for example on the local or custom/test environment before merging your branch to main and before making a new SAPI SDK release. To do so You can create and publish a pre-release.
Pre-release publishes a new version to NPM with a tag other than latest (the default tag).
To publish a pre-release please follow the next steps:
- build SAPI SDK from the root of the project:
``sh`
make core-build
npm version prepatch (preminor, premajor) --preid=beta (alpha, beta, etc...)
- create a new version :
`sh`
npm version prepatch --preid=beta
this would result in something like this 1.4.1-beta.0
- If you've already released a pre-release and want to increment the pre-release part of the version only, run:
`sh`
npm version prerelease
this would result in something like this 1.4.1-beta.1
- publish your changes with a tag other than latest:
`sh`
npm publish --tag=next
During the publish you'll asked to login/enter your OTP (Two Factor Auth Code) which you can access it using Authenticator/1Password app (for more information read here)
- after the new version is published to npm, install it to your local environment specifying the new version:
`sh`
npm install @findhotel/sapi@1.4.1-beta.0
First, install SAPI SDK via the npm
package manager:
`sh`
npm install @findhotel/sapi
Then, import SAPI into your project:
`js`
import sapi from "@findhotel/sapi";
Create SAPI client:
`js
const profileKey = "profile-key";
const sapiClient = await sapi(profileKey, {
anonymousId: "fd9dbb5f-b337-4dd7-b640-1f177d1d3caa",
language: "en",
currency: "USD",
userCountry: "US",
});
`
Now SAPI client is ready to be used in your application.
For full documentation and supported options check client api.
Sapi SDK uses uuid for generation of random strings.uuid uses a global crypto object which is not
available in React Native environment.
To add support, crypto polyfill should be installed on thecrypto.getRandomValues
consumer side. We recommend installing just polyfill if you don't use othercrypto methods. More information about polyfill can be
found here
Playground is a local environment for checking your changes and test if the implementations were correct you can access this environment locally (http://localhost:3000) by opening two terminals and running these commands in order
`bash`watch your changes to core and create a new bundle
make core-dev
`bash`runs the playground on http://localhost:3000
make playground-run
Search for the hotels and hotels\' offers:
`js
const searchParameters = {
placeId: "47319",
checkIn: "2021-10-10",
checkOut: "2021-10-11",
rooms: "2",
};
const callbacks = {
onStart: (response) => {
log("Search started", response);
},
onAnchorReceived: (response) => {
log("Anchor received", response);
},
onHotelsReceived: (response) => {
log("Hotels received", response);
},
onOffersReceived: (response) => {
log("Offers received", response);
},
onComplete: (response) => {
log("Search completed", response);
},
};
const search = await sapiClient.search(searchParameters, callbacks);
`
For full documentation, check search method api.
Get rooms data and rooms\' offers:
`js`
const rooms = await sapiClient.rooms({
hotelId: "47319",
checkIn: "2021-10-10",
checkOut: "2021-10-11",
rooms: "2",
});
We also introduced roomsV2 which will soon deprecate the old rooms method
`js`
const rooms = await sapiClient.roomsV2({
hotelId: "47319",
checkIn: "2021-10-10",
checkOut: "2021-10-11",
rooms: "2",
});
For full documentation, check rooms method api.
Get hotel by id:
`js`
const hotel = await sapiClient.hotel("1196472");
For full documentation, check hotel method api.
Get hotels by ids:
`js`
const hotels = await sapiClient.hotels(["1714808", "1380416", "1710829"]);
For full documentation, check hotels method api.
Get offers for a single hotel:
`js
const parameters = {
hotelId: "1196472",
};
const callbacks = {
onStart: (response) => {
log("Offers started", response);
},
onOffersReceived: (response) => {
log("Offers received", response);
},
onComplete: (response) => {
log("Offers completed", response);
},
};
const offers = await sapiClient.offers(parameters, callbacks);
`
For full documentation, check offers method api.
Get offers for multiple hotels:
`js
const parameters = {
hotelIds: ["1196472", "1041597"],
};
const callbacks = {
onStart: (response) => {
log("Hotels offers started", response);
},
onOffersReceived: (response) => {
log("Hotels offers received", response);
},
onComplete: (response) => {
log("Hotels offers completed", response);
},
};
const offers = await sapiClient.hotelsOffers(parameters, callbacks);
`
For full documentation, check hotels offers method api.
Get hotel(s) availability:
`js
const parameters = {
hotelIds: ["1380416", "1359951"],
startDate: "2023-08-05",
endDate: "2023-08-12",
nights: 2,
rooms: "2",
};
const callbacks = {
onStart: (response) => {
log("Availability request started", response);
},
onAvailabilityReceived: (response) => {
log("Availability received", response);
},
onComplete: (response) => {
log("Availability completed", response);
},
};
const response = await sapiClient.hotelAvailability(parameters, callbacks);
`
For full documentation, check availability method api.
Create SAPI client:
`js
const profileKey = "profile-key";
const sapiClient = await sapi(profileKey, {
anonymousId: "fd9dbb5f-b337-4dd7-b640-1f177d1d3caa",
language: "en",
currency: "USD",
userCountry: "US",
deviceType: "mobile",
pageSize: 20,
initWithAppConfig: {},
algoliaClientOptions: {
timeouts: {
connect: 1, // Connection timeout in seconds
read: 2, // Read timeout in seconds
write: 30, // Write timeout in seconds
},
},
callbacks: {
onConfigReceived: (config) => {
log("Config received", config);
},
},
getTotalRate: (rate) => {
let totalRate = rate.base;
if (includeTaxes) totalRate += rate.taxes;
if (includeHotelFees) totalRate += rate.hotelFees;
return totalRate;
},
apiKey: "sk_test_123456789",
});
`
| name | required | type | default | description | example |
| ---------------------- | -------- | ------------------------ | ----------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ |
| anonymousId | yes | string | | Unique ID identifying users | 2d360284-577b-4a53-8b91-68f72b9227fa |language
| | no | string | en | 2-char language code | en |currency
| | no | string | USD | 3-char uppercased ISO currency code | USD |userCountry
| | no | string | US | 2-char uppercased ISO country code | US |deviceType
| | yes | string | | desktop, mobile, tablet | desktop |pageSize
| | no | number | 20 | Displayed page size | 20 |initWithAppConfig
| | no | AppConfig | | External app config to override internal one | |algoliaClientOptions
| | no | AlgoliaSearchOptions | | Algolia client options used for debugging and setting additional options like timeouts etc. | |variations
| | no | Record | | A/B test variations | {'pp000004-tags2': 'b', 'pp000004-tags3': '1'} |callbacks
| | no | Record | | Client callbacks | |getTotalRate
| | no | function | Total rate including taxes and fees | Function to calculate total display rate based on tax display logic. Used for price filter and sort by price functionalities. | |apiKey
| | no | string | | API key for authenticating requests to the SAPI backend. Each client has its own unique key stored in sapi-backend config to identify the request source. | sk_test_123456789 |
1. onConfigReceived(configs)
Returns configuration settings that are used by sapiClient.
`json`
{
"exchangeRates": {
"EUR": 0.982994
},
"lov": [
{
"id": 9,
"categoryID": 19,
"objectID": "Facility:9",
"value": {
"en": "Airport shuttle"
}
},
{
"categoryID": 2,
"id": 7,
"objectID": "Facility:7",
"value": {
"en": "Swimming pool"
}
}
]
}
methodSearch is a method of sapiClient for searching hotels and offers for
provided searchParameters:
`js`
const search = await sapiClient.search(searchParameters, callbacks);
All parameters are optional.
| name | type | description | example |
| --------------------- | ----------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------- |
| hotelId | string | Hotel Id for hotel search | 1371626 |placeId
| | string | Place Id for place search | 47319 |geolocation
| | {lat: number, lon: number, precision: string} | Geolocation query | {lat: 36.114303, lon: -115.178312} |address
| | string | Address search string | Nieuwe Looiersdwarsstraat 17, |boundingBox
| | [p1Lat, p1Lng, p2Lat, p2Lng] | topLeft and bottomRight coordinates of bounding box to perform search inside it | [46.650828100116044, 7.123046875, 45.17210966999772, 1.009765625] |checkIn
| | string | Check in date (YYYY-MM-DD) | 2021-10-10 |checkOut
| | string | Check out date (YYYY-MM-DD) | 2021-10-11 |rooms
| | string | Rooms configuration | 2 |dayDistance
| | number | Amount of full days from now to desired check in date (works in combination with nights parameter) | 15 |nights
| | number | Number of nights of stay | 3 |sortField
| | string | Defines the sort by criteria | popularity, price |sortOrder
| | string | Defines the sort order | ascending, descending |filters
| | Object | Object with filters | {starRatings: 5} |cugDeals
| | string[] | Codes of closed user group deals to retrieve offers | ['signed_in', 'offline'] |tier
| | string | User tier | member |originId
| | string | Identifier of origin where the request was originated | c3po6twr70 |preferredRate
| | number | Offer\'s price user saw on a CA (meta) platform | 196 |label
| | string | Opaque value that will be passed to tracking systems | gha-vr |userId
| | string | An authenticated user ID, e.g. the Google ID of a user. Used by ACL | c34b0018-d434-4fad-8de6-9d8e8e18cb35 |emailDomain
| | string | Email domain for authenticated user, Used by ACL | @manatuscostarica.com |deviceId
| | string | Device ID, Used by ACL (the IDFV of an iOS device or the GAID of an Android device) | |screenshots
| | number | Number of of screenshot taken by the user and detected by website, Used by ACL | 2 |metadata
| | Record | Additional metadata to be passed to the search | {esd: 'abcds', epv: 'qwert'} |offerSort
| | string | If present, sorts offers by price (see explanation) | |optimizeRooms
| | boolean | If true then rooms optimization logic is enabled for non anchor hotels. Rooms optimization tries to find best offer across possible rooms configurations for a given occupancy | true |optimizeAnchorRooms
| | boolean | If true then rooms optimization logic is enabled for an anchor hotel. Rooms optimization tries to find best offer across possible rooms configurations for a given occupancy | false |sbc
| | string | Split Booking Configuration. If present, searches for split booking offers | sbc: '2~3' |skipOffers
| | boolean | Skip offers search and return only static results | true |searchId
| | string | ID of search session, should only be specified when re-running the search to be tracked as the same session | true |useLiveOfferDataHSO
| | boolean | Hints the back-end when to use or not live offer data for refined hotel sorting | true |
#### Split Booking Configuration
The format for the split booking configuration is as follows:
LOS1~LOS2
where:
LOS1 and LOS2 are integers representing the length of stay for the first and second part of the split booking respectively.
for example if we have these parameters:
checkIn:'2023-08-10'
checkOut:'2023-08-15'
sbc:2~3
The first part of the split booking will be 2 nights and the second part will be 3 nights.
So, SAPI searches for two stays:
split1: checkIn: '2023-08-10' checkOut: '2023-08-12'
split2: checkIn: '2023-08-12' checkOut: '2023-08-15'
#### Filters
| name | type | description | example | filter scope |
| ------------------ | ---------- | -------------------------------------------------------------------------------------------------- | ------------------------------ | ------------ |
| themeIds | number[] | Facet filtering by theme ids | [1, 2, 3] | Hotel |chainIds
| | number[] | Hotels' chain ids | [4, 5] | Hotel |facilities
| | string[] | Facility ids used for facet filtering | [299,7,6,21] | Hotel |starRatings
| | number[] | Hotels' star rating | [3, 4] | Hotel |propertyTypeId
| | number[] | Property type id | [1, 0] | Hotel |guestRating
| | number | Minimum overall guest rating | 5 | Hotel |noHostels
| | boolean | If true, hostels will be excluded from results | true | Hotel |priceMin
| | number | Minimum price per night | 100 | Hotel/Offer |priceMax
| | number | Maximum price per night | 200 | Hotel/Offer |hotelName
| | string | Filters the result based on the Hotel name | Hilton Amsterdam | Hotel |freeCancellation
| | boolean | If true, only offers with free cancellation will be returned | true | Offer |payLater
| | boolean | If true, only offers with pay later option will be returned | true | Offer |mealIncluded
| | boolean | If true, only offers with meal included option will be returned | true | Offer |dealFreeze
| | boolean | If true, only offers that can be frozen will be returned | true | Offer |exclusive
| | boolean | If true, only offers that are exclusive to Vio will be returned | true | Offer |amenities
| | string[] | Meal types | ["breakfast","allInclusive"] | |isVr
| | boolean | If true, then only vacation rentals are returned. If false, then vacation rentals are filtered out | true | Hotel |neighborhoodIds
| | string[] | Filters the result based on the Hotel's neighborhoods | ["47689","45031"] | Hotel |brandIds
| | string[] | Filters the result based on the Hotel's brand | ["12060","2137"] | Hotel |
##### Facilities
Some of the favourite facilities used for filtering:
| name | id |
| ----------------- | --- |
| free_wifi | 299 |swimming_pool
| | 7 |restaurant
| | 5 |parking
| | 6 |pet_friendly
| | 8 |kitchen
| | 50 |hot_tub
| | 61 |spa
| | 193 |air_conditioned
| | 21 |
##### Property types
Some of the property types used for filtering:
| name | id |
| ----------- | --- |
| hotel | 0 |apartment
| | 9 |hostel
| | 5 |motel
| | 1 |
##### Amenities
| name | description |
| -------------- | ------------------------------------------------------------------------------------ |
| breakfast | |lunch
| | |dinner
| | |halfBoard
| | "breakfast && dinner", or the hotel's explicit use of the term "half-board" |fullBoard
| | "breakfast && lunch && dinner", or the hotel's explicit use of the term "full-board" |allInclusive
| | "all meals && all drinks" or the hotel's explicit use of the term "all-inclusive" |
Search method receives callbacks object as the second argument:
`js`
const callbacks = {
onStart: (response) => {
log("Search started", response);
},
onAnchorReceived: (response) => {
log("Anchor received", response);
},
onHotelsReceived: (response) => {
log("Hotels received", response);
},
onOffersReceived: (response) => {
log("Offers received", response);
},
onComplete: (response) => {
log("Search completed", response);
},
};
Each callback returns the full search state available inside SAPI at the
time of callbacks executions. This means that each subsequent callback
incrementally adds new properties and returns updated state. The data in
the state can be repeated between different callbacks.
1. ### onStart()
Runs at the beginning of each new search.
The callback is supplied with generated searchId and
actual search parameters that are used for the search, like dates,
currency, language used if invoker didn\'t provide those.
Another purpose of this callback is that invoker can track that
hotels search has started, in Search SPA the event is
HotelsSearched.
2. ### onAnchorReceived()
Runs when SAPI receives anchor and(?) anchor hotel (in case of hotel
search)\
Returns static data about anchor response.anchor and(?)response.hotelEntities
anchor hotel inside object (in
case of hotel search).
3. ### onHotelsReceived()
Runs when SAPI receives static search results\
Returns static data about hotels inside
response.hotelEntities object as well as appropriateresponse.hotelIds
hotel sort order in array (sorted
according to HSO).
4. ### onOffersReceived()
Runs when SAPI receives a bunch of offers\
Returns hotel's offers (incomplete response) in
response.hotelOfferEntities object as well as updatedresponse.hotelIds
hotel sort order in array (sorted
according to HSO and taking into account the real offers data)\
The offers response with the detailed description can be found in the Offers Response
5. ### onComplete()
Runs when current search is complete and all offers are retrieved\
Return complete SAPI search response.\
The offers response with the detailed description can be found in the Offers Response
The complete SAPI search response is too big to be displayed on the page
so there is a simplified example:
`json`
{
"anchor": {
"_geoloc": {
"lat": -6.28067,
"lon": 39.53601,
"precision": 5000,
"radius": 20000
},
"objectID": "hotel:9660485",
"objectType": "hotel",
"pageSize": 25,
"priceBucketWidth": 16,
"navPath": ["3429", "300878", "235186"],
"navPathInfo": {
"adminDivision": {
"id": "12598",
"name": "Hokkaido Prefecture"
},
"city": {
"id": "43727",
"name": "Otaru"
},
"country": {
"id": "125948",
"name": "Japan"
}
},
"hotelName": "Zanzibar White Sand Luxury Villas & Spa - Relais & Chateaux",
"placeDisplayName": "Zanzibar"
},
"anchorType": "hotel",
"anchorHotelId": "9660485",
"facetFilters": {
"brands": {
"0": {
"value": "Independent"
},
"11157": {
"value": "NH Collection"
},
"12060": {
"value": "Revenue Guru"
}
},
"neighborhoods": {
"253086": {
"value": "Amsterdam City Centre"
},
"305373": {
"value": "Amsterdam-West"
},
"305374": {
"value": "Amsterdam-Zuid"
}
}
},
"hotelEntities": {
"9660485": {
"_geoloc": {
"lat": -6.28067,
"lon": 39.53601
},
"checkInTime": "14:00",
"checkOutTime": "11:00",
"country": "TZ",
"facilities": [
2, 5, 6, 7, 9, 11, 13, 20, 21, 22, 23, 28, 29, 31, 34, 35, 66, 84, 85,
86, 98, 99, 100, 103, 104, 122, 127, 189, 191, 193, 195, 202, 222, 235,
238, 243, 244, 253, 255, 266, 270, 292, 299
],
"guestRating": {
"cleanliness": 9.66,
"dining": 9.42,
"facilities": 9.9,
"location": 9.8,
"overall": 9.55,
"rooms": 9.72
},
"guestType": {
"business": 6,
"couples": 235,
"families": 110,
"groups": 68,
"solo": 13
},
"imageURIs": [
"https://img.fih.io/image/raw/regular/provider_partition=eps/3xWl0k2P0Ofb7vxD4dr1NhFjOtdQaHXLe2p1tYLaOmw=.webp"
],
"navPath": ["3429", "300878", "235186"],
"navPathInfo": {
"adminDivision": {
"id": "12598",
"name": "Hokkaido Prefecture"
},
"city": {
"id": "43727",
"name": "Otaru"
},
"country": {
"id": "125948",
"name": "Japan"
}
}
}
}
}
#### navPathInfo
this structure holds all information about the hotel location(starting from country -> region -> city -> village)
`json`
"navPathInfo": {
"adminDivision": {
"id": "12598",
"name": "Hokkaido Prefecture"
},
"city": {
"id": "43727",
"name": "Otaru"
},
"country": {
"id": "125948",
"name": "Japan"
}
}
After the search is complete:
`js`
const search = await sapiClient.search(searchParameters, callbacks);
it resolves in the search object which has 2 methods:
- search.loadMore() - SDK will return the next set of results ("next" page) for the given search. The same callbacks from original search will be used.search.loadOffers(hotelId, {filters, offersSort})
- - will load all offers (up to 23) for the provided hotelId. You can also override offers' scope filters and offers' sort order.
methodSuggest provides autosuggestions for a given query
`js`
const suggestions = await sapiClient.suggest("London", 6);
| name | type | description | required | example |
| --------------- | -------- | ----------------------------------------- | -------- | -------- |
| query | string | Query string | yes | London |suggestsCount
| | number | Desired number of suggestions (default 6) | no | 10 |
`js`
const suggestions = await sapiClient.suggest("London", 2);
`json`
[
{
"highlightValue": "London",
"objectID": "158584",
"objectType": "place",
"placeDisplayName": "United Kingdom",
"placeTypeName": "city",
"value": "London"
},
{
"highlightValue": "London Heathrow Airport",
"objectID": "167733",
"objectType": "place",
"placeDisplayName": "London, United Kingdom",
"placeTypeName": "airport",
"value": "London Heathrow Airport"
}
]
`js`
const suggestions = await sapiClient.suggest("London hotel", 2);
`json`
[
{
"highlightValue": "Park Plaza Westminster Bridge London",
"objectID": "1546646",
"objectType": "hotel",
"placeDisplayName": "London, United Kingdom",
"placeTypeName": "property",
"value": "Park Plaza Westminster Bridge London"
},
{
"highlightValue": "Hampton by Hilton London Stansted Airport",
"objectID": "3333916",
"objectType": "hotel",
"placeDisplayName": "United Kingdom",
"placeTypeName": "property",
"value": "Hampton by Hilton London Stansted Airport"
}
]
methodFreeTextSuggest provides suggestions for a given free text query based on AI feature
`js`
const result = await sapiClient.freeTextSuggest(
"5 stars hotels in Amsterdam with pets allowed for tomorrow 2 nights"
);
| name | type | description | required | example |
| ------- | -------- | ------------ | -------- | --------------------------------------------------------------------- |
| query | string | Query string | yes | 5 stars hotels in Amsterdam with pets allowed for tomorrow 2 nights |
`js`
const result = await sapiClient.freeTextSuggest(
"5 stars hotels in Amsterdam with pets allowed for tomorrow 2 nights"
);
`json`
[
{
"filters": {
"starRating": 5,
"facilities": [
{
"id": 8,
"value": "Pet Friendly"
}
]
},
"checkIn": "2024-06-12",
"checkOut": "2024-06-14",
"highlightValue": "Amsterdam",
"objectID": "47319",
"objectType": "place",
"placeDisplayName": "The Netherlands",
"placeTypeName": "city",
"value": "Amsterdam"
},
{
"filters": {
"starRating": 5,
"facilities": [
{
"id": 8,
"value": "Pet Friendly"
}
]
},
"checkIn": "2024-06-12",
"checkOut": "2024-06-14",
"highlightValue": "InterContinental Amstel Amsterdam, an IHG Hotel",
"objectID": "1363313",
"objectType": "hotel",
"placeDisplayName": "Weesperbuurt en Plantage, Amsterdam, The Netherlands",
"placeTypeName": "property",
"value": "InterContinental Amstel Amsterdam, an IHG Hotel"
}
]
methodRooms is a method of sapiClient for retrieving rooms information and
offers for a particular itinerary:
V1
`js`
const rooms = await sapiClient.rooms({
hotelId: "1714808",
checkIn: "2021-10-10",
checkOut: "2021-10-11",
rooms: "2",
});
V2
`js`
const rooms = await sapiClient.roomsV2({
hotelId: "1714808",
checkIn: "2021-10-10",
checkOut: "2021-10-11",
rooms: "2",
});
| name | type | description | required | example |
| ---------------- | ------------------------ | ------------------------------------------------------------------------------ | -------- | ------------------------------------------------- |
| hotelId | string | Hotel Id to retrieve rooms | yes | 1714808 |checkIn
| | string | Check in date (YYYY-MM-DD) | yes | 2021-10-10 |checkOut
| | string | Check out date (YYYY-MM-DD) | yes | 2021-10-11 |rooms
| | string | Rooms configuration | yes | 2 |providerCode
| | string | Provider code used for retrieving rooms offers | no | GAR |cugDeals
| | string | Type of cug (closed user group) deals to retrieve | no | signed_in,offline |tier
| | string | User tier | no | plus |clickedOfferId
| | string | Offer id that user clicked on SRP | no | oXstvXafDb2o |preHeat
| | number | Enables pre-heat mode | no | 1 |label
| | string | Opaque value that will be passed to tracking systems | no | gha-vr |userId
| | string | User ID is an authenticated user ID, e.g. the Google ID of a user. | no | 7ea95191-b929-469d-8b02-0397dafd53a1 |emailDomain
| | string | User email domain is for authenticated user as a value, if email is available. | no | @findhotel.net |deviceId
| | string | Device ID (the IDFV of an iOS device or the GAID of an Android device) | no | |searchId
| | string | SearchId override (SDK generates a new one if no provided) | no | 08230dfcc5f0fb95caaa82ce559ea60c4a975d6f |metadata
| | Record | Additional data to be passed to API | no | {vclid: '21d2d152-de19-4f39-b40a-d4bc3002c6e8'} |bundleId
| | string | Bundle Id clicked on SRP. Used to fetch bundle data from cache | no | 13b5d152-de19-4f39-b40a-d4bc3002c3u9 |originId
| | string | Identifier of origin where the request was originated | no | c3po6twr70 |
V1 Response
`json`
{
"anonymousId": "fd9dbb5f-b337-4dd7-b640-1f177d1d3caa",
"hotelId": "1714808",
"searchId": "08230dfcc5f0fb95caaa82ce559ea60c4a975d6f",
"rooms": [
{
"amenities": ["Non-Smoking Facility"],
"amenitiesList": [
"title": "Non-Smoking Facility",
"id": "123"
],
"bedTypes": [],
"bedrooms": [{
"bedConfigurations": [{
"cfgID": 1,
"count": 1,
"type": "Full Bed",
"typeID": "full bed"
}]
}],
"occupancy": {
"maxAdults": 2,
"maxChildren": 1,
"maxOccupancy": 2
},
"description": "20 sqm room Continental breakfast included Free WiFi LCD TV Spacious work desk Choice of pillows Coffee and tea facilities Large walk-in power shower",
"id": "K0LnAe-G9WI",
"images": [{ "url": "http://images.getaroom-cdn.com/image/...." }],
"masterId": "9643931",
"name": "1 Double Standard",
"offers": [
{
"availableRooms": 0,
"canPayLater": false,
"cancellationPenalties": [],
"cug": ["signed_in"],
"id": "o0KDe_mFPN4k",
"links": [
{
"href": "https://secure.findhotel.net/checkout/?hotelID=1714808...",
"method": "GET",
"type": "checkout"
}
],
"prices": [
{
"chargeable": { "base": "1352.77", "taxes": "59.94" },
"currencyCode": "EUR",
"hotelFees": { "breakdown": [], "total": "0" },
"type": "chargeable_currency"
}
],
"providerCode": "GAR",
"providerRateType": "public",
"services": ["breakfast"]
}
]
}
]
}
V2 Respnose
`json`
{
"entities": [
{
"id": "b57ac4d3-72bf-4f0c-b82f-28e29543c06a",
"entityType": "split_booking",
"rooms": [
{
"id": "SHPLitp7oTk",
"name": "Room",
"raaName": "Room",
"amenities": [],
"amenitiesList": [],
"images": [],
"description": "",
"smokingOptionsAvailable": false,
"bedTypes": [],
"area": {},
"bedrooms": [
{
"bedConfigurations": [
{
"cfgID": 1,
"count": 1,
"type": "Full Bed",
"typeID": "full bed"
}
]
}
],
"occupancy": {
"maxAdults": 2,
"maxChildren": 1,
"maxOccupancy": 2
}
},
{
"id": "dn4Qijq4auI",
"name": "cosy room(City view)",
"raaName": "cosy room(City view)",
"amenities": [],
"amenitiesList": [],
"images": [],
"description": "",
"smokingOptionsAvailable": false,
"bedTypes": [],
"area": {},
"bedrooms": [
{
"bedConfigurations": [
{
"cfgID": 1,
"count": 1,
"type": "Full Bed",
"typeID": "full bed"
}
]
}
],
"occupancy": {
"maxAdults": 2,
"maxChildren": 1,
"maxOccupancy": 2
}
}
],
"offers": [
{
"isClicked": true,
"matchType": "by_price",
"matchedOfferPriceDiff": 0,
"id": "b57ac4d3-72bf-4f0c-b82f-28e29543c06a",
"availableRooms": null,
"canPayLater": false,
"cancellationPenalties": [],
"cug": null,
"links": [
{
"href": "https://secure.findhotel.net/checkout/?bundleId=b57ac4d3-72bf-4f0c-b82f-28e29543c06a..",
"method": "GET",
"type": "checkout"
}
],
"prices": [
{
"chargeable": {
"base": "570.7278477192",
"taxes": "46.6959148134"
},
"currencyCode": "EUR",
"hotelFees": {
"breakdown": [],
"total": "74.1946202035"
},
"rate": {
"base": 570.7278477192,
"taxes": 46.6959148134,
"hotelFees": 74.1946202035
},
"type": "chargeable_currency"
}
],
"services": null,
"providerCode": "",
"tags": ["price_matched", "sb_single_flow"],
"metadata": {
"feedID": ""
},
"offerType": "split_booking",
"itineraries": [
{
"checkIn": "2025-03-30",
"checkOut": "2025-03-31",
"roomID": "SHPLitp7oTk",
"offer": {
"id": "ojnRYasJ3-Ws",
"availableRooms": null,
"providerRateType": "public",
"canPayLater": false,
"cancellationPenalties": [],
"cug": [],
"links": [
{
"href": "https://secure.findhotel.net/checkout/?hotelId=1041597...",
"method": "GET",
"type": "checkout"
}
],
"prices": [
{
"chargeable": {
"base": "119.8596330275",
"taxes": "9.8066972477"
},
"currencyCode": "EUR",
"hotelFees": {
"breakdown": [],
"total": "15.5817522936"
},
"rate": {
"base": 119.8596330275,
"taxes": 9.8066972477,
"hotelFees": 15.5817522936
},
"type": "chargeable_currency"
}
],
"services": [],
"providerCode": "SMR",
"tags": [
"preferred_rate",
"top_offer",
"exclusive_cheapest_offer"
],
"metadata": {
"providerRateType": "public",
"feedID": ""
},
"offerType": "regular",
"itineraries": null,
"totalRate": {
"base": 0,
"taxes": 0,
"hotelFees": 0
}
}
},
{
"checkIn": "2025-03-31",
"checkOut": "2025-04-03",
"roomID": "dn4Qijq4auI",
"offer": {
"id": "o33J8HXa4tJY",
"availableRooms": 99,
"providerRateType": "plus",
"canPayLater": false,
"cancellationPenalties": [],
"cug": [],
"links": [
{
"href": "https://secure.findhotel.net/checkout/?hotelId=1041597...",
"method": "GET",
"type": "checkout"
}
],
"prices": [
{
"chargeable": {
"base": "450.8682146917",
"taxes": "36.8892175657"
},
"currencyCode": "EUR",
"hotelFees": {
"breakdown": [],
"total": "58.6128679099"
},
"rate": {
"base": 450.8682146917,
"taxes": 36.8892175657,
"hotelFees": 58.6128679099
},
"type": "chargeable_currency"
}
],
"services": [],
"providerCode": "DDT",
"tags": [
"preferred_rate",
"top_offer",
"exclusive_cheapest_offer"
],
"metadata": {
"providerRateType": "plus",
"feedID": ""
},
"offerType": "regular",
"itineraries": null,
"totalRate": {
"base": 0,
"taxes": 0,
"hotelFees": 0
}
}
}
],
"totalRate": {
"base": 570.7278477192,
"taxes": 46.6959148134,
"hotelFees": 74.1946202035
}
}
],
"hasClickedOffer": true
},
{
"id": "dn4Qijq4auI",
"entityType": "room",
"rooms": [
{
"id": "dn4Qijq4auI",
"name": "cosy room(City view)",
"raaName": "cosy room(City view)",
"amenities": [],
"amenitiesList": [],
"images": [],
"description": "",
"smokingOptionsAvailable": false,
"bedTypes": [],
"area": {}
}
],
"offers": [
{
"id": "o-9NZdSFm3Qc",
"availableRooms": 99,
"providerRateType": "plus",
"canPayLater": false,
"cancellationPenalties": [],
"cug": [],
"links": [
{
"href": "https://secure.findhotel.net/checkout/?hotelId=1041597...",
"method": "GET",
"type": "checkout"
}
],
"prices": [
{
"chargeable": {
"base": "575.6297833706",
"taxes": "47.0969822758"
},
"currencyCode": "EUR",
"hotelFees": {
"breakdown": [],
"total": "74.8318718382"
},
"rate": {
"base": 575.6297833706,
"taxes": 47.0969822758,
"hotelFees": 74.8318718382
},
"type": "chargeable_currency"
}
],
"services": [],
"providerCode": "DDT",
"tags": ["top_offer", "exclusive_cheapest_offer"],
"metadata": {
"providerRateType": "plus",
"feedID": ""
},
"offerType": "regular",
"itineraries": null,
"totalRate": {
"base": 0,
"taxes": 0,
"hotelFees": 0
}
}
],
"hasClickedOffer": false
}
]
}
The rooms() method supports both synchronous requests and long-polling.rooms()
To enable long-polling, simply provide callbacks as the second argument of the method:
`js
const parameters = {
hotelId: "1714808",
checkIn: "2021-10-10",
checkOut: "2021-10-11",
rooms: "2",
};
const callbacks = {
onStart(response) {
log("Rooms Offers Poll Started", response);
},
onOffersReceived(response) {
log("Rooms Offers Poll Received", response);
},
onComplete(response) {
log("Rooms Offers Poll Completed", response);
},
};
const rooms = await sapiClient.rooms(parameters, callbacks);
`
The response follows the same structure as a standard room offers request, with the addition of a status parameter that indicates the progress of the long-polling
`json`
{
"anonymousId": "fd9dbb5f-b337-4dd7-b640-1f177d1d3caa",
"hotelId": "1041597",
"searchId": "9248cf1d-1fdd-4aa5-af6d-2ec6e6671009",
"rooms": [...],
"splitBooking": {...},
"status": {
"complete": true
}
}
methodHotel is a method of sapiClient for retrieving a single hotel by id
`js`
const hotel = await sapiClient.hotel("1196472");
| name | type | description | required | example |
| --------- | -------- | ----------- | -------- | --------- |
| hotelId | string | Hotel Id | yes | 1196472 |
An example of the response for request with hotelId 1196472language
and \"pt-BR\"
` Os seguintes depósitos e taxas são cobrados pelo hotel no momento do serviço prestado, durante o check-in ou check-out. A lista acima pode estar incompleta. As taxas e os depósitos podem não incluir impostos e estão sujeitos a mudanças. Você deverá pagar os seguintes encargos no estabelecimento: Incluímos todas as cobranças que o estabelecimento nos forneceu. No entanto, as cobranças podem variar com base, por exemplo, na duração da estadia ou no tipo de quarto reservado. json`
{
"objectID": "1196472",
"guestRating": {
"cleanliness": 8.82,
"dining": 8.73,
"facilities": 8.73,
"location": 8.8,
"overall": 8.54,
"pricing": 4.49,
"rooms": 8.82,
"service": 8.67
},
"isDeleted": false,
"starRating": 4,
"_geoloc": {
"lat": 52.377805,
"lon": 4.914172
},
"feesOpt": "
"checkInSpInst": "O funcionário da recepção receberá os hóspedes na chegada. Para mais informações, entre em contato com o estabelecimento usando os dados que estão na confirmação da reserva. Os hóspedes que forem usar o traslado da Estação Central de Amsterdã devem entrar em contato com o estabelecimento com antecedência. Devido à COVID-19, as opções de comida e bebida deste estabelecimento podem estar limitadas de acordo com os regulamentos locais.",
"policies": "
",
"phone": "+31205191200",
"imageURIs": [
"https://i.travelapi.com/hotels/2000000/1460000/1452100/1452083/a39e2fc8_w.jpg",
"https://i.travelapi.com/hotels/2000000/1460000/1452100/1452083/3892e861_w.jpg",
"https://i.travelapi.com/hotels/2000000/1460000/1452100/1452083/aed25438_w.jpg"
],
"checkInTime": "15:00",
"checkInInst": "
",
"reviewCount": 17538,
"chainID": "2101",
"checkInEnd": "01:00",
"lastBooked": 1608030831,
"checkOutTime": "12:00",
"feesMnd": "
"deposit": {
"value": {
"currency": "USD",
"amount": 300.0
},
"description": "Deposit: USD 300.00 per accommodation
Breakage deposit: USD 230.00"
},
"reviewCountBkt": 9,
"hotelName": "Movenpick Hotel Amsterdam City Centre",
"sentiments": [
{
"value": "Gostou do bar",
"id": 46
},
{
"value": "Processo rápido de check-in/check-out",
"id": 180
}
],
"facilities": [
{
"categoryID": 4,
"importance": 2,
"value": "Centro de Negócios",
"id": 1
},
{
"categoryID": 17,
"importance": 2,
"value": "Serviço de quarto",
"id": 2
}
],
"placeID": "311007",
"guestType": {
"business": 1333,
"couples": 3125,
"families": 1484,
"groups": 1121,
"solo": 206
},
"themes": [
{
"value": "Cidade",
"id": 2
},
{
"value": "Empresarial",
"id": 12
}
],
"propertyType": {
"value": "Hotel",
"id": 0
},
"placeDisplayName": "Oostelijk Havengebied, Amsterdã",
"displayAddress": "Piet Heinkade 11, Oostelijk Havengebied, Amsterdã, Holanda",
"isApproximateLocation": true
}
methodHotel is a method of sapiClient for retrieving multiple hotels by hotel ids
`js`
const hotels = await sapiClient.hotels(["1714808", "1380416", "1710829"]);
| name | type | description | required | example |
| ---------- | ---------- | ----------- | -------- | --------------------------------- |
| hotelIds | string[] | Hotel Ids | yes | ['1714808','1380416','1710829'] |
An example of the response for request with hotelId 1196472language
and \"pt-BR\"
``json
{
"1196472": {
"objectID": "1196472",
"guestRating": {
"cleanliness": 8.82,
"