---
title: "Expedia Hotels API: Search Hotels and Rooms as JSON"
description: "Search Expedia hotels by region and dates, then list rooms and rate plans as JSON. Real parameters, a recorded response and 1 credit per call."
url: https://perceptlayer.com/blog/expedia-hotels-api-search-json
---

[Blog](https://perceptlayer.com/blog) / [API guides](https://perceptlayer.com/blog/category/api-guides)

# Expedia Hotels API: Search Hotels and Rooms as JSON

6 Jul 2026 Perceptlayer 7 min read

To search Expedia hotels as JSON, call `GET /v1/expedia/hotels-search` with a `region_id`, a check-in date and a check-out date. It returns a ranked list of properties with price, guest rating and a `property_id`. Pass that ID to `GET /v1/expedia/hotels-rooms` to get each room and its rate plans. Every call costs 1 credit, and failed calls cost nothing.

## Key takeaways

-   The flow is four endpoints on the [Expedia API](https://perceptlayer.com/apis/expedia): `destinations` finds a region, `hotels-search` lists properties, `hotels-rooms` lists rooms and rate plans, `hotels-detail` takes a `property_id`.
-   `hotels-search` requires `check_in`, `check_out` and `region_id`. Paging uses `limit` and `offset`, and the response includes `has_more` and `next_offset`.
-   `hotels-rooms` returns `total_price` and `price_per_night` for each `rate_plan_id`, in the currency named in the response.
-   Each call costs 1 credit, so looking up a region, searching it and pulling rooms for one hotel is 3 credits.
-   Expedia Group runs its own partner API, Rapid, for travel partners. If you're a partner, that's the first route to check.

## Which endpoints make up the Expedia hotel search flow?

Four endpoints cover a hotel search from a typed place name to a bookable room. You look up a destination, search hotels in it for a date range, then drill into one property for rooms or detail. All four are `GET` requests that return JSON and cost 1 credit each. The table lists the required and optional parameters.

| Endpoint | Returns | Required | Optional |
| --- | --- | --- | --- |
| `/v1/expedia/destinations` | Destinations | `query` | `limit` |
| `/v1/expedia/hotels-search` | Hotels Search | `check_in`, `check_out`, `region_id` | `adults`, `children`, `region_name`, `sort`, `limit`, `offset` |
| `/v1/expedia/hotels-rooms` | Hotels Rooms | `check_in`, `check_out`, `property_id` | `adults`, `children` |
| `/v1/expedia/hotels-detail` | Hotels Detail | `property_id` | `check_in`, `check_out`, `adults` |

Each endpoint has its own page with a playground: [Destinations](https://perceptlayer.com/apis/expedia/destinations), [Hotels Search](https://perceptlayer.com/apis/expedia/hotels-search), [Hotels Rooms](https://perceptlayer.com/apis/expedia/hotels-rooms) and [Hotels Detail](https://perceptlayer.com/apis/expedia/hotels-detail). The wider [Travel APIs](https://perceptlayer.com/verticals/travel) area lists the other travel sources in the catalogue.

## How do you authenticate and make the first call?

Send your key in the `X-API-Key` header on every request. Keys start with `sk_live_`, and where a header isn't possible you can use the `api_key` query parameter instead. A free account gets 1,000 credits every month after you verify an email address, with no credit card, so the calls below are free to try. The [authentication docs](https://docs.perceptlayer.com/authentication) cover the details.

Start with the destination lookup. `query` takes up to 100 characters, and `limit` is optional:

```bash
curl "https://api.perceptlayer.com/v1/expedia/destinations?query=Paris&limit=5" \
  -H "X-API-Key: $API_KEY"
```

Run it in the playground on the [Destinations page](https://perceptlayer.com/apis/expedia/destinations) to see the response for your own query, then use the region you want as `region_id` in the search. The recorded searches in this article use `region_id=2734` with `region_name=Paris, France`. If you don't have a key yet, [get a free API key](https://perceptlayer.com/signup) first.

## How do you search hotels for a date range?

`GET /v1/expedia/hotels-search` takes a `region_id`, `check_in` and `check_out`, and returns a ranked list of properties for those dates. Add `adults` for the party size, `region_name` as a label (up to 120 characters), and `limit` and `offset` to page. This request asks for the first 10 hotels in Paris for two adults:

```bash
curl "https://api.perceptlayer.com/v1/expedia/hotels-search?region_id=2734&region_name=Paris%2C+France&check_in=2026-11-20&check_out=2026-11-23&adults=2&limit=10" \
  -H "X-API-Key: $API_KEY"
```

### Parameters

| Parameter | Type | Required | Notes |
| --- | --- | --- | --- |
| `check_in` | string | yes | Dates in the recorded requests look like `2026-11-20` |
| `check_out` | string | yes | Same format as `check_in` |
| `region_id` | string | yes | From the destinations lookup |
| `adults` | integer | no | Number of adults |
| `children` | string | no | Children in the party |
| `region_name` | string | no | Up to 120 characters |
| `sort` | string | no | One of `recommended`, `price_low_to_high`, `review` |
| `limit` | integer | no | Hotels per page |
| `offset` | integer | no | Where the page starts |

### What does the response look like?

This is a recorded response for the request above, trimmed to the first hotel:

```json
{
  "success": true,
  "data": {
    "region_id": "2734",
    "check_in": "2026-11-20",
    "check_out": "2026-11-23",
    "hotels": [
      {
        "property_id": "564660",
        "position": 1,
        "name": "Mercure Paris Centre Tour Eiffel",
        "area": "Paris",
        "guest_rating": 8,
        "guest_rating_label": "Very good",
        "review_count": 1000,
        "total_price": 690,
        "price_per_night": 230,
        "free_cancellation": false,
        "sponsored": true,
        "amenities_preview": ["restaurant", "bar"],
        "currency": "EUR"
      },
      …
    ],
    "total_results": 500,
    "offset": 0,
    "has_more": true,
    "next_offset": 10
  },
  "meta": {
    "endpoint_family": "hotel_search",
    "billable": true
  }
}
```

A few fields are worth knowing before you build on them. The recording shows `position: 1` and `sponsored: true` on the hotel shown (and `sponsored: true` on both hotels recorded); the brief doesn't document what either field means, so check them against your own results before relying on them. The prices are in the `currency` field ( `EUR` here), and `total_price` for this stay is `price_per_night` times three nights: 3 × 230 = 690. Some hotels also carry an `original_price` next to `total_price`, which shows a discount. Each hotel also has `url` and `image_urls`, left out above.

### How do you page through results?

Use `has_more` and `next_offset`. The recorded response reports `total_results` of 500, `offset` of 0 and `next_offset` of 10 with `limit=10`, so the next call repeats the request with `offset=10`. The recording shows `has_more` as `true`; no final page is recorded, so check the last page's response yourself before you rely on its shape. Each page is a separate call at 1 credit, so reading 500 results at 10 per page is 50 calls × 1 credit = 50 credits. The [request and response docs](https://docs.perceptlayer.com/requests) describe the response envelope.

## How do you list rooms and rate plans for one hotel?

`GET /v1/expedia/hotels-rooms` takes a `property_id` from the search, the same dates, and optionally `adults` and `children`. It returns each room type with its features, amenities and a list of rate plans, each with a `rate_plan_id`, `total_price` and `price_per_night`. Rooms are where you see the spread within one hotel:

```bash
curl "https://api.perceptlayer.com/v1/expedia/hotels-rooms?property_id=564660&check_in=2026-11-20&check_out=2026-11-23&adults=2" \
  -H "X-API-Key: $API_KEY"
```

Recorded response for that request, trimmed to the first room:

```json
{
  "success": true,
  "data": {
    "property_id": "564660",
    "check_in": "2026-11-20",
    "check_out": "2026-11-23",
    "currency": "EUR",
    "rooms": [
      {
        "unit_id": "226385",
        "name": "Classic Room, 1 Double Bed",
        "features": ["18 sq m", "Sleeps 2"],
        "highlights": ["Soundproofed rooms", "Air-conditioning"],
        "amenities": ["Wheelchair accessibility", "Bathtub or shower"],
        "rate_plans": [
          {
            "rate_plan_id": "397104173",
            "total_price": 690,
            "price_per_night": 230
          },
          {
            "rate_plan_id": "397104181",
            "total_price": 825,
            "price_per_night": 275
          }
        ]
      },
      …
    ]
  },
  "meta": {
    "endpoint_family": "room_offers",
    "billable": true
  }
}
```

The first rate plan, at 690 for three nights, matches the `total_price` the search gave for the same hotel and dates. The second room in the recorded response, "Classic Room, 2 Single Beds", has plans at 700 and 825. In this recording the search price equals the lowest rate plan of the first room, and the rooms call shows what the other plans cost.

## What does the detail endpoint add?

`GET /v1/expedia/hotels-detail` takes a `property_id` and, optionally, `check_in`, `check_out` and `adults`. Only `property_id` is required. We haven't recorded a response for it, so we can't show its fields here. See the [Hotels Detail page](https://perceptlayer.com/apis/expedia/hotels-detail) for the fields and run it in the playground with your key.

## How do you chain the calls in Python?

Search first, then fetch rooms for the top results. The script uses only the parameters and fields in the recordings above, and you need `API_KEY` set. It searches Paris for two adults, takes the first three hotels and prints each room's rate plans:

```python
import os
import requests

BASE = "https://api.perceptlayer.com"
HEADERS = {"X-API-Key": os.environ["API_KEY"]}
DATES = {"check_in": "2026-11-20", "check_out": "2026-11-23"}

def get(path, **params):
    r = requests.get(BASE + path, headers=HEADERS, params=params, timeout=30)
    r.raise_for_status()
    return r.json()["data"]

search = get(
    "/v1/expedia/hotels-search",
    region_id="2734",
    region_name="Paris, France",
    adults=2,
    limit=10,
    **DATES,
)

for hotel in search["hotels"][:3]:
    print(hotel["name"], hotel["total_price"], hotel["currency"])
    rooms = get(
        "/v1/expedia/hotels-rooms",
        property_id=hotel["property_id"],
        adults=2,
        **DATES,
    )
    for room in rooms["rooms"]:
        for plan in room["rate_plans"]:
            print("  ", room["name"], plan["rate_plan_id"], plan["total_price"])
```

This run makes 1 search and 3 rooms calls: 4 calls × 1 credit = 4 credits. Add a `try` around `raise_for_status` and read the JSON error body when it fails; the [error docs](https://docs.perceptlayer.com/errors) list the codes. If a request times out, it may have succeeded and been billed, because requests have no idempotency key.

## What does it cost to run at scale?

Every call to these endpoints costs 1 credit, so cost is the call count. Watching 50 hotels for one stay, once a day for 30 days, is 50 × 30 = 1,500 rooms calls, or 1,500 credits. That's above the 1,000 free credits you get each month, and within the smallest 10,000-credit Starter pack. Purchased credits last 12 months, with no subscription. [Pricing](https://perceptlayer.com/pricing) has what credits cost.

Responses carry `X-Credits-Used` and `X-Credits-Remaining`, so you can track spend per call. Rates are per account and counted per minute: free accounts get 1 request a second, and paying accounts at least 5. A 429 response carries a `Retry-After` header with the seconds to wait. See [rate limits](https://docs.perceptlayer.com/rate-limits) and [credits](https://docs.perceptlayer.com/credits). For a rate-monitoring design, read [hotel price data for rate monitoring](https://perceptlayer.com/blog/hotel-price-data-rate-monitoring).

## Should you use Expedia's own API instead?

If you're a travel partner of Expedia Group, probably yes. Expedia Group runs a partner API called [Rapid](https://partner.expediagroup.com/en-us/solutions/build-your-travel-experience/rapid-api) for lodging search and booking, and its [developer support pages](https://developers.expediagroup.com/rapid/support) point to the partner support channels. Check there for current access terms. It's the natural route if you need to take bookings or want a contractual relationship with Expedia.

Perceptlayer fits a different job: reading search results as JSON without a partner agreement, for price comparison, research or an agent that needs live hotel prices. Data is collected from publicly available sources and returned as found at the time of the call. For other hotel sources, see the [Booking.com guide](https://perceptlayer.com/blog/booking-com-hotel-prices-api) and the [Google Hotels guide](https://perceptlayer.com/blog/google-hotels-api).

Next step: [get a free API key](https://perceptlayer.com/signup), then run `hotels-search` in the playground on the [Expedia API page](https://perceptlayer.com/apis/expedia).

## APIs in this post

-   [Expedia **1 credit per call** · 4 endpoints](https://perceptlayer.com/apis/expedia)

## Frequently asked questions

### Does Expedia have a public hotel search API?

Expedia Group offers a partner API, Rapid, for lodging search and booking, aimed at travel partners. For read-only hotel search results as JSON without a partner agreement, the `hotels-search` endpoint on Perceptlayer takes a region, dates and party size and returns ranked properties with prices.

### How much does an Expedia hotel search cost per call?

Each of the four Expedia endpoints costs 1 credit per successful call. A search plus rooms for one hotel is 2 credits. Failed calls cost nothing and return `X-Credits-Used: 0`. Free accounts get 1,000 credits a month, and [pricing](https://perceptlayer.com/pricing) shows what credit packs cost.

### How do I get the region\_id for a city?

Call `GET /v1/expedia/destinations` with `query` set to the place name, for example `query=Paris`, and use the region you want as `region_id` in the search. The recorded searches in this guide use `region_id=2734` for Paris, France.

### Why is the search price different from a room's price?

The search returns one headline price per hotel, such as 690 for three nights in the recorded response. The rooms endpoint lists every room and rate plan for the same dates, such as 690 and 825 for one room type. Use the rooms call when you need the full spread.

### What happens when the balance runs out?

The service is prepaid. When credits run out, calls return `insufficient_credits` with HTTP status 402 and nothing more is debited. There is no automatic top-up. Errors come back as JSON with a `code`, `message` and `request_id`, described in the [error docs](https://docs.perceptlayer.com/errors).

expedia hotel search api hotel prices travel rooms

## More from the blog

1.  [API guides](https://perceptlayer.com/blog/category/api-guides) 25 Aug 2026 Perceptlayer 8 min read
    
    ## [Booking.com Hotel Prices API: Search, Rooms and Reviews](https://perceptlayer.com/blog/booking-com-hotel-prices-api)
    
    Eleven Booking.com endpoints as JSON: search, prices, rooms and reviews, with parameters and a 1,000-hotel price check costed in credits.
    
2.  [API guides](https://perceptlayer.com/blog/category/api-guides) 24 Jul 2026 Perceptlayer 8 min read
    
    ## [Trivago Hotel Prices API: Deals and Search as JSON](https://perceptlayer.com/blog/trivago-api-hotel-prices)
    
    Call trivago suggest, search and deals as JSON: 1 credit per call, a recorded Paris search response, and the provider\_count field in a metasearch result.
    
3.  [API guides](https://perceptlayer.com/blog/category/api-guides) 22 Oct 2026 Perceptlayer 7 min read
    
    ## [Booking.com Reviews API: Hotel Review Scores as JSON](https://perceptlayer.com/blog/booking-com-reviews-api-json)
    
    Pull Booking.com review scores and guest reviews as JSON with two GET calls at 1 credit each. Parameters, sorting, paging and the cost of covering a city.
