# Get hotel rates

> Every room and rate combination for a specific hotel, with cancellation policies. Required before pre_book.

Endpoint: `POST /api/v1/hotels/get_rates`

Source: https://fx-port.com/docs/hotels/rates

Get every **room and rate** of one hotel for a stay: each with its meal plan, its cancellation schedule, its taxes and its price. The `bookHash` of the rate the user chooses is what you send to [`pre_book`](https://fx-port.com/docs/hotels/pre-book.md).

**Permission:** Read


> [!WARNING]
> **Sandbox: any hotel is priced, only the test hotels can be booked**
>
> `get_rates` returns the real rates of any hotel in the sandbox. A rate of another hotel than the supplier test hotels cannot be booked there: see [Sandbox testing](https://fx-port.com/docs/hotels/sandbox-testing.md). `test_hotel` has rates for the residencies `dz`, `ru` and `us` only.


## Request body

`POST /api/v1/hotels/get_rates`

| Field | Type | Required | Default | Description |
|---|---|---|---|---|
| `hotel_id` | string | Yes | | The FX-Port id of the hotel: `hotels_details[].id` of a [search](https://fx-port.com/docs/hotels/search.md), or a hit of the [name search](https://fx-port.com/docs/hotels/name-search.md). |
| `checkin` | string | Yes | | Check-in date, `YYYY-MM-DD`. From yesterday to 400 days ahead. |
| `checkout` | string | Yes | | Check-out date, after `checkin`. A stay is 1 to 30 nights. |
| `guests` | array | Yes | | One `{ adults, children }` object per room. See [Guests and rooms](https://fx-port.com/docs/hotels/guests-and-rooms.md). |
| `residency` | string | No | `gb` | Residency of the guests, a two-letter country code. Some rates exist for some residencies only. |
| `language` | string | No | `en` | Language of the room names (`fr` gives `Standard chambre`). |


> [!CAUTION]
> Do not send a `currency` field: it is refused with `400`. Prices come in the currency of your agency.


Send the **same** `guests` and `residency` as in the search, or the rates and prices will differ. A value written in another well-defined way (a number as text, a date with a midnight time, `GB-SCT` as residency ...) is read like in [`search_advanced`](https://fx-port.com/docs/hotels/search.md#values-the-api-reads-for-you), and a number sent as `hotel_id` is not looked up as a supplier `hid` here. Identical calls of your agency that arrive at the same moment are merged into one supplier call; each caller still gets its own `request_id` and headers.

### Legacy names

These older spellings still work. The answer carries a `deprecated_field` warning that names the spelling to use (see [Legacy names](https://fx-port.com/docs/legacy-names.md)).

| Older name | Use instead |
|---|---|
| `id`, `hotelId` | `hotel_id` |
| `check_in`, `checkinDate`, `checkin_date` | `checkin` |
| `check_out`, `checkoutDate`, `checkout_date` | `checkout` |

## Request examples


**One room**

**Request**

```bash
curl https://api.fx-port.com/api/v1/hotels/get_rates \
  -H "Authorization: Bearer fxp_test_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "hotel_id": "test_hotel_do_not_book",
  "checkin": "2026-12-01",
  "checkout": "2026-12-04",
  "residency": "gb",
  "language": "en",
  "guests": [
    {
      "adults": 2,
      "children": []
    }
  ]
}'
```

**Two rooms, with a child**

**Request**

```bash
curl https://api.fx-port.com/api/v1/hotels/get_rates \
  -H "Authorization: Bearer fxp_test_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "hotel_id": "test_hotel_do_not_book",
  "checkin": "2026-12-10",
  "checkout": "2026-12-12",
  "residency": "gb",
  "language": "en",
  "guests": [
    {
      "adults": 2,
      "children": [
        8
      ]
    },
    {
      "adults": 1,
      "children": []
    }
  ]
}'
```

**The other test hotel**

**Request**

```bash
curl https://api.fx-port.com/api/v1/hotels/get_rates \
  -H "Authorization: Bearer fxp_test_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "hotel_id": "test_hotel",
  "checkin": "2026-12-01",
  "checkout": "2026-12-04",
  "residency": "dz",
  "language": "en",
  "guests": [
    {
      "adults": 2,
      "children": []
    }
  ]
}'
```


## Response

A real answer of the sandbox, cut to the cheapest of the 14 rates of the test hotel (0.5 to 1.4 s, 23 KB for the 14 rates):

~~~json
{
  "environment": "sandbox",
  "success": true,
  "data": {
    "hotelId": "test_hotel_do_not_book",
    "hid": 8473727,
    "rates": [
      {
        "bookHash": "h-14574656-7ce9-5a21-b673-8654c5ebd416",
        "matchHash": "m-a1f40b22-c086-57c9-b2b5-70c8e59cb27c",
        "room": {
          "name": "Standard room",
          "beddingType": "single bed",
          "miscRoomType": null,
          "capacity": null,
          "bathroom": "private-bathroom",
          "hasPrivateBathroom": true,
          "balcony": false,
          "roomClass": "room",
          "quality": "standard",
          "view": null,
          "floor": null,
          "bedrooms": null,
          "isFamily": false,
          "isClub": false,
          "genderRestriction": null,
          "nonSmoking": true
        },
        "meal": {
          "type": "nomeal",
          "included": false,
          "childMealIncluded": false
        },
        "dailyPrices": [
          1,
          1,
          1
        ],
        "allotment": 77,
        "isPackage": false,
        "anyResidency": true,
        "deposit": null,
        "noShow": null,
        "cancellationPolicy": {
          "freeCancellationBefore": "2026-12-01T21:00:00",
          "policies": [
            {
              "from": null,
              "until": "2026-12-01T21:00:00",
              "originalAmount": 0,
              "originalCurrency": "EUR",
              "currency": "DZD"
            },
            {
              "from": "2026-12-01T21:00:00",
              "until": null,
              "originalAmount": 3,
              "originalCurrency": "EUR",
              "currency": "DZD"
            }
          ]
        },
        "pricing": {
          "supplierCurrency": "EUR",
          "agencyCurrency": "DZD",
          "originalPrice": 3,
          "agencyCommission": 2.97,
          "totalPrice": 1484.97,
          "taxes": {
            "cityTax": {
              "includedInPrice": false,
              "amount": 23.1,
              "currency": "HNL"
            },
            "electricityFee": {
              "includedInPrice": true,
              "amount": 0.6,
              "currency": "EUR"
            },
            "resortFee": {
              "includedInPrice": true,
              "amount": 0.68,
              "currency": "EUR"
            }
          }
        },
        "exchangeRate": 285
      }
    ]
  },
  "processingTime": 0.335
}
~~~

| Field | Description |
|---|---|
| `data.hotelId`, `data.hid` | The hotel, by FX-Port id and by the supplier's numeric id. |
| `data.rates[]` | The rates, **in no particular order**: sort them by `pricing.totalPrice` yourself. |
| `bookHash` | The token of the rate. Send it as `book_hash` to [`pre_book`](https://fx-port.com/docs/hotels/pre-book.md). Valid for **30 minutes**: after that, ask for the rates again. |
| `matchHash` | A second identifier of the rate. **It is not a booking token**: `pre_book` does not read it. Send `bookHash`. |
| `room` | The room: `name` (in your `language`), bedding, bathroom, class, quality, view, size flags. The full room types, with photos, are in the content of the hotel ([`get_hotel_info`](https://fx-port.com/docs/hotels/hotel-details.md), `roomGroups`): match them by room name. |
| `meal` | `type`, `included` and `childMealIncluded`. The types are `nomeal`, `breakfast`, `lunch`, `dinner`, `half-board`, `half-board-lunch`, `half-board-dinner`, `full-board`, `all-inclusive` and `ultra-all-inclusive`. |
| `dailyPrices` | The supplier's price of each night, in the supplier's currency (EUR). For information. |
| `allotment` | How many rooms of this type are left at this rate. |
| `anyResidency` | `false` when the rate is reserved for some residencies: it exists for the `residency` of your request only. |
| `isPackage` | `true` when the rate is part of a package. |
| `cancellationPolicy` | When the booking can be cancelled for free, and the penalty windows after that. See below. |
| `pricing`, `exchangeRate` | The price of the rate, for the whole stay and all rooms, and the exchange rate used. See [Hotel pricing](https://fx-port.com/docs/hotels/pricing.md). |
| `processingTime` | Seconds the call took. |

The keys you feed back or compute with also come with a snake_case twin: `book_hash`, `match_hash`, `daily_prices`, `exchange_rate`, `pricing.total_price`, `pricing.agency_commission`, `pricing.agency_currency`, `pricing.supplier_currency`, `cancellationPolicy.free_cancellation_before`, `data.hotel_id` and `processing_time`. See [Legacy names](https://fx-port.com/docs/legacy-names.md).

## Choosing a rate

- **A room type appears many times**: one rate per meal plan and per cancellation condition. Group the rates by `room.name` and show the cheapest of each, with what differs (meal, cancellation).
- **Show what is paid at the hotel.** `pricing.taxes` lists taxes and fees; those with `includedInPrice: false` are paid by the traveler at the property, on top of `totalPrice`, in the currency the supplier states (it can be the local currency, such as `HNL`).
- **Only prepaid rates are returned.** Rates that are paid at the hotel are not offered, because FX-Port bookings are paid from your agency balance. The price of the search is the cheapest rate of the supplier, so in rare cases the cheapest rate here is a little higher.
- **A rate is for one occupancy and one residency.** If the user changes a guest or the country, call again.

## Cancellation policy

`cancellationPolicy` says what a cancellation costs, window after window:

- `freeCancellationBefore` is the moment until which the booking can be cancelled **for free**, in UTC, without a zone suffix (`2026-12-01T21:00:00`). `null` means the rate is **non-refundable**.
- `policies[]` is the schedule from now to the check-in and after: the free window first (`originalAmount: 0`), then the windows with a penalty.
- The amounts are the **supplier's**: `originalAmount` is in `originalCurrency` (EUR). The `currency` field only labels your agency currency, and **no** amount in your currency is given: see [Cancellation penalties](https://fx-port.com/docs/hotels/pricing.md#cancellation-penalties). What is refunded is in the answer of [`cancel_hotel`](https://fx-port.com/docs/hotels/cancel.md).

## No rate is not an error

A hotel that is unknown, closed, not available for the dates or not available for the residency answers `200` with `hotelId: null` and an empty list. **Check `rates.length`**; do not wait for a `404`:

~~~json
{
  "environment": "sandbox",
  "success": true,
  "data": {
    "hotelId": null,
    "hid": null,
    "rates": []
  },
  "processingTime": 0.16
}
~~~

If you expected rates, check the dates, the occupancy, the residency (`test_hotel` has none for `gb` or `fr`) and `isClosed` in the content of the hotel.

## Errors

| HTTP | Code | Cause |
|---|---|---|
| `400` | `missing_field` | `hotel_id`, `checkin`, `checkout` or `guests` is missing. `errors[0].field` names the first missing field and `errors[0].details.missing_fields` lists all of them when several are missing. The message is the older sentence (`Missing required fields: ['guests']`). |
| `400` | `bad_request` | You sent a `currency` field. |
| `400` | `invalid_date`, `date_in_past`, `date_too_far`, `checkout_before_checkin`, `stay_too_long` | The dates are not a real stay. |
| `400` | `invalid_guests`, `invalid_adult_count`, `too_many_children`, `invalid_child_age`, `too_many_guests`, `too_many_rooms` | The rooms break [the rules](https://fx-port.com/docs/hotels/guests-and-rooms.md#constraints). |
| `400` | `invalid_residency`, `invalid_language`, `invalid_hotel_id` | The code is not valid. |
| `502` | `supplier_invalid_response` | The supplier answered in a form that could not be used. Retry in a few minutes. |
| `503` | `supplier_unavailable`, `supplier_timeout`, `supplier_busy` | The supplier could not be reached, did not answer in time, or is at capacity. With `supplier_busy` the call was **refused before anything was sent**: retry after the seconds in `Retry-After`. Otherwise retry with backoff. |
| `401`, `403` | | The key is missing, wrong or not allowed: [Authentication](https://fx-port.com/docs/authentication.md). |
| `429` | `rate_limit_exceeded` | Too many calls: [Rate limits](https://fx-port.com/docs/rate-limits.md). |

A search that the supplier itself refuses comes as `400` too, with the supplier's own code in `errors[0].code` and `errors[0].type` `supplier_error`.

## Next step

Take the `bookHash` of the chosen rate and call [`pre_book`](https://fx-port.com/docs/hotels/pre-book.md) to lock its price.

## Related


- [Hotel pricing](https://fx-port.com/docs/hotels/pricing.md): What `totalPrice`, `agencyCommission` and the taxes mean, and what you are debited.
- [Pre-book a rate](https://fx-port.com/docs/hotels/pre-book.md): Lock the price of the chosen rate.
- [Guests and rooms](https://fx-port.com/docs/hotels/guests-and-rooms.md): The occupancy of a request.
- [API reference: Get the rooms and rates of one hotel](https://fx-port.com/docs/api/get-hotel-rates.md): Every field and error.
