Skip to main content
Amenities describe what a hotel or room type offers, for example a sauna, parking, or a balcony. Hotels and room types have separate sets of amenities, called scopes: hotel and roomType.

Amenity dictionary

GET /api/v2/amenity-categories

Fetch the amenity dictionary.
The dictionary is grouped by category. Each item in data contains a category and its amenities. The whole dictionary is returned in one response, without pagination. Accept-Language selects the label languages. A category is identified by scope and code; a code is unique only within its scope. Each amenity has a code that identifies it, a localized label, and detailKinds, the details that can be specified for it. Amenities with the options detail kind list their choices in options.
Dictionary excerpt (scope=hotel)

Hotel categories

Room type categories

Assigned amenities

Hotel and room type responses group the assigned amenities by category in amenityCategories. Categories without assigned amenities are omitted. Categories, amenities, and options follow the dictionary order. An amenity that is not listed is not offered or not specified. Each amenity value contains the amenity code and the fields of its detail kinds.

Labels

Responses identify categories, amenities, and options by code. To display them, either:
  • Match the codes to the dictionary. Use scope hotel for hotel amenities and roomType for room type amenities, including room types embedded in a hotel. Then match category.code, the amenity code, and each option code.
  • Request expand=amenityLabels. The response then includes a localized label on each category, amenity, and option.

Included with this room

Room benefits belong to the inclusions category in scope roomType. Fetch them as part of the amenity dictionary:
Dictionary category excerpt
Room type lists, details, and hotel responses with expand=roomTypes return only the benefits assigned to each room type. Find them in amenityCategories:
Room with all three benefits
The category is omitted when no benefits are assigned. Labels are available in the dictionary or with expand=amenityLabels.

Parking for a room

Room parking is returned in the room type’s inclusions category. pricing.type: "included" means parking is included in that room’s price. Hotel parking is returned separately in the hotel’s parking category, including its localized note. Room type responses do not inherit hotel parking details.

Detail kinds

An amenity’s detailKinds lists the details that can be specified for it. Each detail kind adds fixed fields to the amenity value: A value contains the fields of all declared detail kinds, even when a detail is not specified, and no other detail fields. The order of detailKinds has no meaning.

Proximity

proximity says where the amenity is located: in-hotel (inside the hotel building), connecting (in an adjacent building linked by an internal passage), or nearby (a short distance from the hotel). proximityMeters is the approximate distance in metres, from 0 to 1,000,000. It is set only when proximity is nearby.

Pricing

pricing is null when the price conditions are unknown. Otherwise it is an object with type and schedule, for hotel and room type amenities alike. A pricing block uses days, from, and to, like opening hours. Both times can be null: entry is then included throughout the opening hours on those days. Otherwise both times are specified. A schedule does not imply that entry outside its windows is paid. Price conditions and opening hours are independent. A free-entry window can be specified when opening hours are unknown. When both are known, windows lie within opening hours, including any breaks. The response uses days without times for windows that cover the whole opening day. If a window covers the entire known opening week, the response uses included instead. Monetary amounts are not structured fields. Any price description remains text in note; these values do not change the calculated booking price.

Coverage

coverage describes whether an amenity is available in all physical rooms of a room type or only some: all means every room; some means only some rooms. An assigned amenity that declares this detail kind always returns one of these values, never null. An unspecified value defaults to all. The room type dictionary declares coverage on these amenities:
Room type bathroom excerpt
In this example, showers and baths are each available in some rooms. some does not guarantee that a booked room has the amenity. The response does not identify individual rooms or describe which amenities occur together. Other amenities omit coverage, including hotel amenities and the room type inclusions category. Room parking uses the pricing detail kind.

Options

options lists the selected choices by code, for example the types of sauna. The amenity’s dictionary entry lists every choice, in the same order. With expand=amenityLabels, each option also has a localized label.

Opening hours

openingHours is null when unknown. Otherwise, its non-empty schedule lists the opening intervals for the week:
  • days contains weekdays from monday to sunday, each at most once, in week order.
  • from and to are hotel local times in HH:mm format. Both are required. 24:00 is allowed only in to and means the end of that day.
  • to is later than from. Intervals across midnight use two blocks.
  • Blocks that share a day do not overlap. Adjacent intervals are combined; identical intervals on different days share a block.
  • A day absent from a known schedule is closed. An unknown schedule does not indicate closure.
A 24/7 schedule has one block containing all seven days, from: "00:00", and to: "24:00". Blocks are ordered by their first weekday, then start and end time. Opening hours do not specify whether entry is included.
Opening hours of a cafe

Includes

The aquapark amenity’s includes array lists its facilities. The dictionary entry declares includedAmenityCodes: indoor_pool, outdoor_pool, children_pool, whirlpool, and sauna, in that order. Each code references an amenity definition in the hotel scope. The array contains each selected code once and does not nest further. Each item already contains its resolved location, price conditions, opening hours, and note. Sauna also contains options. Read these values directly; you do not need to compute inheritance. Labels and option labels are included with expand=amenityLabels. Location and distance come from the aquapark. Other values use the facility’s own conditions when specified, otherwise the aquapark’s. An inherited free-entry window is limited to the facility’s known opening hours. No overlap yields pricing: null, which does not mean paid entry. When parent hours are unknown, inherited days without times remain days without times; they do not establish unrestricted free entry. A facility’s own note replaces the whole aquapark note, including translations. A missing translation remains null. Note and price conditions are resolved independently. The hotel’s own pool or sauna remains a separate top-level amenity. An included facility is not automatically duplicated there. An empty includes means the aquapark’s contents are unspecified.
Aquapark with a sauna (Accept-Language: en)

Note

note is additional information for guests, up to 200 characters per language. It follows Accept-Language; a missing translation is null.

Presence-only amenities

An amenity with an empty detailKinds array has no details. Its value contains only code, plus label with expand=amenityLabels. Examples include hairdryer, kettle, welcome_drink, and fruit_bowl. The dictionary lists the detail kinds for each amenity.

Filtering

GET /api/v2/hotels and GET /api/v2/room-types accept two amenity filters. Each endpoint filters by its own scope. When you combine both filters, both must match. Unknown codes return 400. The filters match assigned amenity codes, regardless of details such as proximity, pricing, or coverage. For example, amenities=balcony matches room types with either coverage: "all" or coverage: "some". For hotels, a facility can match as an own amenity or through an aquapark’s includes, regardless of price conditions. For example, amenities=indoor_pool,sauna can match an own pool and an aquapark sauna. A nearby facility also matches; use the returned proximity to narrow the results. Filtering happens before pagination, so pagination.total counts all matching hotels.
The room type parking filter matches room assignments only. It does not match rooms solely because their hotel offers parking. Check the returned pricing to determine whether parking is included. To find hotels by room type amenities, filter room types and use their hotelId.

Caching

The dictionary changes only with SpaPortal releases. To keep a cached copy current:
  1. Store the dictionary with its ETag and X-SpaPortal-Amenities-Version response headers. To cache all label languages, omit Accept-Language or send Accept-Language: *.
  2. Compare the X-SpaPortal-Amenities-Version header of hotel and room type responses with your stored version. When it differs, fetch the dictionary again.
  3. To revalidate, repeat the same request with If-None-Match: <stored ETag>. The API returns 304 without a body while your copy is current.
If a response contains an amenity code that is not in your copy, refresh the dictionary.