Get hotel rates
Every room and rate combination for a specific hotel, with cancellation policies. Required before pre_book.
POST/api/v1/hotels/get_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.
Permission: Read
Request body
POST /
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
hotel_id | string | Yes | The FX-Port id of the hotel: hotels_details[].id of a search, or a hit of the name search. | |
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. | |
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). |
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, 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).
| Older name | Use instead |
|---|---|
id, hotelId | hotel_id |
check_in, checkinDate, checkin_date | checkin |
check_out, checkoutDate, checkout_date | checkout |
Request examples
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": []
}
]
}'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": []
}
]
}'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):
{
"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. 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, 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. |
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.
Choosing a rate
- A room type appears many times: one rate per meal plan and per cancellation condition. Group the rates by
room.nameand show the cheapest of each, with what differs (meal, cancellation). - Show what is paid at the hotel.
pricing.taxeslists taxes and fees; those withincludedInPrice: falseare paid by the traveler at the property, on top oftotalPrice, in the currency the supplier states (it can be the local currency, such asHNL). - 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:
freeCancellationBeforeis the moment until which the booking can be cancelled for free, in UTC, without a zone suffix (2026-12-01T21:00:00).nullmeans 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:
originalAmountis inoriginalCurrency(EUR). Thecurrencyfield only labels your agency currency, and no amount in your currency is given: see Cancellation penalties. What is refunded is in the answer ofcancel_hotel.
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:
{
"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. |
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. | |
429 | rate_limit_exceeded | Too many calls: Rate limits. |
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 to lock its price.