API v2 · Hotels
Agent brief

Everything we hold on one hotel.

The full profile: gallery, rooms and their listings, venues, policies, reviews and prices for a stay.

GET /v2/hotels/{hotel_id}$0.005 / call
182 photos, no duplicatesThe gallery arrives deduplicated and labelled, with the hero image picked for you.

images.count · 182

Rooms carry their listingsEach room lists where else it’s sold, already matched, so you never reconcile them yourself.

rooms[].listings[]

Ask only for what you renderName the sections you need and the response leaves out the rest.

sections=core,images,rooms

One call returns the whole profile. Ask for fewer sections to keep it small. Prices appear only when you send check_in and check_out; there are no undated prices.

Pass the search_id from a search made in the last 30 minutes and the detail is already warm. The price is the same either way.

Hotels

Hotel detail

GET/v2/hotels/{hotel_id}
Mode
Sync · 200
Price
$0.005 / call
SKU
hotel.detail, rates.direct.live
Auth
Scope content

Synchronous; needs the content scope and bills hotel.detail once per call. Prices appear only when check_in and check_out are both set; rates=refresh also starts a rates.live job, which needs the rates scope and the Build plan or higher and bills rates.direct.live when it completes. sections limits the response to the parts you ask for.

Billing

hotel.detail per call. rates=refresh also starts a live rates job, billed as rates.direct.live when it completes.

Parameters

NameInTypeDescription
hotel_id
REQUIRED
pathstring (uuid)The hotel's hotel_id (uuid), as returned by resolve or search.
sectionsqueryobjectComma-separated sections to return, any of core, images, rooms, venues, policies, facts, reviews, guest_voice, rates, location; all of them when omitted.
check_inquerystring (date)Arrival date, YYYY-MM-DD; rates are only returned when both dates are set.
check_outquerystring (date)Departure date, YYYY-MM-DD, after check_in and sent together with it.
adultsqueryobjectAdults across all rooms for the rates lookup; defaults to 2 per room.
children_agesqueryobjectComma-separated ages (0-17) of the children; cached rates cover adults-only parties, so any child returns no prices and a rates_party_unsupported warning.
roomsqueryobjectNumber of rooms for the rates lookup; defaults to 1.
currencyqueryobjectISO 4217 currency of the caller; prices are never converted and stay in the currency they were observed in.
localequeryobjectLanguage for descriptions and other translated text, as a language tag (pt-BR reads as pt); English when omitted or untranslated.
search_idqueryobjectThe search_id of a recent search that returned this hotel, which lets the call reuse the content that search prepared; the price is the same.
ratesquerystringcached serves stored observations; refresh also starts a live rates job (needs the rates scope and the Build plan, billed rates.direct.live). One of cached, refresh.
image_widthqueryintegerWidth in pixels of every image URL returned: 640, 1024 or 1600. One of 640, 1024, 1600.
reviews_limitqueryintegerMaximum review items in reviews.items.

Example

Request
curl 'https://api.vistalink.com/v2/hotels/43d0be71-5f3c-4c8e-9a52-1d7e2b6f0a19?sections=core%2Cimages%2Crooms%2Crates&check_in=2026-11-14&check_out=2026-11-16&adults=2&currency=EUR&search_id=srch_3f9c2a7e4b1d4c8a9e6f0b2d5a7c1e93' \
  -H "Authorization: Bearer $VISTALINK_API_KEY"
Response
200
{
  "billing": {
    "amount": "0.005",
    "currency": "USD",
    "sku": "hotel.detail",
    "units": 1
  },
  "core": {
    "amenities": [
      "POOL"
    ],
    "check_in_time": "15:00",
    "check_out_time": "11:00",
    "description": "Boutique hotel near the Trevi Fountain.",
    "hotel_type": "hotel",
    "languages": [
      "en",
      "it"
    ],
    "name": "Umiltà 36",
    "operating_status": "open",
    "phone": "+39 06 000000",
    "review_count": 2056,
    "review_score": 9.1,
    "short_character": "quiet, central",
    "star_rating": 5,
    "website_url": "https://umilta36.com"
  },
  "hotel_id": "43d0be71-5f3c-4c8e-9a52-1d7e2b6f0a19",
  "images": {
    "count": 182,
    "hero": {
      "image_id": "a1f3c5e7-9b2d-4f6a-8c0e-2d4f6a8c0e1b",
      "label": "building",
      "url": "https://images.vistalink.com/h/43d0be71/ext.jpg?w=1024"
    },
    "items": [
      {
        "height": 683,
        "image_id": "b2e4d6f8-0a1c-4e3a-9d5b-7f9a1c3e5d2c",
        "label": "room",
        "position": 1,
        "url": "https://images.vistalink.com/h/43d0be71/room.jpg?w=1024",
        "width": 1024
      }
    ]
  },
  "location": {
    "address": "Via dell'Umiltà 36",
    "city": "Rome",
    "country": "IT",
    "lat": 41.9,
    "lng": 12.48,
    "neighbourhood_text": "Trevi",
    "transport_notes": "Metro A Barberini, 600 m"
  },
  "provenance": {
    "images_release": "7c1e9a3f-2b4d-4e6a-8f0c-5d9b1a3e7f24",
    "profile_source": "site",
    "profile_updated_at": "2026-10-07T09:30:00Z"
  },
  "rates": {
    "channels": [
      {
        "channel": "direct",
        "engine": "SYNXIS",
        "observed_at": "2026-10-07T09:30:00Z",
        "offers": [
          {
            "availability": "available",
            "board": "breakfast",
            "channel": "direct",
            "currency": "EUR",
            "engine": "SYNXIS",
            "excluded_charges": [],
            "free_cancellation": false,
            "includes_taxes": true,
            "listing_key": "engine_room:1234567",
            "nightly_avg": "481.00",
            "observed_at": "2026-10-07T09:30:00Z",
            "occupancy": {
              "adults": 2,
              "children_ages": []
            },
            "offer_id": "off_t3c8e1a9b4f2d7e6a5c0b",
            "rate_name": "Non-refundable, breakfast included",
            "room_id": "5b1e2f3a-0c4d-4e5f-8a9b-1c2d3e4f5a6b",
            "room_name": "Superior Room",
            "total": "962.00",
            "url": "https://umilta36.com/book?checkin=2026-11-14&checkout=2026-11-16"
          }
        ],
        "status": "observed"
      }
    ],
    "stay": {
      "check_in": "2026-11-14",
      "check_out": "2026-11-16",
      "rooms": [
        {
          "adults": 2,
          "children_ages": []
        }
      ]
    }
  },
  "request_id": "req_8f2c1a9b3d4e5f60",
  "rooms": [
    {
      "accessible": false,
      "amenities": [
        "AIR_CONDITIONING"
      ],
      "balcony": false,
      "bed_config": [
        {
          "count": 1,
          "type": "king"
        }
      ],
      "description": "City view.",
      "images": [],
      "kitchen": false,
      "listings": [
        {
          "listing_key": "site:superior",
          "match_confidence": "high",
          "name": "Superior Double Room",
          "provider": "umilta36.com",
          "source": "site",
          "url": "https://umilta36.com/rooms/superior"
        }
      ],
      "max_occupancy": 3,
      "name": "Superior Room",
      "occupancy": 2,
      "offers": [
        {
          "availability": "available",
          "board": "breakfast",
          "channel": "direct",
          "currency": "EUR",
          "engine": "SYNXIS",
          "excluded_charges": [],
          "free_cancellation": false,
          "includes_taxes": true,
          "listing_key": "engine_room:1234567",
          "nightly_avg": "481.00",
          "observed_at": "2026-10-07T09:30:00Z",
          "occupancy": {
            "adults": 2,
            "children_ages": []
          },
          "offer_id": "off_t3c8e1a9b4f2d7e6a5c0b",
          "rate_name": "Non-refundable, breakfast included",
          "room_id": "5b1e2f3a-0c4d-4e5f-8a9b-1c2d3e4f5a6b",
          "room_name": "Superior Room",
          "total": "962.00",
          "url": "https://umilta36.com/book?checkin=2026-11-14&checkout=2026-11-16"
        }
      ],
      "room_id": "5b1e2f3a-0c4d-4e5f-8a9b-1c2d3e4f5a6b",
      "size_m2": 28,
      "views": [
        "city"
      ]
    }
  ],
  "sections_present": [
    "core",
    "location",
    "images",
    "rooms",
    "rates"
  ]
}

Response 200

Successful Response

FieldTypeDescription
request_id
REQUIRED
stringOpaque id of this request, also sent as the X-Request-Id header; quote it when contacting support.
billingBilling | nullWhat a billed call cost. Present on every billed response; billing is null on free ones.
hotel_id
REQUIRED
string (uuid)The hotel's id.
sections_presentstring[]Requested sections that have data for this hotel, in a fixed order; rates is listed only when some channel has prices.
coreobject | nullIdentity, ratings and contact details.
core.name
REQUIRED
stringHotel name.
core.hotel_typestring | nullKind of property as catalogued, free text such as hotel; null when unknown.
core.star_ratingnumber | nullOfficial star rating, 1 to 5; null when unrated.
core.review_scorenumber | nullAverage guest review score on a 0 to 10 scale; null when there are no reviews.
core.review_countinteger | nullNumber of guest reviews behind review_score, which can differ from reviews.count (the reviews we hold).
core.descriptionstring | nullLong description of the hotel in locale when translated, else English; null when there is none.
core.short_characterstring | nullOne line on what the hotel is like, from its published profile; null without a profile.
core.chainstring | nullHotel chain the property belongs to; null when independent or unknown.
core.brandstring | nullBrand within the chain; null when none or unknown.
core.operating_statusstring | nullWhether the hotel is trading, for example open, closed_temporarily, closed, opening_soon or unknown; null without a profile.
core.check_in_timestring | nullEarliest check-in time, usually HH:MM in local time; null when unknown.
core.check_out_timestring | nullLatest check-out time, usually HH:MM in local time; null when unknown.
core.phonestring | nullHotel phone number as published.
core.website_urlstring (uri) | nullThe hotel's own website.
core.booking_urlstring (uri) | nullThe hotel's listing URL on the third-party channel; null when it has none.
core.amenitiesstring[]Uppercase amenity codes, as listed by GET /v2/amenities.
core.languagesstring[]Languages the staff speak, as listed in the hotel profile, for example en; empty when unknown.
locationobject | nullPosition, address and getting there.
location.latnumber | nullLatitude in decimal degrees.
location.lngnumber | nullLongitude in decimal degrees.
location.addressstring | nullStreet address on one line: street and number, postcode, city.
location.citystring | nullCity the hotel is in.
location.countrystring | nullISO 3166-1 alpha-2 country code, or the country name when no code is known.
location.neighbourhood_textstring | nullA paragraph on the neighbourhood, in locale when translated; null without a profile.
location.transport_notesstring | nullHow to get there by public transport, in locale when translated; null without a profile.
imagesobject | nullGallery in curated order.
images.heroobject | nullThe lead image; null when the hotel has no images.
images.hero.image_idstring (uuid) | nullImage id; the same image keeps the same id across responses.
images.hero.url
REQUIRED
string (uri)CDN URL of the image at image_width pixels wide (never upscaled).
images.hero.labelstring | nullWhat the image shows, for example building, room, bathroom or swimming pool outdoor; null when unlabelled.
images.hero.room_idstring (uuid) | nullThe room this image shows, matching rooms[].room_id; null for images of the hotel as a whole.
images.hero.widthinteger | nullWidth in pixels of the image at url; null when unknown.
images.hero.heightinteger | nullHeight in pixels of the image at url; null when unknown.
images.hero.positioninteger | null1-based position in gallery order; null on hero and venue images.
images.countintegerHow many distinct images the hotel has, which can exceed the 50 in items. Default 0.
images.itemsDetailImage[]Up to 50 images in gallery order.
images.items[].image_idstring (uuid) | nullImage id; the same image keeps the same id across responses.
images.items[].url
REQUIRED
string (uri)CDN URL of the image at image_width pixels wide (never upscaled).
images.items[].labelstring | nullWhat the image shows, for example building, room, bathroom or swimming pool outdoor; null when unlabelled.
images.items[].room_idstring (uuid) | nullThe room this image shows, matching rooms[].room_id; null for images of the hotel as a whole.
images.items[].widthinteger | nullWidth in pixels of the image at url; null when unknown.
images.items[].heightinteger | nullHeight in pixels of the image at url; null when unknown.
images.items[].positioninteger | null1-based position in gallery order; null on hero and venue images.
roomsDetailRoom[] | nullPhysical room types with their listings and prices.
rooms[].room_idstring (uuid) | nullRoom id, stable across responses and used by Offer.room_id.
rooms[].name
REQUIRED
stringRoom name.
rooms[].descriptionstring | nullRoom description in locale when translated, else English; null when there is none.
rooms[].size_m2number | nullRoom size in square metres; null when unknown.
rooms[].occupancyinteger | nullStandard number of guests the room is sold for; null when unknown.
rooms[].max_occupancyinteger | nullMost guests the room can hold, including extra beds; equals occupancy when no maximum is known.
rooms[].bed_configBedConfig[]Beds in the room; empty when unknown.
rooms[].bed_config[].type
REQUIRED
stringBed type as the source names it, for example king, double, twin or sofa bed.
rooms[].bed_config[].countintegerNumber of beds of this type. Default 1.
rooms[].viewsstring[]Views from the room, for example city or sea.
rooms[].balconyboolean | nullTrue when the room has a balcony or terrace, false when it does not, null when unknown.
rooms[].accessibleboolean | nullTrue when the room is wheelchair accessible, false when it is not, null when unknown.
rooms[].kitchenboolean | nullTrue when the room has a kitchen or kitchenette, false when it does not, null when unknown.
rooms[].amenitiesstring[]Uppercase amenity codes of the room.
rooms[].imagesDetailImage[]Up to 6 images of the room.
rooms[].images[].image_idstring (uuid) | nullImage id; the same image keeps the same id across responses.
rooms[].images[].url
REQUIRED
string (uri)CDN URL of the image at image_width pixels wide (never upscaled).
rooms[].images[].labelstring | nullWhat the image shows, for example building, room, bathroom or swimming pool outdoor; null when unlabelled.
rooms[].images[].room_idstring (uuid) | nullThe room this image shows, matching rooms[].room_id; null for images of the hotel as a whole.
rooms[].images[].widthinteger | nullWidth in pixels of the image at url; null when unknown.
rooms[].images[].heightinteger | nullHeight in pixels of the image at url; null when unknown.
rooms[].images[].positioninteger | null1-based position in gallery order; null on hero and venue images.
rooms[].listingsRoomListing[]The same room on other channels, matched to this room.
rooms[].listings[].source
REQUIRED
stringWhere the listing is: site (the hotel's website), engine (its booking engine), booking (the third-party channel) or expedia. One of booking, site, engine, expedia.
rooms[].listings[].providerstring | nullWho publishes the listing, for example official_site or the booking engine's name.
rooms[].listings[].namestring | nullRoom name as that listing shows it.
rooms[].listings[].listing_keystring | nullThe listing's own key, such as room_type:1234567, matching Offer.listing_key where the channel has one.
rooms[].listings[].urlstring (uri) | nullPage of the listing; null when there is none.
rooms[].listings[].match_confidencestring | nullHow sure the match between this listing and the room is: high or medium (low-confidence candidates are not shown); null when not scored. One of high, medium, low.
rooms[].offersOffer[]Prices for this room for the requested stay; empty without dates or when none were observed.
venuesVenue[] | nullRestaurants, bars, spa and other places at the hotel.
venues[].venue_idstring | nullVenue id, stable across responses.
venues[].kind
REQUIRED
stringWhat the venue is, for example dining, bar, cafe, spa, pool, gym, beach or kids.
venues[].sectionstring | nullGroup the venue is shown under: dining, wellness, activities, families, business, parking, transport, shops or spaces.
venues[].namestring | nullVenue name, in locale when translated.
venues[].summarystring | nullA short description, in locale when translated.
venues[].cuisinestring | nullType of cuisine for dining venues, free text such as seafood; null otherwise.
venues[].opening_hoursstring | nullOpening hours as one line of free text; null when not published.
venues[].price_pointstring | nullPrice level, free text such as $$$; null when unknown.
venues[].accessVenueAccess[]Who may use the venue and on what terms.
venues[].access[].subject
REQUIRED
stringWho the rule is for: hotel (all guests), or room:<room_id>, board_basis:<key> or audience:<key> for a subset.
venues[].access[].access
REQUIRED
stringTerms of use, for example included, surcharge, members, restricted or adults_only.
venues[].itemsVenueItem[]Menu items or services offered.
venues[].items[].name
REQUIRED
stringItem name, in locale when translated.
venues[].items[].price_textstring | nullPrice as published, free text such as EUR 18; null when not published.
venues[].imagesDetailImage[]Images of the venue.
venues[].images[].image_idstring (uuid) | nullImage id; the same image keeps the same id across responses.
venues[].images[].url
REQUIRED
string (uri)CDN URL of the image at image_width pixels wide (never upscaled).
venues[].images[].labelstring | nullWhat the image shows, for example building, room, bathroom or swimming pool outdoor; null when unlabelled.
venues[].images[].room_idstring (uuid) | nullThe room this image shows, matching rooms[].room_id; null for images of the hotel as a whole.
venues[].images[].widthinteger | nullWidth in pixels of the image at url; null when unknown.
venues[].images[].heightinteger | nullHeight in pixels of the image at url; null when unknown.
venues[].images[].positioninteger | null1-based position in gallery order; null on hero and venue images.
policiesPolicy[] | nullHouse rules and terms.
policies[].kind
REQUIRED
stringPolicy topic, for example checkin, checkout, children, extra_bed, pets, smoking, cancellation, deposit, payment, city_tax, breakfast, parking or other.
policies[].summarystring | nullThe policy in one or two sentences, in locale when translated.
policies[].structuredobject | nullThe policy's terms as key-value data when the source gives them, for example times or age limits; shape depends on kind.
factsFact[] | nullOther facts about the hotel.
facts[].kind
REQUIRED
stringFact topic, for example history_moment, brand, loyalty, language, trivia, transport_distance, sustainability_practice or other.
facts[].label
REQUIRED
stringShort heading for the fact.
facts[].value
REQUIRED
stringThe fact itself as text, in locale when translated.
noticesNotice[] | nullTemporary notices; returned with core.
notices[].severitystringinfo, warning or critical. One of info, warning, critical. Default info.
notices[].title
REQUIRED
stringNotice heading, in locale when translated.
notices[].bodystring | nullNotice text, in locale when translated.
notices[].valid_tostring (date-time) | nullLast day the notice applies, at midnight with no time zone; null when open-ended.
reviewsobject | nullGuest reviews.
reviews.countintegerNumber of reviews we hold, which can differ from core.review_count. Default 0.
reviews.category_scoresmap of numberAverage score per category on a 0 to 10 scale, keyed by the category's display name, for example Cleanliness, Staff or Location.
reviews.itemsReviewItem[]Up to reviews_limit reviews, newest first.
reviews.items[].review_idstring | nullReview id.
reviews.items[].ratingnumber | nullThe guest's score on a 0 to 10 scale.
reviews.items[].titlestring | nullReview headline.
reviews.items[].positivestring | nullWhat the guest liked.
reviews.items[].negativestring | nullWhat the guest disliked.
reviews.items[].traveller_typestring | nullKind of trip as the source records it, for example Couple, Family, Solo traveller, Business or Group.
reviews.items[].datestring | nullDate we collected the review, YYYY-MM-DD; not the date of the stay.
guest_voiceobject | nullThemes from guest reviews.
guest_voice.praisedVoiceTheme[]Up to 6 themes with sentiment 0.5 or higher, most positive first.
guest_voice.praised[].type
REQUIRED
stringTheme, for example location, service, cleanliness, rooms, food, value, noise or views.
guest_voice.praised[].summarystring | nullWhat guests say about the theme, in one or two sentences.
guest_voice.praised[].sentimentnumber | nullHow positive guests are on the theme, from 0 (negative) to 1 (positive).
guest_voice.praised[].quotesstring[]Up to 3 verbatim quotes from this hotel's reviews.
guest_voice.watch_outsVoiceTheme[]Up to 4 themes with sentiment 0.4 or lower, most negative first.
guest_voice.watch_outs[].type
REQUIRED
stringTheme, for example location, service, cleanliness, rooms, food, value, noise or views.
guest_voice.watch_outs[].summarystring | nullWhat guests say about the theme, in one or two sentences.
guest_voice.watch_outs[].sentimentnumber | nullHow positive guests are on the theme, from 0 (negative) to 1 (positive).
guest_voice.watch_outs[].quotesstring[]Up to 3 verbatim quotes from this hotel's reviews.
ratesobject | nullStored prices for the stay.
rates.stayobject | nullThe stay the prices are for; null when no dates were given.
rates.stay.check_instring (date) | nullArrival date, YYYY-MM-DD.
rates.stay.check_outstring (date) | nullDeparture date, YYYY-MM-DD.
rates.stay.roomsOccupancy[]The party per room; a party read from the query is split evenly over its rooms and has no children.
rates.stay.rooms[].adults
REQUIRED
integerAdults in the room.
rates.stay.rooms[].children_agesinteger[]Age of each child in the room; empty for adults only.
rates.stay.originstring | nullquery when the stay was read from the query text, filters when it came from filters.stay; null on hotel detail. One of query, filters.
rates.channelsRateChannel[]Prices per channel, direct first; empty without dates, for parties with children, or when nothing was observed.
rates.channels[].channel
REQUIRED
stringdirect for the hotel's own booking engine, third_party for the Enterprise third-party channel. One of direct, third_party, expedia.
rates.channels[].enginestring | nullCode of the hotel's booking engine on the direct channel, for example SYNXIS; null on the third-party channel.
rates.channels[].status
REQUIRED
stringobserved when seen recently (within 4 hours for stays in the next 14 days, else 24 hours) or stale when older but within 7 days; channels with no prices are left out. One of observed, stale, unknown, unsupported.
rates.channels[].observed_atstring (date-time) | nullWhen the newest price on this channel was observed, RFC 3339 UTC.
rates.channels[].offersOffer[]Prices for each room and rate, cheapest first, sold-out last.
rates.refresh_jobobject | nullThe live rates job started by rates=refresh; its result arrives through GET /v2/jobs/{job_id} or the rates.completed webhook. Null otherwise.
rates.refresh_job.job_id
REQUIRED
stringJob id; poll it with GET /v2/jobs/{job_id}.
provenanceobject | nullWhere the content comes from.
provenance.profile_sourcestring | nullMain source of the published profile: site (the hotel's website) or booking (the third-party channel); null without a profile. One of site, booking.
provenance.profile_updated_atstring (date-time) | nullWhen the profile was last updated, RFC 3339 UTC.
provenance.images_releasestring | nullId of the gallery release the image order comes from; null when the images section was not requested or the hotel is not in a release.
warningsDetailWarning[]Things the call could not fully do.
warnings[].code
REQUIRED
stringrates_party_unsupported (stored rates cover adults-only parties), channel_unavailable (the third-party channel was left out), refresh_requires_dates or refresh_unavailable (no live job was started). One of rates_party_unsupported, channel_unavailable, refresh_requires_dates, refresh_unavailable.
warnings[].messagestring | nullExplanation in English.
warnings[].channelstring | nullThe channel left out, third_party; set only on channel_unavailable. Always third_party.

Errors

StatusCodeWhen
400bad_requestValidation errors carry details.fields[].
401unauthorizedNo key, or the key is unknown or revoked.
402insufficient_creditNot enough credit for the call. Checked before any work starts, so nothing was billed.
403forbiddenMissing scope, or details.reason tier_required: this needs the Build plan or higher.
404not_foundThe hotel, offer, booking, job or webhook doesn't exist for this key.
429rate_limitedOver the per-minute limit for the tier. Wait Retry-After seconds.
503unavailableWe're temporarily unable to serve the call. Retry with backoff.
Catalog

Amenity codes

GET/v2/amenities
Mode
Sync · 200
Price
Free
Auth
Any key

Synchronous and free; any valid key. The codes filters.amenities accepts; a kind: group code matches any of its members.

Example

Request
curl 'https://api.vistalink.com/v2/amenities' \
  -H "Authorization: Bearer $VISTALINK_API_KEY"
Response
200
{
  "amenities": [
    {
      "category": "pool",
      "code": "POOL",
      "kind": "group",
      "label": "Pool (any)",
      "members": [
        "POOL_OUTDOOR",
        "POOL_INDOOR",
        "POOL_HEATED",
        "POOL_ROOFTOP",
        "POOL_WITH_VIEW"
      ]
    },
    {
      "category": "pool",
      "code": "POOL_ROOFTOP",
      "kind": "amenity",
      "label": "Rooftop pool",
      "members": []
    }
  ],
  "request_id": "req_8f2c1a9b3d4e5f60"
}

Response 200

Successful Response

FieldTypeDescription
request_id
REQUIRED
stringOpaque id of this request, also sent as the X-Request-Id header; quote it when contacting support.
billingBilling | nullWhat a billed call cost. Present on every billed response; billing is null on free ones.
amenities
REQUIRED
Amenity[]Every amenity and group code.
amenities[].code
REQUIRED
stringUppercase code, for example POOL_ROOFTOP.
amenities[].label
REQUIRED
stringReadable name in English.
amenities[].category
REQUIRED
stringGroup the code belongs to, for example pool, wellness, parking or connectivity.
amenities[].kindstringamenity for a single amenity, group for a code that matches any of its members. One of amenity, group. Default amenity.
amenities[].membersstring[]For a group, the codes it matches; empty for a single amenity.

Errors

StatusCodeWhen
400bad_requestValidation errors carry details.fields[].
401unauthorizedNo key, or the key is unknown or revoked.
403forbiddenMissing scope.
429rate_limitedOver the per-minute limit for the tier. Wait Retry-After seconds.
503unavailableWe're temporarily unable to serve the call. Retry with backoff.
Catalog

Supported booking engines

GET/v2/engines
Mode
Sync · 200
Price
Free
Auth
Any key

Synchronous and free; any valid key. Booking engines we read prices from or book on, with their capabilities and hotel counts.

Example

Request
curl 'https://api.vistalink.com/v2/engines' \
  -H "Authorization: Bearer $VISTALINK_API_KEY"
Response
200
{
  "engines": [
    {
      "book": true,
      "engine": "SYNXIS",
      "hotel_count": 1830,
      "live_rates": true,
      "name": "SynXis",
      "success_class": "unmeasured"
    }
  ],
  "request_id": "req_8f2c1a9b3d4e5f60"
}

Response 200

Successful Response

FieldTypeDescription
request_id
REQUIRED
stringOpaque id of this request, also sent as the X-Request-Id header; quote it when contacting support.
billingBilling | nullWhat a billed call cost. Present on every billed response; billing is null on free ones.
engines
REQUIRED
Engine[]Supported engines, most hotels first.
engines[].engine
REQUIRED
stringEngine code, as Offer.engine and RateChannel.engine report it, for example SYNXIS.
engines[].name
REQUIRED
stringEngine name.
engines[].live_rates
REQUIRED
booleanTrue when direct prices can be read live for hotels on this engine.
engines[].book
REQUIRED
booleanTrue when bookings can be made on this engine.
engines[].success_classstringMeasured booking success rate band: high, medium, low, or unmeasured (the value for every engine today). One of high, medium, low, unmeasured. Default unmeasured.
engines[].hotel_countinteger | nullIndexed hotels on this engine; null when none or not counted.

Errors

StatusCodeWhen
400bad_requestValidation errors carry details.fields[].
401unauthorizedNo key, or the key is unknown or revoked.
403forbiddenMissing scope.
429rate_limitedOver the per-minute limit for the tier. Wait Retry-After seconds.
503unavailableWe're temporarily unable to serve the call. Retry with backoff.
Catalog

Health

GET/v2/health
Mode
Sync · 200
Price
Free
Auth
None

Public: no key, free, synchronous. 200 when every dependency is up, 503 with the same body when one is down.

Example

Request
curl 'https://api.vistalink.com/v2/health'
Response
200
{
  "checks": {
    "database": "ok"
  },
  "request_id": "req_8f2c1a9b3d4e5f60",
  "status": "ok",
  "version": "2.0"
}

Response 200

Successful Response

FieldTypeDescription
status
REQUIRED
stringok when every dependency is up (HTTP 200), degraded when one is down (HTTP 503). One of ok, degraded.
versionstringAPI version, 2.0. Default 2.0.
checksmap of stringState of each dependency checked, keyed by name (database): ok or unavailable.
request_idstring | nullId of this request.

Errors

StatusCodeWhen
503Degraded.
Catalog

This OpenAPI document

GET/v2/openapi.json
Mode
Sync · 200
Price
Free
Auth
None

Public: no key, free, synchronous. The OpenAPI 3.1 document for /v2, the same file the docs, SDKs and CLI are generated from.

Example

Request
curl 'https://api.vistalink.com/v2/openapi.json'
Response
200
{}

Response 200

OpenAPI 3.1 document

Hotels

Catalog helpers

GET /v2/amenities lists the codes filters.amenities takes. An entry with kind: group (POOL) stands for every code in its members (indoor, outdoor, rooftop…); kind: amenity is a single feature.

GET /v2/engines lists the booking engines we read, keyed by engine, with live_rates, book and hotel_count. success_class is high, medium or low once we've measured an engine, and unmeasured until then.

GET /v2/health needs no key. It answers {status, checks, request_id}: status is ok, or degraded with a 503 and the failing dependency named in checks.

Objects

Shared objects.

Objects that appear in more than one place. Billing is on the Billing page and ErrorBody under Errors & limits.

Offer

One bookable price for one room and rate, as observed on a channel.

FieldTypeDescription
offer_id
REQUIRED
stringOffer id, off_t plus 20 hex characters, specific to your account; book it with POST /v2/bookings within 30 minutes of receiving it.
channel
REQUIRED
stringWhere the price was observed: direct for the hotel's own booking engine, third_party for the Enterprise third-party channel. One of direct, third_party, expedia.
enginestring | nullCode of the hotel's booking engine for direct offers, for example SYNXIS; null on the third-party channel.
room_idstring (uuid) | nullCatalogue room this offer is for, matching rooms[].room_id on hotel detail; null when the source names the room only by its own code or name.
room_namestring | nullRoom name as the source shows it; null when the source does not name the room.
listing_keystring | nullThe source's own key for the room, such as engine_room:SUP or room_type:1234567; null when the source has none.
rate_namestring | nullRate plan name as the source shows it, or one built from the cancellation terms and board, such as Free cancellation, breakfast included.
total
REQUIRED
stringPrice for the whole stay and the whole party, a decimal string in currency; taxes the source quoted separately are added in when their amount is known. "0" when sold out.
currency
REQUIRED
stringISO 4217 currency the price was observed in; never converted.
nightly_avgstring | nulltotal divided by the number of nights, for the whole party rather than per room; null when sold out.
includes_taxesboolean | nullTrue when total includes taxes and fees, false when some are listed in excluded_charges, null when the source does not say.
excluded_chargesExcludedCharge[]Charges payable on top of total; usually empty.
excluded_charges[].kind
REQUIRED
stringWhat the charge is; taxes_and_fees (taxes and fees the source quoted separately) is the only kind produced today.
excluded_charges[].amount
REQUIRED
stringAmount of the charge, a decimal string in currency.
excluded_charges[].currency
REQUIRED
stringISO 4217 currency of amount.
excluded_charges[].perstring | nullWhat amount is charged per: stay (once for the whole stay, the only basis produced today), night, person or person_night. One of night, stay, person, person_night.
excluded_charges[].notestring | nullShort note on how the charge is applied, for example quoted on top of the total.
boardstring | nullMeals included: none, breakfast, half (breakfast and dinner), full (all meals) or all_inclusive; null when the source does not say. One of none, breakfast, half, full, all_inclusive.
free_cancellationboolean | nullTrue when the rate can be canceled free of charge, false when it is non-refundable, null when unknown.
cancellation_deadlinestring (date-time) | nullLast moment to cancel free of charge, RFC 3339; set only on offers read live, null otherwise.
paymentstring | nullWhen the guest pays: prepay at booking or pay_at_hotel at the property; null when the source does not say. One of prepay, pay_at_hotel.
occupancyobject | nullThe party this price is for, totalled across all rooms.
occupancy.adults
REQUIRED
integerAdults in the room.
occupancy.children_agesinteger[]Age of each child in the room; empty for adults only.
availabilitystringavailable when the room can be booked at this price, sold_out when the source showed it as unavailable, unknown when it did not say. One of available, sold_out, unknown. Default unknown.
observed_atstring (date-time) | nullWhen this price was observed, RFC 3339 UTC.
urlstring (uri) | nullPage on the source where this price was observed; null when the source has no linkable page.