Skip to content

Accommodation API

Live hotel prices and availability, for any stay and any traveler#

Price the hotels you track by their IDs, up to 10,000 in one call, search every hotel of a destination, or build a price calendar for one hotel across up to 500 date ranges. Prices are read live from Booking.com, Expedia and Ostrovok for the exact stay you describe: dates, rooms, adults, children's ages, the traveler's country, the currency and the device.

  • AccessAPI key
  • Platforms3 supported
  • Endpoint POST /accommodations/fetch/{network} POST /accommodations/fetch/{network}/{propertyId} GET /accommodations/lookup
  • CommitmentNone: pay per use, no minimum

At a glance

  • Up to 10,000 hotel IDs priced in one call
  • Price calendars: up to 500 date ranges for one hotel
  • Any room setup: rooms, adults, children aged 0–18
  • Quote from a traveler's country, in a chosen currency, on desktop or mobile
  • Prices with and without taxes and fees, plus a ready booking link

Why teams choose Zembra for this#

Performance

Synchronous: thousands of hotels are priced in parallel and returned in the same response.

Scale

10,000 hotel IDs per call, every hotel of a destination in one search, 500 date ranges per hotel.

Precision

Prices for the exact stay: check-in and check-out dates, each room's adults and children's ages, quoted as a traveler from a given country.

Transparent prices

Booking.com prices come back both as the displayed price and as the total including excluded taxes and fees. When a hotel shows several offers, the cheapest is kept.

Ready to act

Every result includes a booking link pre-filled with the dates, rooms and guests, the hotel's photo, location and review score.

Fair pricing

Priced per block of 1,000 hotels or 25 date ranges. Requests that fail, or that the platform can't serve, cost nothing.

How it works

From request to data#

  1. 1

    Pick how you price

    Send the hotel IDs you track, a destination to search, or one hotel with a list of date ranges.

  2. 2

    Describe the stay

    Dates, rooms with adults and children's ages, and optionally the traveler's country, the currency and forMobile.

  3. 3

    Zembra queries the platform

    Requests go out in parallel, from the traveler's country when you set one. For a destination search, Zembra pages through every result the platform announces.

  4. 4

    Get standardized prices

    Each hotel comes back with its price, availability, review score, photo, address and booking link, in the same format for every platform.

Pricing

Pay per use, no commitment#

Pay per hotels priced, per date ranges or per search; requests the platform can't serve cost nothing. Credits get cheaper as your volume grows: how credit pricing works.

What a call costs#

CallCreditsStarts at
Price by ID, per started block of 1,000 hotels
1 to 1,000 IDs cost 240; 1,001 to 2,000 cost 480.
240 $0.048
Price calendar, per started block of 25 date ranges
1 to 25 ranges cost 100; up to 500 ranges cost 2,000.
100 $0.02
Destination search, first platform
Per search.
125 $0.025
Destination search, each extra platform
Per extra platform available at the time of the search.
100 $0.02
No result or unknown hotel (404)
Destination search shown: a 404 is charged like a successful call and not refunded, for every Accommodation endpoint.
125 $0.025
Errors and outages
Invalid parameters, platforms not enabled, rate limits and platform errors (503) are not charged.
0 Free

Examples#

ProjectCreditsPrice
Price 5,000 hotels once 1,200 Free on sign-up
Price 1,000 hotels daily for 30 days 7,200 Free on sign-up
One hotel, 365 one-night stays 1,500 Free on sign-up
One destination search 125 Free on sign-up

Prices use the credit tiers of the cost estimator, where you can estimate your own project. The first 10,000 credits are free.

API reference

Requests and parameters#

Base URL https://api.zembra.io, authenticated with Authorization: Bearer <token>. A sandbox at https://sandbox.zembra.io returns realistic dummy data without using credits.

GET /accommodations/lookup?networks[]=&searchString=
cURL
  • cURL
  • .Net
  • Go
  • Java
  • Node.js
  • PHP
  • Python
  • Ruby
curl --location 'https://api.zembra.io/accommodations/lookup?searchString=Rome%2C%20IT&targetCountry=US&currency=USD&checkin=2023-01-01&checkout=2023-01-04&rooms=&latitude=25.77178&longitude=-80.19009' \
--header 'Authorization: Bearer {token}'
var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Get, "https://api.zembra.io/accommodations/lookup?searchString=Rome, IT&targetCountry=US&currency=USD&checkin=2023-01-01&checkout=2023-01-04&rooms=&latitude=25.77178&longitude=-80.19009");
request.Headers.Add("Authorization", "Bearer {token}");
var response = await client.SendAsync(request);
response.EnsureSuccessStatusCode();
Console.WriteLine(await response.Content.ReadAsStringAsync());
package main

import (
  "fmt"
  "net/http"
  "io/ioutil"
)

func main() {

  url := "https://api.zembra.io/accommodations/lookup?searchString=Rome%2C%20IT&targetCountry=US&currency=USD&checkin=2023-01-01&checkout=2023-01-04&rooms=&latitude=25.77178&longitude=-80.19009"
  method := "GET"

  client := &http.Client {
  }
  req, err := http.NewRequest(method, url, nil)

  if err != nil {
    fmt.Println(err)
    return
  }
  req.Header.Add("Authorization", "Bearer {token}")

  res, err := client.Do(req)
  if err != nil {
    fmt.Println(err)
    return
  }
  defer res.Body.Close()

  body, err := ioutil.ReadAll(res.Body)
  if err != nil {
    fmt.Println(err)
    return
  }
  fmt.Println(string(body))
}
OkHttpClient client = new OkHttpClient().newBuilder()
  .build();
MediaType mediaType = MediaType.parse("text/plain");
RequestBody body = RequestBody.create(mediaType, "");
Request request = new Request.Builder()
  .url("https://api.zembra.io/accommodations/lookup?searchString=Rome, IT&targetCountry=US&currency=USD&checkin=2023-01-01&checkout=2023-01-04&rooms=&latitude=25.77178&longitude=-80.19009")
  .method("GET", body)
  .addHeader("Authorization", "Bearer {token}")
  .build();
Response response = client.newCall(request).execute();
var myHeaders = new Headers();
myHeaders.append("Authorization", "Bearer {token}");

var requestOptions = {
  method: 'GET',
  headers: myHeaders,
  redirect: 'follow'
};

fetch("https://api.zembra.io/accommodations/lookup?searchString=Rome, IT&targetCountry=US&currency=USD&checkin=2023-01-01&checkout=2023-01-04&rooms=&latitude=25.77178&longitude=-80.19009", requestOptions)
  .then(response => response.text())
  .then(result => console.log(result))
  .catch(error => console.log('error', error));
<?php
$client = new Client();
$headers = [
  'Authorization' => 'Bearer {token}'
];
$request = new Request('GET', 'https://api.zembra.io/accommodations/lookup?searchString=Rome, IT&targetCountry=US&currency=USD&checkin=2023-01-01&checkout=2023-01-04&rooms=&latitude=25.77178&longitude=-80.19009', $headers);
$res = $client->sendAsync($request)->wait();
echo $res->getBody();
import requests

url = "https://api.zembra.io/accommodations/lookup?searchString=Rome, IT&targetCountry=US&currency=USD&checkin=2023-01-01&checkout=2023-01-04&rooms=&latitude=25.77178&longitude=-80.19009"

payload = {}
headers = {
  'Authorization': 'Bearer {token}'
}

response = requests.request("GET", url, headers=headers, data=payload)

print(response.text)
require "uri"
require "net/http"

url = URI("https://api.zembra.io/accommodations/lookup?searchString=Rome, IT&targetCountry=US&currency=USD&checkin=2023-01-01&checkout=2023-01-04&rooms=&latitude=25.77178&longitude=-80.19009")

https = Net::HTTP.new(url.host, url.port)
https.use_ssl = true

request = Net::HTTP::Get.new(url)
request["Authorization"] = "Bearer {token}"

response = https.request(request)
puts response.read_body

Response

{
    "message": "List of properties for Miami",
    "data": {
        "properties": {
            "booking": {
                "network": "booking",
                "id": 123456789,
                "name": "The Grand Hotel",
                "district": "Larger San Diego",
                "distanceFromDestination": "1.3 miles from downtown",
                "address": {
                    "street": "123 Main St.",
                    "city": "San Diego",
                    "zipCode": "92078-4605",
                    "country": "United States",
                    "countryCode": "us",
                    "latitude": 25.77178,
                    "longitude": -80.19009
                },
                "price": {
                    "total": 1562.78,
                    "withoutFees": 1344.99,
                    "currency": "USD"
                },
                "reviews": {
                    "score": 9.2,
                    "count": 465
                },
                "link": "",
                "photo": ""
            }
        },
        "counts": {
            "returned": {
                "booking": 501
            },
            "displayable": {
                "booking": 2786
            },
            "total": 501
        }
    }
}
{
    "message": "Invalid parameter limit"
}
{
    "message": "Invalid bearer token"
}
{
    "message": "Cannot process request: balance too low!"
}
{
    "message": "Requested network [Expedia] is not enabled for your account"
}
{
    "message": "Hotels not found!"
}

POST /accommodations/fetch/{network}: Price the hotels you track, by ID#

networkpath, required: booking or expedia
The platform the IDs belong to. One platform per call.
propertyIdsarray of numbers, required, 1–10,000
The hotels' IDs on the platform. Duplicates are ignored.
checkindate YYYY-MM-DD, required
Up to 500 days from today.
checkoutdate YYYY-MM-DD, required
After check-in, for a stay shorter than 90 nights.
roomsarray, required
One object per room: adults (at least 1) and optional children, a list of ages from 0 to 18.
targetCountryISO 3166-1 alpha-2, optional
Quote the price as a traveler from this country.
currencyISO 4217, optional
The currency of the prices. Default USD.
forMobileboolean, optional
Quote the price shown on mobile.

POST /accommodations/fetch/{network}/{propertyId}: Price calendar for one hotel#

networkpath, required: booking
The platform.
propertyIdpath, required, number
The hotel's ID on the platform.
dateRangesarray, required, 1–500
Each entry has a checkin and a checkout, with the same rules as above. Duplicates are ignored.
rooms, targetCountry, currency, forMobileas above
rooms is required.

GET /accommodations/lookup: Search every hotel of a destination#

networks[]array, required
The platforms to search: booking.
searchStringstring, required
The destination: a city, region, district, landmark or hotel name.
latitude, longitudenumbers, optional, sent together
When the destination's name is ambiguous, the suggestion closest to these coordinates is used.
checkin, checkoutdates YYYY-MM-DD, required
The stay.
rooms, targetCountry, currency, forMobileas above
rooms is required.

Status codes#

200Prices returned
400Invalid parameter: dates, rooms, country, currency or platform
401Missing or invalid token
402Balance too low
403Platform not enabled on your account
404No hotel for this search, or unknown hotel ID (charged)
429Rate limit reached
503Platform temporarily unavailable, not charged

Data

What you get back#

A hotel#

id, namenumber or string, string
The hotel's ID on the platform and its name.
availableboolean
Whether the hotel can be booked for the stay (search, Expedia, and each date range).
price.totalnumber
The price of the whole stay. On Booking.com it includes the taxes and fees shown separately.
price.withoutFeesnumber
Booking.com: the displayed price, without those taxes and fees.
price.currencystring
The currency of the prices.
addressobject
street, city, zipCode, country, countryCode, latitude, longitude (Booking.com).
district, distanceFromDestinationstring
The area and the distance to the searched destination, as the platform shows them.
reviewsobject
score and count: the hotel's review score and number of reviews (search and Expedia).
linkURL
The hotel's page, pre-filled with the dates, rooms and guests.
photoURL
The hotel's main photo.
dateRangesarray
Price calendar only: for each range, dates, available, price and link.
Example response
{
    "status": "SUCCESS",
    "message": "List of properties for Miami",
    "data": {
        "properties": {
            "booking": [
                {
                    "id": 123456,
                    "name": "The Grand Hotel Miami",
                    "available": true,
                    "district": "Downtown Miami, Miami",
                    "distanceFromDestination": "0.6 miles from downtown",
                    "address": {
                        "street": "123 Biscayne Blvd",
                        "city": "Miami",
                        "zipCode": "33132",
                        "country": "United States",
                        "countryCode": "us",
                        "latitude": 25.77178,
                        "longitude": -80.19009
                    },
                    "reviews": {
                        "score": 8.7,
                        "count": 1465
                    },
                    "link": "https://www.booking.com/hotel/us/the-grand-miami.html?dest_id=123456&selected_currency=USD&aid=304142&checkin=2026-12-10&checkout=2026-12-13&dest_type=hotel&group_adults=2&req_adults=2&no_rooms=1&group_children=0&req_children=0&sr_order=popularity&sb_price_type=total&type=total#hprt-table",
                    "photo": "https://cf.bstatic.com/xdata/images/hotel/max1024x768/123.jpg",
                    "price": {
                        "total": 1562.78,
                        "withoutFees": 1344.99,
                        "currency": "USD"
                    }
                }
            ]
        },
        "counts": {
            "returned": {
                "booking": 512
            },
            "displayable": {
                "booking": 512
            },
            "total": 512
        }
    },
    "cost": 125,
    "balance": 98875
}

Three ways to price#

By ID (POST /accommodations/fetch/{network}) for the hotels you track, on Booking.com or Expedia. Price calendar (POST /accommodations/fetch/{network}/{propertyId}) for one Booking.com or Ostrovok hotel across many date ranges. Destination search (GET /accommodations/lookup) for every hotel of a city, region, district or landmark on Booking.com, or of a destination on Ostrovok.

The destination is resolved with the platform's own suggestions; add latitude and longitude to pick the right one when the name is ambiguous. Zembra then pages through every result the platform announces. counts.displayable is what the platform announced, counts.returned what came back.

Sold out and unavailable hotels#

When pricing by ID on Booking.com, hotels with no price for the stay are left out of the response. On Expedia they come back with available: false and no price. Sold out, closed, minimum-stay rules or too few rooms for the guests are the usual reasons.

Traveler country and currency#

targetCountry sends the request from that country, so you see the prices its travelers see. On Booking.com an unsupported currency falls back to the hotel's local currency. On Expedia, the country selects the matching Expedia site, and a currency that site doesn't offer falls back to its default currency.

Desktop or mobile prices#

Platforms sometimes show different prices on mobile. Set forMobile to quote what a mobile traveler sees.

Taxes and fees#

On Booking.com, price.total adds the taxes and fees the platform shows separately to the displayed price, which is in price.withoutFees.

Performance

Speed and limits#

Hotels per call #

1 to 10,000 IDs, all from the same platform.

Date ranges per call #

1 to 500, for one hotel.

Dates #

Check-in up to 500 days from today; check-out after check-in and less than 90 nights later.

Guests #

At least one room, each with at least 1 adult; children aged 0 to 18.

Rate limit #

100 requests per minute by default, per account and per service; 429 beyond that.

Sandbox #

The Accommodation API is not available in the sandbox.

Supported platforms

3 platforms, one format#

Use cases

What teams build with it#

Rate shopping

Price your hotel and your competitors every day, for the same stay and the same guests.

Rate parity monitoring

Check that a hotel shows the same price on Booking.com and Expedia, in every country and on mobile.

Revenue management

Build price calendars of up to 500 date ranges to see how rates move across dates.

Market studies

Pull every hotel of a destination with its price, score and location to map supply and pricing.

FAQ

Common questions#

How many hotels can I price in one call?

Up to 10,000 hotel IDs per call, all from the same platform. #

Which platforms are supported?

Booking.com and Expedia for pricing by ID; Booking.com and Ostrovok for price calendars and destination search. #

Can I see prices from another country?

Yes. Set targetCountry to quote as a traveler from that country, and currency for the currency. On Expedia, only the currencies of the matching Expedia site are available. #

Can I price one hotel across many dates?

Yes. Send up to 500 date ranges for one Booking.com or Ostrovok hotel in one call, at 100 credits per 25 ranges. #

Do prices include taxes and fees?

On Booking.com you get both: price.total with the taxes and fees shown separately, and price.withoutFees without them. #

What happens to sold-out hotels?

On Booking.com they're left out of the response; on Expedia they come back with available: false. #

Am I charged if the platform fails?

No. Platform errors return 503 and cost nothing; retry after a few minutes. #

Start today for free

Create an account and get 10,000 free credits to try the Accommodation API and every other Zembra service. No commitment, no minimum.