Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@findhotel/sapi

Package Overview
Dependencies
Maintainers
0
Versions
194
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@findhotel/sapi

FindHotel Search API

  • 1.17.2
  • Source
  • npm
  • Socket score

Version published
Weekly downloads
264
decreased by-70%
Maintainers
0
Weekly downloads
 
Created
Source

SAPI SDK

SDK provides a high level TypeScript/JavaScript API for searching hotels, hotels' offers and rooms.

Table of contents

Release process

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 Registry

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

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

New release

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

New release from a development branch

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:

    make core-build
    
  • create a new version npm version prepatch (preminor, premajor) --preid=beta (alpha, beta, etc...):

    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:

      npm version prerelease
      

      this would result in something like this 1.4.1-beta.1

  • publish your changes with a tag other than latest:

    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:

    npm install @findhotel/sapi@1.4.1-beta.0
    

Tutorials

Getting Started

First, install SAPI SDK via the npm package manager:

npm install @findhotel/sapi

Then, import SAPI into your project:

import sapi from "@findhotel/sapi";

Create SAPI client:

const profileKey = "profile-key";

const sapiClient = await sapi(profileKey, {
  anonymousId: "fd9dbb5f-b337-4dd7-b640-1f177d1d3caa",
  language: "en",
  currency: "USD",
  countryCode: "US",
});

Now SAPI client is ready to be used in your application.

For full documentation and supported options check client api.

React Native

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 the consumer side. We recommend installing just crypto.getRandomValues polyfill if you don't use other crypto methods. More information about polyfill can be found here

Playground

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

# watch your changes to core and create a new bundle
make core-dev
# runs the playground on http://localhost:3000
make playground-run

Usage

Search for the hotels and hotels' offers:

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 hotel's rooms offers

Get rooms data and rooms' offers:

const rooms = await sapiClient.rooms({
  hotelId: "47319",
  checkIn: "2021-10-10",
  checkOut: "2021-10-11",
  rooms: "2",
});

For full documentation, check rooms method api.

Get hotel

Get hotel by id:

const hotel = await sapiClient.hotel("1196472");

For full documentation, check hotel method api.

Get hotels

Get hotels by ids:

const hotels = await sapiClient.hotels(["1714808", "1380416", "1710829"]);

For full documentation, check hotels method api.

Get hotel offers

Get offers for a single hotel:

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 hotel(s) availability (aka price calendar)

Get hotel(s) availability:

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.

API Reference

SAPI client

Create SAPI client:

const profileKey = "profile-key";

const sapiClient = await sapi(profileKey, {
  anonymousId: "fd9dbb5f-b337-4dd7-b640-1f177d1d3caa",
  language: "en",
  currency: "USD",
  countryCode: "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;
  },
});

Supported options

namerequiredtypedefaultdescriptionexample
anonymousIdyesstringUnique ID identifying users2d360284-577b-4a53-8b91-68f72b9227fa
languagenostringen2-char language codeen
currencynostringUSD3-char uppercased ISO currency codeUSD
countryCodenostringUS2-char uppercased ISO country codeUS
deviceTypeyesstringdesktop, mobile, tabletdesktop
pageSizenonumber20Displayed page size20
initWithAppConfignoAppConfigExternal app config to override internal one
algoliaClientOptionsnoAlgoliaSearchOptionsAlgolia client options used for debugging and setting additional options like timeouts etc.
variationsnoRecord<string, string>A/B test variations{'pp000004-tags2': 'b', 'pp000004-tags3': '1'}
callbacksnoRecord<string, fnc>Client callbacks
getTotalRatenofunctionTotal rate including taxes and feesFunction to calculate total display rate based on tax display logic. Used for price filter and sort by price functionalities.

Available client callbacks

  1. onConfigReceived(configs)

    Returns configuration settings that are used by sapiClient.

Response

{
  "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"
      }
    }
  ]
}

search() method

Search is a method of sapiClient for searching hotels and offers for provided searchParameters:

const search = await sapiClient.search(searchParameters, callbacks);

Search parameters

All parameters are optional.

nametypedescriptionexample
hotelIdstringHotel Id for hotel search1371626
placeIdstringPlace Id for place search47319
geolocation{lat: number, lon: number, precision: string}Geolocation query{lat: 36.114303, lon: -115.178312}
addressstringAddress search stringNieuwe 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]
checkInstringCheck in date (YYYY-MM-DD)2021-10-10
checkOutstringCheck out date (YYYY-MM-DD)2021-10-11
roomsstringRooms configuration2
dayDistancenumberAmount of full days from now to desired check in date (works in combination with nights parameter)15
nightsnumberNumber of nights of stay3
sortFieldstringDefines the sort by criteriapopularity, price
sortOrderstringDefines the sort orderascending, descending
filtersObjectObject with filters{starRatings: 5}
cugDealsstring[]Codes of closed user group deals to retrieve offers['signed_in', 'offline']
tierstringUser tiermember
originIdstringIdentifier of origin where the request was originatedc3po6twr70
preferredRatenumberOffer's price user saw on a CA (meta) platform196
labelstringOpaque value that will be passed to tracking systemsgha-vr
userIdstringAn authenticated user ID, e.g. the Google ID of a user. Used by ACLc34b0018-d434-4fad-8de6-9d8e8e18cb35
emailDomainstringEmail domain for authenticated user, Used by ACL@manatuscostarica.com
deviceIdstringDevice ID, Used by ACL (the IDFV of an iOS device or the GAID of an Android device)
screenshotsnumberNumber of of screenshot taken by the user and detected by website, Used by ACL2
metadataRecord<string, string>Additional metadata to be passed to the search{esd: 'abcds', epv: 'qwert'}
offerSortstringIf present, sorts offers by price (see explanation)
optimizeRoomsbooleanIf 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 occupancytrue
optimizeAnchorRoomsbooleanIf 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 occupancyfalse
sbcstringSplit Booking Configuration. If present, searches for split booking offerssbc: '2~3'
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

nametypedescriptionexamplefilter scope
themeIdsnumber[]Facet filtering by theme ids[1, 2, 3]Hotel
chainIdsnumber[]Hotels' chain ids[4, 5]Hotel
facilitiesstring[]Facility ids used for facet filtering[299,7,6,21]Hotel
starRatingsnumber[]Hotels' star rating[3, 4]Hotel
propertyTypeIdnumber[]Property type id[1, 0]Hotel
guestRatingnumberMinimum overall guest rating5Hotel
noHostelsbooleanIf true, hostels will be excluded from resultstrueHotel
priceMinnumberMinimum price per night100Hotel/Offer
priceMaxnumberMaximum price per night200Hotel/Offer
hotelNamestringFilters the result based on the Hotel nameHilton AmsterdamHotel
freeCancellationbooleanIf true, only offers with free cancellation will be returnedtrueOffer
payLaterbooleanIf true, only offers with pay later option will be returnedtrueOffer
mealIncludedbooleanIf true, only offers with meal included option will be returnedtrueOffer
amenitiesstring[]Meal types["breakfast","allInclusive"]
isVrbooleanIf true, then only vacation rentals are returned. If false, then vacation rentals are filtered outtrueHotel
Facilities

Some of the favourite facilities used for filtering:

nameid
free_wifi299
swimming_pool7
restaurant5
parking6
pet_friendly8
kitchen50
hot_tub61
spa193
air_conditioned21
Property types

Some of the property types used for filtering:

nameid
hotel0
apartment9
hostel5
motel1
Amenities

Supported amenities:

namedescription
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"

Callbacks

Search method receives callbacks object as the second argument:

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(?) anchor hotel inside response.hotelEntities 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 appropriate hotel sort order in response.hotelIds 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 updated hotel sort order in response.hotelIds 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

Response

The complete SAPI search response is too big to be displayed on the page so there is a simplified example:

{
  "anchor": {
    "admDivisionLevel1": "The Netherlands",
    "admDivisionLevel2": "Amsterdam",
    "admDivisionLevel3": "Bedrijventerrein Sloterdijk",
    "hotelName": "Holiday Inn Express Amsterdam - Sloterdijk Station, an IHG Hotel",
    "objectID": "hotel:1714808",
    "objectType": "hotel",
    "pageSize": 26,
    "placeDisplayName": "Bedrijventerrein Sloterdijk, Amsterdam",
    "priceBucketWidth": 19,
    "_geoloc": {
      "lat": 52.388326,
      "lon": 4.837264,
      "precision": 5000,
      "radius": 20000
    }
  },
  "anchorHotelId": "1714808",
  "anchorType": "hotel",
  "facets": {
    "facilities": { "1": 212, "2": 348, "3": 116 },
    "pricing.medianRateBkt": { "3": 4, "4": 12, "5": 19 },
    "pricing.medianRateMoFrBkt": { "3": 4, "4": 6, "5": 8, "6": 9 },
    "pricing.medianRateSaSuBkt": { "2": 2, "3": 4, "4": 6, "5": 10 },
    "pricing.minRateBkt": { "2": 22, "3": 38, "4": 46, "5": 98 },
    "propertyTypeId": { "0": 1030, "2": 6, "3": 601, "4": 86 },
    "starRating": { "1": 351, "2": 313, "3": 596, "4": 441, "5": 86 },
    "themeIds": { "1": 9, "2": 1373, "3": 143, "5": 1092, "8": 119 }
  },
  "hotelEntities": {
    "1041597": {
      "chainID": "351",
      "checkInTime": "15:00",
      "checkOutTime": "12:00",
      "country": "NL",
      "displayAddress": "Stationsplein 49, Burgwallen-Nieuwe Zijde, Amsterdam, The Netherlands",
      "facilities": [1, 2, 5, 8, 9, 11],
      "guestRating": {
        "cleanliness": 7.94,
        "dining": 7.51,
        "facilities": 7.54,
        "location": 8.09,
        "overall": 7.83,
        "service": 8.8
      },
      "guestType": {
        "business": 793,
        "couples": 2558,
        "families": 1145,
        "groups": 1276,
        "solo": 398
      },
      "hotelName": "ibis Amsterdam Centre",
      "imageURIs": [
        "https://i.travelapi.com/hotels/1000000/30000/25600/25525/c2387aed_w.jpg"
      ],
      "isDeleted": false,
      "lastBooked": 1658320362,
      "objectID": "1041597",
      "parentChainID": "10",
      "placeDisplayName": "Burgwallen-Nieuwe Zijde, Amsterdam",
      "pricing": {
        "medianRateBkt": 12,
        "medianRateMoFrBkt": 13,
        "medianRateSaSuBkt": 11,
        "minRate": 116,
        "minRateBkt": 75,
        "minRateBkt": 5,
        "priced": 1
      },
      "propertyTypeId": 0,
      "regularPriceRange": [224, 243],
      "reviewCount": 5766,
      "sentiments": [177, 250, 300, 419],
      "starRating": 3,
      "tags": ["b220918-1"],
      "themeIds": [2, 5, 12],
      "_geoloc": { "lat": 52.37947, "lon": 4.89699 }
    }
  },
  "hotelIds": ["1840170", "2766940", "3310443", "3035769"],
  "hotelOfferEntities": {
    "1041597": {
      "anchorRate": {
        "base": 244.6021305,
        "hotelFees": 23.122149135,
        "taxes": 20.4147
      },
      "availableOffersCount": 23,
      "cheapestRate": {
        "base": 222.55,
        "taxes": 19.08,
        "hotelFees": 16.17
      },
      "id": "1041597",
      "offers": [
        {
          "id": "o5vFF4-wmvcc",
          "currency": "EUR",
          "availableRooms": 6,
          "url": "https://r.vio.com?...",
          "rate": {
            "base": 197.17,
            "taxes": 17.75,
            "hotelFees": 31.8
          },
          "accessTier": "",
          "providerCode": "BKS",
          "intermediaryProvider": "BKS",
          "roomName": "Standard Twin Room",
          "package": {
            "amenities": ["breakfast"],
            "canPayLater": false
          },
          "cancellationPenalties": [],
          "tags": ["top_offer"],
          "metadata": {
            "feedID": ""
          }
        }
      ],
      "discount": {
        "discountProvider": "FHT",
        "hasDiscountProvider": true,
        "hasParityProvider": true,
        "modifier": "b"
      }
    }
  },
  "hotelsHaveStaticPosition": true,
  "offset": 0,
  "resultsCount": 26,
  "resultsCountTotal": 3401,
  "searchId": "0f9642a0-eab7-4acb-8313-bc6d19c408f6",
  "searchParameters": {
    "hotelId": "1714808",
    "checkIn": "2022-09-01",
    "checkOut": "2022-09-02",
    "rooms": "2"
  },
  "status": {
    "anchorComplete": true,
    "nonAnchorComplete": true
  }
}

Load more results and load more offers for a hotel

After the search is complete:

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.

suggest() method

Suggest provides autosuggestions for a given query

const suggestions = await sapiClient.suggest("London", 6);

Suggest parameters

nametypedescriptionrequiredexample
querystringQuery stringyesLondon
suggestsCountnumberDesired number of suggestions (default 6)no10

Response

const suggestions = await sapiClient.suggest("London", 2);
[
  {
    "highlightValue": "<em>London</em>",
    "objectID": "158584",
    "objectType": "place",
    "placeDisplayName": "United Kingdom",
    "placeTypeName": "city",
    "value": "London"
  },
  {
    "highlightValue": "<em>London</em> Heathrow Airport",
    "objectID": "167733",
    "objectType": "place",
    "placeDisplayName": "London, United Kingdom",
    "placeTypeName": "airport",
    "value": "London Heathrow Airport"
  }
]
const suggestions = await sapiClient.suggest("London hotel", 2);
[
  {
    "highlightValue": "Park Plaza Westminster Bridge <em>London</em>",
    "objectID": "1546646",
    "objectType": "hotel",
    "placeDisplayName": "London, United Kingdom",
    "placeTypeName": "property",
    "value": "Park Plaza Westminster Bridge London"
  },
  {
    "highlightValue": "Hampton by Hilton <em>London</em> Stansted Airport",
    "objectID": "3333916",
    "objectType": "hotel",
    "placeDisplayName": "United Kingdom",
    "placeTypeName": "property",
    "value": "Hampton by Hilton London Stansted Airport"
  }
]

freeTextSuggest() method

FreeTextSuggest provides suggestions for a given free text query based on AI feature

const result = await sapiClient.freeTextSuggest(
  "5 stars hotels in Amsterdam with pets allowed for tomorrow 2 nights"
);

Suggest parameters

nametypedescriptionrequiredexample
querystringQuery stringyes5 stars hotels in Amsterdam with pets allowed for tomorrow 2 nights

Response

const result = await sapiClient.freeTextSuggest(
  "5 stars hotels in Amsterdam with pets allowed for tomorrow 2 nights"
);
[
  {
    "filters": {
      "starRating": 5,
      "facilities": [
        {
          "id": 8,
          "value": "Pet Friendly"
        }
      ]
    },
    "checkIn": "2024-06-12",
    "checkOut": "2024-06-14",
    "highlightValue": "<em>Amsterdam</em>",
    "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 <em>Amsterdam</em>, an IHG Hotel",
    "objectID": "1363313",
    "objectType": "hotel",
    "placeDisplayName": "Weesperbuurt en Plantage, Amsterdam, The Netherlands",
    "placeTypeName": "property",
    "value": "InterContinental Amstel Amsterdam, an IHG Hotel"
  }
]

rooms() method

Rooms is a method of sapiClient for retrieving rooms information and offers for a particular itinerary:

const rooms = await sapiClient.rooms({
  hotelId: "1714808",
  checkIn: "2021-10-10",
  checkOut: "2021-10-11",
  rooms: "2",
});

Rooms parameters

nametypedescriptionrequiredexample
hotelIdstringHotel Id to retrieve roomsyes1714808
checkInstringCheck in date (YYYY-MM-DD)yes2021-10-10
checkOutstringCheck out date (YYYY-MM-DD)yes2021-10-11
roomsstringRooms configurationyes2
providerCodestringProvider code used for retrieving rooms offersnoGAR
cugDealsstringType of cug (closed user group) deals to retrievenosigned_in,offline
tierstringUser tiernoplus
clickedOfferIdstringOffer id that user clicked on SRPnooXstvXafDb2o
clickedOfferBaseRatenumberBase rate value of the offer which user clicked on SRPno25
clickedOfferTaxesnumberTaxes value of the offer which user clicked on SRPno10
clickedOfferHotelFeesnumberHotel fees value of the offer which the user clicked on SRPno5
preHeatnumberEnables pre-heat modeno1
labelstringOpaque value that will be passed to tracking systemsnogha-vr
userIdstringUser ID is an authenticated user ID, e.g. the Google ID of a user.no7ea95191-b929-469d-8b02-0397dafd53a1
emailDomainstringUser email domain is for authenticated user as a value, if email is available.no@findhotel.net
deviceIdstringDevice ID (the IDFV of an iOS device or the GAID of an Android device)no
searchIdstringSearchId override (SDK generates a new one if no provided)no08230dfcc5f0fb95caaa82ce559ea60c4a975d6f
metadataRecord<string, string>Additional data to be passed to APIno{vclid: '21d2d152-de19-4f39-b40a-d4bc3002c6e8'}
bundleIdstringBundle Id clicked on SRP. Used to fetch bundle data from cacheno13b5d152-de19-4f39-b40a-d4bc3002c3u9

Response

{
  "anonymousId": "fd9dbb5f-b337-4dd7-b640-1f177d1d3caa",
  "hotelId": "1714808",
  "searchId": "08230dfcc5f0fb95caaa82ce559ea60c4a975d6f",
  "rooms": [
    {
      "amenities": ["Non-Smoking Facility"],
      "bedTypes": [],
      "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"]
        }
      ]
    }
  ]
}

hotel() method

Hotel is a method of sapiClient for retrieving a single hotel by id

const hotel = await sapiClient.hotel("1196472");

Hotel parameters

nametypedescriptionrequiredexample
hotelIdstringHotel Idyes1196472

Response

An example of the response for request with hotelId 1196472 and "pt-BR" language

{
  "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": "<p>Os seguintes depósitos e taxas são cobrados pelo hotel no momento do serviço prestado, durante o check-in ou check-out. </p> <ul> <li>Taxa para café da manhã em estilo buffet: EUR 25.00 por pessoa (aproximadamente)</li>  <li>Taxa para o acesso à internet com fio no quarto: EUR 15 por dia (as tarifas podem variar)</li> </ul> <p>A lista acima pode estar incompleta. As taxas e os depósitos podem não incluir impostos e estão sujeitos a mudanças. </p>",
  "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": "<ul>  <li>Este estabelecimento oferece transporte da estação de trem. Os hóspedes devem entrar em contato com o estabelecimento antes da viagem com os detalhes da chegada usando as informações para contato presentes na confirmação da reserva. </li> <li>É necessário reservar os seguintes serviços: serviços de massagem. As reservas podem ser feitas contatando este hotel antes da viagem usando as informações para contato fornecidas na confirmação da reserva. </li> <li>Somente hóspedes registrados podem ter acesso aos quartos. </li></ul>",
  "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": "<ul>  <li>Pessoas extras podem incorrer em taxas adicionais que variam dependendo da política do estabelecimento</li><li>Documento de identificação oficial com foto e cartão de crédito, cartão de débito ou depósito em dinheiro podem ser exigidos no momento do check-in para despesas extras.</li></ul>",
  "reviewCount": 17538,
  "chainID": "2101",
  "checkInEnd": "01:00",
  "lastBooked": 1608030831,
  "checkOutTime": "12:00",
  "feesMnd": "<p>Você deverá pagar os seguintes encargos no estabelecimento:</p> <ul><li>Depósito: EUR 50 por noite</li><li>A cidade cobra um imposto de EUR 3.00 por pessoa, por noite até 21 noites. Esse imposto não se aplica a crianças de até 16 anos. </li><li>Será cobrado um imposto municipal/local de 6.422 %</li></ul> <p>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. </p>",
  "deposit": "Deposit: USD 300.00 per accommodation<br>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"
}

hotels() method

Hotel is a method of sapiClient for retrieving multiple hotels by hotel ids

const hotels = await sapiClient.hotels(["1714808", "1380416", "1710829"]);

Hotels parameters

nametypedescriptionrequiredexample
hotelIdsstring[]Hotel Idsyes['1714808','1380416','1710829']

Response

An example of the response for request with hotelId 1196472 and "pt-BR" language

{
  "1196472": {
    "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": "<p>Os seguintes depósitos e taxas são cobrados pelo hotel no momento do serviço prestado, durante o check-in ou check-out. </p> <ul> <li>Taxa para café da manhã em estilo buffet: EUR 25.00 por pessoa (aproximadamente)</li>  <li>Taxa para o acesso à internet com fio no quarto: EUR 15 por dia (as tarifas podem variar)</li> </ul> <p>A lista acima pode estar incompleta. As taxas e os depósitos podem não incluir impostos e estão sujeitos a mudanças. </p>",
    "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": "<ul>  <li>Este estabelecimento oferece transporte da estação de trem. Os hóspedes devem entrar em contato com o estabelecimento antes da viagem com os detalhes da chegada usando as informações para contato presentes na confirmação da reserva. </li> <li>É necessário reservar os seguintes serviços: serviços de massagem. As reservas podem ser feitas contatando este hotel antes da viagem usando as informações para contato fornecidas na confirmação da reserva. </li> <li>Somente hóspedes registrados podem ter acesso aos quartos. </li></ul>",
    "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": "<ul>  <li>Pessoas extras podem incorrer em taxas adicionais que variam dependendo da política do estabelecimento</li><li>Documento de identificação oficial com foto e cartão de crédito, cartão de débito ou depósito em dinheiro podem ser exigidos no momento do check-in para despesas extras.</li></ul>",
    "reviewCount": 17538,
    "chainID": "2101",
    "checkInEnd": "01:00",
    "lastBooked": 1608030831,
    "checkOutTime": "12:00",
    "feesMnd": "<p>Você deverá pagar os seguintes encargos no estabelecimento:</p> <ul><li>Depósito: EUR 50 por noite</li><li>A cidade cobra um imposto de EUR 3.00 por pessoa, por noite até 21 noites. Esse imposto não se aplica a crianças de até 16 anos. </li><li>Será cobrado um imposto municipal/local de 6.422 %</li></ul> <p>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. </p>",
    "deposit": "Deposit: USD 300.00 per accommodation<br>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"
  }
}

offers() method

Retrieves offers for a single hotel by provided parameters

const parameters = {
  hotelId: "1196472",
  checkIn: "2022-10-10",
  checkOut: "2022-10-11",
  rooms: "2",
};

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);

Offers parameters

nametypedescriptionrequiredexample
hotelIdstringHotel Idyes1196472
checkInstringCheck in date (YYYY-MM-DD) (SDK generates default date if no provided)no2022-10-10
checkOutstringCheck out date (YYYY-MM-DD)) (SDK generates default date if no provided)no2022-10-11
roomsstringRooms configurationno2
searchIdstringSearchId override (SDK generates a new one if no provided)no08230dfcc5f0fb95caaa82ce559ea60c4a975d6f
cugDealsstring[]Codes of closed user group deals to retrieve offersno['signed_in', 'offline']
tierstringUser tiernomember
freeCancellationboolean(Deprecated, use freeCancellation in the filters object instead) If true, only offers with free cancellation will be returnednotrue
isAnchorbooleanAnchor/Non anchor hotel (default false)notrue
originIdstringIdentifier of origin where the request was originatednoc3po6twr70
labelstringOpaque value that will be passed to tracking systemsnogha-vr
preferredRatenumberOffer's price user saw on a CA (meta) platformno196
metadataRecord<string, string>Additional data to be passed to APIno{esd: 'abcds', epv: 'qwert'}
filtersObjectObject with filtersno{pay_later:true}
offersSortstringIf present, sorts offers by price (see explanation)no
getAllOffersbooleanIf true SAPI returns all (up to 23) offers for the hotel (by default or if it's false SAPI returns only up to 3 top offers)no
optimizeRoomsbooleanIf 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 occupancyno
optimizeAnchorRoomsbooleanIf 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 occupancyno
sbcstringSplit Booking Configuration. If present, searches for split booking offersnosbc: '2~3'
promosstring[]The list of promos to enableno['web2app']

Offer sorting by one parameter for now. It is price, but there are different variants of sorting by price.:

  • price:base_asc - sort offers by base price ascending
  • price:total_asc - sort offers by total rate breakdown ascending
  • price:with_tax_asc - sort offers by base rate with tax ascending

It overwrites OSO sorting and sorting boost as well. If offersSort parameter is provided, then OSO sorting and sorting boost will be ignored.

Object with callbacks:

const callbacks = {
  onStart: (response) => {
    log("Offers started", response);
  },
  onOffersReceived: (response) => {
    log("Offers received", response);
  },
  onComplete: (response) => {
    log("Offers completed", response);
  },
};
  1. onStart()

    Runs at the beginning of each new offers request.
    Returns adjusted and validated offers request parameters.

  2. onOffersReceived()

    Runs on every received batch of offers.
    Returns hotel's offers (incomplete response).

  3. onComplete()

    Runs after offers request complete.
    Returns hotel's offers (complete response).

Response

An example of the response for request parameters:

const parameters = {
  hotelId: "1926746",
  checkIn: "2022-07-10",
  checkOut: "2022-07-11",
  rooms: "2",
};

The response with the detailed description can be found in the Offers Response

hotelAvailability() method

Returns availability of the provided hotels for different dates (aka price calendar)

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);

Hotel(s) availability search parameters

nametypedescriptionrequiredexample
hotelIdsstring, string[]Hotel id(s) to retrieve offers foryes['1380416','1359951']
startDatestringDate of first check in (YYYY-MM-DD)yes'2023-10-10'
endDatestringDate of last check in (including). Can't be further than 61 days from StartDate (YYYY-MM-DD)yes'2023-10-11'
nightsnumberNights is length of stayyes3
roomsstringRooms configurationyes'2'
cugDealsstring[]Codes of closed user group deals to retrieve offersno['signed_in', 'offline']
tierstringUser tierno'member'
originIdstringIdentifier of origin where the request was originatedno'c3po6twr70'

Callbacks

Object with callbacks:

const callbacks = {
  onStart: (response) => {
    log("Availability request started", response);
  },
  onAvailabilityReceived: (response) => {
    log("Availability received", response);
  },
  onComplete: (response) => {
    log("Availability completed", response);
  },
};
  1. onStart()

    Runs at the beginning of each new availability request.
    Returns adjusted and validated availability search parameters.

  2. onAvailabilityReceived()

    Runs on every received batch of availability.
    Returns availability with offers (incomplete response).

  3. onComplete()

    Runs after availability request is completed.
    Returns availability with offers (complete response).

Response

An example of the response:

{
  "1380416": {
    "2023-08-05": {
      "hotelID": "1380416",
      "cheapestRate": {
        "base": 531.9901869159,
        "taxes": 34.7298130841,
        "hotelFees": 0
      },
      "offers": [
        {
          "id": "oEfGbUTje2BE",
          "currency": "EUR",
          "availableRooms": null,
          "url": "https://r.vio.com?lbl=providerRateType%3Dmember&ofd=...",
          "rate": {
            "base": 531.9901869159,
            "taxes": 34.7298130841,
            "hotelFees": 0
          },
          "accessTier": "sensitive",
          "providerCode": "WTM",
          "intermediaryProvider": "FHT",
          "package": {
            "amenities": ["breakfast"],
            "canPayLater": false
          },
          "cancellationPenalties": [
            {
              "start": "2023-07-30T10:00:00Z",
              "end": "2023-08-02T09:59:00Z",
              "amount": 0,
              "currency": "EUR"
            }
          ],
          "tags": ["top_offer", "exclusive_cheapest_offer"],
          "metadata": {
            "providerRateType": "member",
            "feedID": ""
          },
          "roomName": "STANDARD DOUBLE/TWIN"
        }
      ]
    },
    "2023-08-06": {
      "hotelID": "1380416",
      "cheapestRate": {
        "base": 493.9438317757,
        "taxes": 32.2961682243,
        "hotelFees": 0
      },
      "offers": [
        {
          "id": "omVVqhCXEbIw",
          "currency": "EUR",
          "availableRooms": null,
          "url": "https://r.vio.com?lbl=providerRateType%3Dmember&ofd=book_uri...",
          "rate": {
            "base": 493.9438317757,
            "taxes": 32.2961682243,
            "hotelFees": 0
          },
          "accessTier": "sensitive",
          "providerCode": "WTM",
          "intermediaryProvider": "FHT",
          "package": {
            "amenities": ["breakfast"],
            "canPayLater": false
          },
          "cancellationPenalties": [],
          "tags": ["top_offer", "exclusive_cheapest_offer"],
          "metadata": {
            "providerRateType": "member",
            "feedID": ""
          },
          "roomName": "STANDARD DOUBLE/TWIN"
        }
      ]
    }
  }
}

reviews() method

Returns hotels customers reviews by hotel Id(s)

const result = await sapiClient.reviews(1099818);
const result = await sapiClient.reviews([1099818, 8596574]);

Reviews parameters

nametypedescriptionrequiredexample
hotelIdsstring, string[]Hotel Ids to get reviews foryes[1099818, 8596574]

Response

const result = await sapiClient.reviews(1099818);
{
  "1099818": [
    {
      "checkIn": "2023-04-07",
      "checkOut": "2023-04-10",
      "language": "en",
      "reviewDate": "2023-04-10 14:34:30",
      "reviewSource": "WOT",
      "reviewerName": "Pavel",
      "reviewerScore": 10,
      "text": "Great layout, friendly helpful staff, enjoyable stay"
    },
    {
      "checkIn": "2021-12-25",
      "checkOut": "2021-12-26",
      "language": "en",
      "reviewDate": "2022-01-05 23:21:58",
      "reviewSource": "WOT",
      "reviewerName": "Eamon",
      "reviewerScore": 8,
      "text": "Nice, clean and new"
    }
  ]
}

Rooms configuration

rooms is a string that encodes the configuration of rooms requested by the user and follows the next rules:

  • Rooms are separated by pipe |
  • Adults and children are separated by colon :
  • Children ages are separated by comma ,
Examples
  • 1:4,6|3 → Two rooms, one with one adult and two children ages four and six and the other with three adults and no children
  • 3 → One room with three adults and no children
  • 2:4 → One room with two adults and one child aged four
  • 1:0,13,16 → One room with one adult and three children (aged zero, thirteen and sixteen)

How to generate searchId?

Every search should have a searchId - a random unique string used for tracking purposes. SAPI SDK automatically generates a unique searchId each time clients call search(), rooms() or offers() methods. Sometimes it might be useful to preserve the searchId between multiple actions. For this case all mentioned methods accept searchId as a parameter and use it instead of internally generated one.

SAPI SDK exposes a special method generateSearchId to generate searchId which then can be used in the clients app.

Example
import { generateSearchId } from "@findhotel/sapi";

const searchId = generateSearchId();

const rooms = await sapiClient.rooms({
  hotelId: "47319",
  checkIn: "2023-10-10",
  checkOut: "2023-10-11",
  rooms: "2",
  searchId,
});

FAQs

Package last updated on 01 Aug 2024

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc