Hotel details
The full page of one hotel, or its compact card with detail_level. Reads the cache and fetches an unknown hotel live.
POST/api/v1/hotels/get_hotel_info
Read one hotel for the page the user opens before checkout: the description, every room type, the photos, the policies, the facts and how to reach the hotel. It is the only content call that asks the supplier when FX-Port does not hold the hotel yet.
Permission: Read
Request body
POST /
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
hotel_id | string or integer | One of the two | One hotel: its FX-Port id (hotels_details[].id, a hit of the name search) or its numeric supplier id (hid). The only form that asks the supplier when the hotel is not cached. | |
hotel_ids | array | One of the two | Several hotels, at most 200. Cached hotels only: the others are left out. Wins over hotel_id when both are sent. | |
detail_level | string | No | full | full: the whole document, in camelCase. minimal (alias mini): the compact card, in snake_case. |
images_limit | integer | No | With detail_level: "minimal": keep only the first N photos of each card, 0 to 30. With full it is ignored and the answer carries a coerced_value warning. | |
language | string | No | en | Language of the live fetch of a hotel that is not cached. Cached content is English whatever you send. |
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 |
|---|---|
hotelId, id, hid | hotel_id |
hotelIds, ids | hotel_ids |
detailLevel | detail_level |
imagesLimit | images_limit |
Request examples
curl https://api.fx-port.com/api/v1/hotels/get_hotel_info \
-H "Authorization: Bearer fxp_test_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"hotel_id": "test_hotel_do_not_book",
"language": "en"
}'curl https://api.fx-port.com/api/v1/hotels/get_hotel_info \
-H "Authorization: Bearer fxp_test_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"hotel_id": "test_hotel_do_not_book",
"detail_level": "minimal",
"images_limit": 1
}'curl https://api.fx-port.com/api/v1/hotels/get_hotel_info \
-H "Authorization: Bearer fxp_test_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"hotel_ids": [
"test_hotel_do_not_book",
"hotel_honduras_maya_6"
],
"detail_level": "minimal"
}'How the hotel is found
- With
hotel_id, FX-Port looks for the id in its cache of hotel content. A number (or a numeric string) is taken as the supplier'shidand looked up the same way. - A single
hotel_idthat is not cached is fetched from the supplier (about 0.3 s), stored for every customer, and answered. This is how you get the card of a hotel listed inmissing_hotel_idsof a search: see Hotels without content. - With
hotel_idsnothing is fetched: the hotels that are cached come back, the others are left out without an error, and the answer is{"data": []}when none is cached.
A single hotel_id that no source knows is 404 hotel_not_found.
Response
data is always a list, with one entry for a single hotel_id. The entry is the full document (camelCase), here shortened to one element per list:
{
"environment": "sandbox",
"data": [
{
"id": "test_hotel_do_not_book",
"hid": 8473727,
"name": "Test Hotel (Do Not Book) test",
"kind": "Apartment",
"hotelChain": "No chain",
"isClosed": false,
"isGenderSpecificationRequired": false,
"address": "Avenida Roble, Frente al Mall Multiplaza, Tegucigalpa",
"postalCode": null,
"latitude": 14.079872,
"longitude": -87.21677,
"region": {
"id": 3421,
"name": "Tegucigalpa",
"countryCode": "HN",
"iata": "TGU",
"type": "City"
},
"starRating": 0,
"starCertificate": null,
"contact": {
"email": "[email protected]",
"phone": "+15555550123"
},
"checkIn": "14:00:00",
"checkOut": "12:00:00",
"frontDeskHours": {
"start": "00:00:00",
"end": "00:00:00"
},
"images": [
"https://cdn.worldota.net/t/{size}/extranet/b5/d3/b5d3d33394494c68321246882c1bd93a6832dcd5.jpeg"
],
"amenityGroups": [
{
"groupName": "General",
"amenities": [
"24-hour reception",
"Early check-in",
"Late check-out"
],
"paidAmenities": []
}
],
"serpFilters": [
"has_internet",
"has_parking",
"kitchen"
],
"descriptionStruct": [
{
"title": "Location",
"paragraphs": [
"A perfect fit for a big group of travellers: apartment «Test Hotel (Do Not Book)» ..."
],
"html": "<h3>Location</h3><p>A perfect fit for a big group of travellers ...</p>"
}
],
"metapolicyExtraInfo": "Front desk is open 24/7.",
"metapolicyStruct": {
"internet": [
{
"inclusion": "included",
"price": "0.00",
"currency": null,
"price_unit": "unspecified",
"internet_type": "unspecified",
"work_area": "hotel"
}
],
"pets": [
{
"inclusion": "not_included",
"price": "560.00",
"currency": "USD",
"price_unit": "per_room_per_stay",
"pets_type": "unspecified"
}
],
"children": [
{
"age_start": 0,
"age_end": 3,
"price": "500.00",
"currency": "CHF",
"extra_bed": "available"
}
]
},
"roomGroups": [
{
"roomGroupId": 95,
"name": "Standard Double room full double bed",
"nameStruct": {
"mainName": "Standard Double room",
"beddingType": "full double bed",
"bathroom": "shared bathroom"
},
"images": [
"https://cdn.worldota.net/t/{size}/extranet/b5/d3/b5d3d33394494c68321246882c1bd93a6832dcd5.jpeg"
],
"roomAmenities": [
"bath",
"heating"
],
"roomClass": "room",
"quality": "standard",
"bathroom": "private-bathroom",
"hasPrivateBathroom": true,
"capacity": 2,
"balcony": false,
"view": null,
"floor": null,
"bedrooms": null,
"beddingType": "double-bed",
"isFamily": false,
"isClub": false,
"genderRestriction": null
}
],
"facts": {
"floorsNumber": null,
"roomsNumber": null,
"yearBuilt": null,
"yearRenovated": null,
"electricity": {
"voltage": [],
"frequency": [
60
],
"sockets": [
"a",
"b"
]
}
},
"paymentMethods": [
"visa",
"maestro",
"cash"
],
"keysPickup": {
"type": "lockbox",
"isContactless": true,
"email": "[email protected]",
"phone": "+15555550123",
"officeAddress": null,
"extraInfo": "The keys are in the box by the front door."
}
}
]
}The reference describes every field: get_hotel_info for this camelCase form and The Hotel full object for the snake_case form of the same data. Things to know when you read it:
checkInandcheckOutare times of the day (14:00:00, local time), not dates.imagesare URL templates with{size}: build the picture URL as described in Images.imagescan benull.roomGroupsis about 80% of the document. Each room type carries its own photos and amenities, with its properties decoded into words (roomClass,quality,beddingType,hasPrivateBathroom,balcony,view).metapolicyStructtells what is paid at the property (parking, extra beds, pets, deposits). Readchildrenbefore you say a child stays free. Its entries keep snake_case keys (price_unit,age_start).isClosed: truemeans the property is closed at the supplier:get_rateswill find no rate.- The key style follows
detail_level.fullanswers camelCase;minimalanswers the snake_case card of The Hotel mini object. Do not mix them in one parser.
Eight keys of the full document come twice, as camelCase and as a snake_case twin, so that you can move to snake_case at your pace: hotelChain/hotel_chain, isClosed/is_closed, postalCode/postal_code, starRating/star_rating, starCertificate/star_certificate, checkIn/check_in_time, checkOut/check_out_time and region.countryCode/region.country_code. Add ?naming=snake to get every key in snake_case. See Conventions and naming.
Good practice
- Cache the document by hotel id (not by language: the content is English). A day is a safe duration.
- Call it once, when the user opens the hotel, not for every hotel of a list.
- Fetch a missing hotel with one id per call, five calls at a time at most, and remember the failures for a few minutes. The supplier answers about 30 hotel content calls a minute for all of FX-Port. On a
503 supplier_busywait the seconds ofRetry-After: the call was refused before anything was sent. - Send
"language": "en"when the hotel may not be cached: the fetched content is stored for every customer in the language of that call.
Errors
| HTTP | Code | Cause |
|---|---|---|
400 | missing_field | Neither hotel_id nor hotel_ids is sent (Hotel IDs are required; errors[0].field is hotel_ids). |
400 | invalid_hotel_id, invalid_hotel_ids | An id is not 1 to 128 characters (letters, digits and _ . : -; or a positive integer for a hid), or hotel_ids is not a list. |
400 | too_many_ids | More than 200 ids in hotel_ids. |
400 | invalid_detail_level, invalid_images_limit, invalid_language | The value is not one of the documented ones. |
404 | hotel_not_found | A single hotel_id that neither FX-Port nor the supplier knows. |
429 | supplier_rate_limited | The supplier is receiving too many content calls (only when FX-Port asks it for a single hotel). Retry after a minute. |
502 | supplier_error, supplier_invalid_response | The supplier could not process the request, or answered in a form that could not be used. Retry with backoff. |
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. |