---
title: "Search"
method: POST
path: "/v1/search"
tags: ["Hotels"]
---

# Search

`POST /v1/search`

## Request

- Base URL: `https://api.vistalink.com`
- URL: `https://api.vistalink.com/v1/search`
- Auth: one of:
  - none
  - HTTP bearer

Structured hotel search over the catalogue. No LLM is involved.

Works without an API key (per-IP rate limit; `include_rates` is ignored).
Key-less results carry a price only when both `check_in` and `check_out`
are given: the cached all-inclusive total for exactly that stay, when one
is available (no live lookup). With a Build (or higher) key, `include_rates=true` plus
`check_in`/`check_out` also fetches live rates for those dates.

## Request body

- SearchRequest: POST /v1/search — structured hotel search (no LLM). Needs a location: ``city``, ``latitude`` + ``longitude``, or ``hotel_name`` (a city named in ``vibe`` is used when none is given). Unknown fields are a 422 naming them; a few common aliases (``location``, ``query``, ``checkIn``, ``adults``, ...) map onto the documented names (``app/mcp_api/agent_inputs.py``).
  - `city` string, nullable: City to search in, e.g. 'Lisbon'. A search needs city, latitude+longitude, or hotel_name.
  - `country` string, nullable: ISO 3166-1 alpha-2 country code of the city, e.g. 'PT'. Use it for city names that exist in several countries (Paris FR vs Paris US).
  - `area` string, nullable: Neighbourhood or landmark within city to search around, e.g. 'Brera' or 'Colosseum' (with city='Milan' / 'Rome'). Results are limited to hotels near it.
  - `latitude` number, nullable: Search centre latitude in decimal degrees, used with longitude (0 = not set).
  - `longitude` number, nullable: Search centre longitude in decimal degrees, used with latitude (0 = not set).
  - `radius_meters` integer, nullable: Radius around latitude/longitude in metres (0 = default).
  - `check_in` string, date, nullable: Check-in date, YYYY-MM-DD (today or later). Prices need check_in and check_out.
  - `check_out` string, date, nullable: Check-out date, YYYY-MM-DD, after check_in.
  - `guests` integer, nullable: Number of adult guests (0 = default of 2).
  - `rooms` integer, nullable: Number of rooms (0 = 1). Prices are only quoted for one room.
  - `budget_min` number, nullable: Minimum price per night (request currency)
  - `budget_max` number, nullable: Maximum price per night (request currency)
  - `daily_budget_min` number, nullable: Synonym of budget_min (per-night floor); wins if both sent
  - `daily_budget_max` number, nullable: Synonym of budget_max (per-night cap); wins if both sent
  - `currency` string, nullable: ISO 4217 currency code for prices, e.g. 'EUR', 'USD', 'GBP'.
  - `amenities` string[], nullable: Amenities the hotel must have, e.g. ['wifi', 'pool'].
  - `vibe` string[], nullable: What the traveller is looking for, as free-text phrases or tags, e.g. ['boutique design hotel'] or ['romantic', 'quiet'].
  - `hotel_name` string, nullable: Hotel name or part of it, e.g. 'Four Seasons'.
  - `limit` integer: Maximum number of results, 1-20 without an API key (1-50 with one). Larger values are reduced to the maximum.
  - `include_rates` boolean: Fetch live rates from booking scraper (adds ~30-45s). Requires an API key; ignored key-less

## Response `200`

Successful Response

- SearchResponse: POST /v1/search response body.
  - `hotels` HotelCard[]
    - `hotel_id` string, required
    - `name` string, required
    - `city` string, nullable
    - `country` string, nullable
    - `address` string, nullable
    - `star_rating` number, nullable
    - `review_score` number, nullable
    - `review_count` integer, nullable
    - `latitude` number, nullable
    - `longitude` number, nullable
    - `website_url` string, nullable
    - `parley_url` string, nullable: Public hotel page on Parley (meetparley.ai) with the stay's dates; send users here to view or book the hotel.
    - `amenities` string[]
    - `price` union
      - StayPrice: The price of one specific stay. Only present when the request carried both ``check_in`` and ``check_out``. Always the all-inclusive total (taxes and fees included) for the whole stay, in ``currency``, for one room and ``adults`` guests, from a scrape no older than 72 hours.
        - `total` number, required: All-inclusive total for the whole stay, in `currency`.
        - `currency` string, required: ISO 4217 currency of `total` (the requested currency).
        - `includes_taxes_and_fees` boolean: Always true: totals with an unknown tax basis are never returned.
        - `check_in` string, date, required
        - `check_out` string, date, required
        - `nights` integer, required
        - `adults` integer
        - `rooms` integer
        - `source_total` number, nullable: The same total in the currency it was observed in.
        - `source_currency` string, nullable
        - `observed_at` string, date-time, nullable: When this price was observed.
      - object
    - `images` HotelImageCard[]: Ordered gallery of hotel images (up to 5 on /v1/chat and /v1/search; up to 50 on /v1/hotels/{id}). Use for lightbox/carousel — not as the card thumbnail. Empty list when the hotel has no images.
      - `url` string, required: Fully-qualified CDN URL of the image.
      - `label` string, nullable: Image category label (e.g. 'swimming pool indoor', 'building', 'room'). Null when unclassified.
    - `review_highlight` string, nullable
    - `guest_insights` GuestInsights: Aggregated guest insights for a hotel.
      - `highlights` string[]
      - `aspects` GuestInsightAspect[]
        - `type` string, required
        - `summary` string, required
        - `sentiment` number, nullable
        - `quote` string, nullable
    - `description` string, nullable
    - `hotel_type` string, nullable
    - `rates` RoomRate[], nullable
      - `room_name` string
      - `amenities` string[]
      - `room_size_sqm` number, nullable
      - `room_description` string, nullable
      - `offers` RoomOffer[]
        - `price` number, nullable
        - `currency` string
        - `includes_taxes_and_fees` boolean, nullable
        - `has_breakfast` boolean
        - `has_free_cancellation` boolean
    - `hero_image_url` string, nullable: Intent-aware thumbnail URL. Pre-selected to match the query context (e.g. a pool search returns a pool photo). Always use this field as the card thumbnail — do not substitute images[0]. Null only when the hotel has no images.
    - `hero_image_label` string, nullable: Category label of the hero image (e.g. 'swimming pool indoor', 'building'). Null when unclassified.
    - `explanation` string, nullable
    - `explanation_factors` ExplanationFactor[]
      - `factor_type` string, required
      - `human_readable` string, required
      - `contribution` number, required
    - `poi_distances` object
    - `routes_to_pois` object
  - `total` integer
  - `fallback_used` boolean
  - `fallback_message` string, nullable
  - `pricing_note` string, nullable: Why results carry no (or only some) prices: prices need both check_in and check_out, and are only the all-inclusive total for that stay. Key-less: the cached total for those dates; with a key, include_rates=true also fetches live rates.
  - `notes` string[], nullable: How the request was interpreted, when it was not taken literally: an alias read as its documented name, a city taken from vibe, a country chosen for a city name found in several countries, a reduced limit.
  - `usage` UsageInfo: Per-request usage info attached to responses. Surfaces cost and latency — the metrics API clients need for budgeting and performance monitoring. Token-level detail is recorded internally but not exposed here (billing is per-call, not per-token).
    - `latency_ms` integer, nullable
    - `cost_usd` number, nullable

## Other responses

- `401`: Unauthorized
- `422`: Validation Error
- `429`: Too Many Requests

---

[API](https://skmtc.dev/vistalink/apis/vistalink-hotels-api.md) · [All operations](https://skmtc.dev/vistalink/apis/vistalink-hotels-api/llms.txt) · [OpenAPI document](https://skmtc.dev/vistalink/apis/vistalink-hotels-api/revisions/a5bb847f45c2?raw)
