---
title: "Read hotel search results"
method: GET
path: "/v1/hotels/searches/{searchId}/results"
tags: ["Hotels"]
---

# Read hotel search results

`GET /v1/hotels/searches/{searchId}/results`

Lean list cards; default 10, max 50 per page. searchComplete:true terminal, false advisory; poll snapshotCandidateCount to a steady non-zero. totalCandidates===0 = filters only if totalBeforeFilters>0, else none bookable once complete. ?refundable=true = witnessed.

## Path parameters

- `searchId` string, required

## Query parameters

- `page` integer
- `pageSize` integer
- `sort` 'relevance' | 'price_asc' | 'price_desc' | 'star_desc' | 'review_score_desc' | 'distance_asc'
- `currency` string
- `locale` string
- `min-star` integer
- `max-star` integer
- `min-review-score` number
- `min-price` number
- `max-price` number
- `refundable` '0' | '1' | 'true' | 'false'
- `deals-only` '0' | '1' | 'true' | 'false'
- `amenities` string[]
- `property-types` string[]
- `brands` string[]
- `chains` string[]
- `districts` string[]
- `view` 'card'

## Response `200`

Ranked hotels, as lean list cards. Amenities, the full image list, the address and brand/chain are not on a card – read the hotel for the one row you picked.

- object
  - `searchId` string, required — The id of the search this snapshot belongs to.
  - `currencyCode` string, required — Currency the prices in this snapshot are quoted in.
  - `searchComplete` boolean, required — Upstream aggregation flag. true is authoritative/terminal; conclude NO BOOK-ON-WEGO BOOKABLE INVENTORY only when true AND metadata.totalBeforeFilters === 0 – never that no hotel exists, since only Book-on-Wego inventory was requested. A zero totalCandidates on its own means only that this read's filters matched nothing, and an empty page with totalCandidates > 0 is pagination. false is inconclusive, so watch metadata.snapshotCandidateCount convergence to stop sooner.
  - `stay` object — What upstream priced: the dates and occupancy every price on this read covers. Present when upstream states them.
    - `checkIn` string, required — Check-in date priced upstream, YYYY-MM-DD.
    - `checkOut` string, required — Check-out date priced upstream, YYYY-MM-DD.
    - `nights` integer, required — Nights between checkIn and checkOut. price.total covers this many nights of price.amountPerNight.
    - `occupancy` object, required — The occupancy priced upstream for this search (ages resolved, incl. fallback).
      - `adults` integer, required — Adults priced upstream for this search.
      - `childrenAges` integer[], required — Resolved per-child ages actually sent upstream (age-8 fallback when omitted).
      - `rooms` integer, required — Rooms priced upstream for this search.
  - `metadata` object, required — Pagination, the snapshot's filter vocabulary, the settle counters for this read, and what it resolved currency and locale to.
    - `page` integer, required — 1-based page number of this snapshot.
    - `pageSize` integer, required — Hotels requested per page.
    - `resultCount` integer, required — Hotels on this page. The page only – judge a filter on totalCandidates.
    - `totalCandidates` integer, required — Hotels matching this read's filters across the snapshot – the count that judges a filter, not the page. A filter that matched nothing is totalCandidates 0 with totalBeforeFilters above 0.
    - `totalBeforeFilters` integer, required — Hotels that survived the Book-on-Wego join, before this read's filters ran. A filter that matched nothing is totalCandidates === 0 with totalBeforeFilters > 0. Zero means no Book-on-Wego-bookable inventory surfaced for these dates – it is NOT proof that no hotel exists, since the join runs over a sampled rate list. Equal to totalCandidates on an unfiltered read.
    - `filterOptions` object, required — The filterable vocabulary of the hotels in this snapshot, ordered by count, over the same population as totalBeforeFilters. The amenities / property-types / brands / chains / districts query params take an entry's name field VERBATIM (matched case-insensitively as a substring), so pick from here rather than guessing a synonym. A term listed here matches AT LEAST its count on an unfiltered read; the count is a lower bound, since one term can span several values (Pool also matches Indoor Pool). Still growing while searchComplete is false. priceRange does the same job for the numeric bounds: it states the span min-price / max-price are measured on, so read it before choosing either.
      - `amenities` object[], required — Amenity terms present in this snapshot, by count.
        - `name` string, required — The term the matching filter query param accepts, verbatim.
        - `count` integer, required — Hotels carrying this value on an unfiltered read; a lower bound, since one term can span several values.
      - `propertyTypes` object[], required — Property-type terms present in this snapshot, by count.
        - `name` string, required — The term the matching filter query param accepts, verbatim.
        - `count` integer, required — Hotels carrying this value on an unfiltered read; a lower bound, since one term can span several values.
      - `brands` object[], required — Brand terms present in this snapshot, by count.
        - `name` string, required — The term the matching filter query param accepts, verbatim.
        - `count` integer, required — Hotels carrying this value on an unfiltered read; a lower bound, since one term can span several values.
      - `chains` object[], required — Chain terms present in this snapshot, by count.
        - `name` string, required — The term the matching filter query param accepts, verbatim.
        - `count` integer, required — Hotels carrying this value on an unfiltered read; a lower bound, since one term can span several values.
      - `districts` object[], required — District terms present in this snapshot, by count.
        - `name` string, required — The term the matching filter query param accepts, verbatim.
        - `count` integer, required — Hotels carrying this value on an unfiltered read; a lower bound, since one term can span several values.
      - `priceRange` object — The span the min-price / max-price bounds compare against, on their own basis: amountPerNight plus every per-night charge the card publishes beside it, localTaxPerNight and taxAmountPerNight where present, covering every room in the search. Both ends are attainable, since the bounds are inclusive, so min-price at min and max-price at max each keep the whole snapshot, and a bound outside the span returns nothing. Read over each hotel's headline price, so with ?refundable=true the bounds move to lowestRefundablePrice and can reach past max. Folded over the same population as totalBeforeFilters, so it does not narrow as other filters bite, and it still moves while searchComplete is false. Absent when the snapshot holds no hotel.
        - `min` number, required — The cheapest hotel's all-in nightly figure, in the response currency.
        - `max` number, required — The dearest hotel's all-in nightly figure, in the response currency.
    - `hasMore` boolean, required — Another page of hotels follows.
    - `snapshotCandidateCount` integer, required — Upstream aggregation counter – the practical early convergence signal. Two spaced (not back-to-back), equal, non-zero reads ≈ settled enough to render; it stabilizes well before searchComplete flips, so use it to stop polling sooner. A heuristic, not proof of completion (searchComplete:true is that). Not the same as totalCandidates (the post-Book-on-Wego-join hotel count).
    - `createdAt` string — When this search was created (ISO 8601) – the freshness anchor for these prices. A search older than about 10 minutes may answer 404 as expired. Absent when the search service omits it.
    - `currencyCode` string, required — The currency this read ASKED upstream for, and the one every price on it is meant to be in. Read it beside currencyCodeSource before you show a number: a price computed in the wrong currency renders as a perfectly normal price, with no error and no odd shape to notice, so the response states which one rather than leaving it to be inferred. Where the operation also publishes a top-level currencyCode, that field reports the currency the prices actually came back in; the two agree unless upstream declined to reprice.
    - `currencyCodeSource` 'explicit' | 'default', required — How the API resolved currencyCode: explicit (the caller sent currency – including a value equal to the default) or default (USD, no currency sent). A default here is the one signal that the request never carried the currency you meant.
    - `locale` string, required — The language tag this read asked upstream for – what any localized text on it was resolved in (room and board names, airline and airport names, review prose).
    - `localeSource` 'explicit' | 'default', required — How the API resolved locale: explicit (the caller sent locale – including a value equal to the default) or default (en, no locale sent). A default here explains text that came back in a language the caller did not ask for.
  - `results` object[], required — The requested page of ranked hotels, as list cards.
    - `hotelId` number, required — The hotel's numeric id; read its detail with GET /v1/hotels/{hotelId}.
    - `name` string, required — Hotel display name.
    - `star` number — Star rating (1-5), when classified.
    - `review` object — Aggregate guest review score and count.
      - `score` number, required — Aggregate guest review score (0-10).
      - `count` number, required — Number of guest reviews behind the score.
    - `price` object, required — A card's per-night and stay price. Every amount covers the whole booking, all rooms in the search, so quote these figures as they stand. Upstream rounds per booking, so total is the exact stay figure and amountPerNight is one night of it. Any per-night figure published beside amountPerNight is charged ON TOP of it: add localTaxPerNight, and taxAmountPerNight when it appears, to reach what a guest pays and what the price sorts and bounds rank on.
      - `scope` 'booking', required — What every amount here covers: the whole booking, all rooms in the search.
      - `amountPerNight` number, required — One night of the whole booking, in the response currency, covering every room. Excludes localTaxPerNight, and taxAmountPerNight when that field appears. Rounded to a whole currency unit upstream; total is the exact stay figure.
      - `taxAmountPerNight` number — Per-night tax charged on top of amountPerNight, in the response currency. Present only when the amount excludes it; absent means amountPerNight already covers any such tax, which is the usual case. Add it the same way as localTaxPerNight.
      - `localTaxPerNight` number — Per-night local tax (city/tourism/municipality), charged on top of amountPerNight. wego.com adds it to the price it displays. Present when upstream reports the tax, where 0 means none charged. Absent when upstream reports it as unknown.
      - `totalLocalTax` number — Stay total of local tax as reported upstream, which rounds the nightly figure. Present when upstream reports the tax. Absent when upstream reports it as unknown.
      - `total` number — Stay total in the response currency. Excludes totalLocalTax, mirroring amountPerNight.
      - `deal` object — The discount advertised on this rate, absent when the rate carries none. Describes THIS price object, so with ?refundable=true it describes the refundable rate the card switched to.
        - `label` string — The offer's own tag, e.g. 'Best Deal'. Its presence also says where wasPerNight came from: see that field.
        - `percentOff` integer, required — The offer's discount as a whole percent. A tagged offer's percent wins over a usual-price one, matching the site's precedence, and the two often differ. wego.com renders a percentage only for an UNTAGGED offer; a tagged one it shows as its label plus a crossed-out price, so do not attribute this figure to what the page displays. Always describes the same offer as wasPerNight, so the pair never disagree.
        - `wasPerNight` number — Pre-discount per-night price, on amountPerNight's basis so the two subtract cleanly. When label is present this is COMPUTED back from the offer's unrounded discount, since a tagged offer carries no pre-discount price of its own and wego.com renders the same computed figure; quote it as approximate, and expect it to differ slightly from a figure you derive using the rounded percentOff. When label is absent it is the quoted pre-discount price itself.
        - `wasTotal` number — Pre-discount stay total, on total's basis. Computed or quoted on the same rule as wasPerNight.
        - `promoCode` string — Promo code the traveller enters at checkout, when the offer carries one. Most live offers are provider discounts with no code.
      - `totalUsd` number, required — Stay total in USD, the cross-currency ranking key.
      - `currency` string, required — ISO 4217 currency of these amounts.
    - `refundable` 'available' | 'unknown', required — Refundability WITNESS, not a boolean. 'available' = a free-cancellation Book-on-Wego rate was seen in this snapshot. 'unknown' = none was seen, which is NOT evidence that none exists – this envelope carries only a sample of each hotel's rates, so a negative is not computable here. To answer 'does this hotel have a refundable room', read the hotel's rooms/rates.
    - `lowestRefundablePrice` object — Lowest refundable Book-on-Wego rate. Present exactly when refundable is 'available'.
      - `scope` 'booking', required — What every amount here covers: the whole booking, all rooms in the search.
      - `amountPerNight` number, required — One night of the whole booking, in the response currency, covering every room. Excludes localTaxPerNight, and taxAmountPerNight when that field appears. Rounded to a whole currency unit upstream; total is the exact stay figure.
      - `taxAmountPerNight` number — Per-night tax charged on top of amountPerNight, in the response currency. Present only when the amount excludes it; absent means amountPerNight already covers any such tax, which is the usual case. Add it the same way as localTaxPerNight.
      - `localTaxPerNight` number — Per-night local tax (city/tourism/municipality), charged on top of amountPerNight. wego.com adds it to the price it displays. Present when upstream reports the tax, where 0 means none charged. Absent when upstream reports it as unknown.
      - `totalLocalTax` number — Stay total of local tax as reported upstream, which rounds the nightly figure. Present when upstream reports the tax. Absent when upstream reports it as unknown.
      - `total` number — Stay total in the response currency. Excludes totalLocalTax, mirroring amountPerNight.
      - `deal` object — The discount advertised on this rate, absent when the rate carries none. Describes THIS price object, so with ?refundable=true it describes the refundable rate the card switched to.
        - `label` string — The offer's own tag, e.g. 'Best Deal'. Its presence also says where wasPerNight came from: see that field.
        - `percentOff` integer, required — The offer's discount as a whole percent. A tagged offer's percent wins over a usual-price one, matching the site's precedence, and the two often differ. wego.com renders a percentage only for an UNTAGGED offer; a tagged one it shows as its label plus a crossed-out price, so do not attribute this figure to what the page displays. Always describes the same offer as wasPerNight, so the pair never disagree.
        - `wasPerNight` number — Pre-discount per-night price, on amountPerNight's basis so the two subtract cleanly. When label is present this is COMPUTED back from the offer's unrounded discount, since a tagged offer carries no pre-discount price of its own and wego.com renders the same computed figure; quote it as approximate, and expect it to differ slightly from a figure you derive using the rounded percentOff. When label is absent it is the quoted pre-discount price itself.
        - `wasTotal` number — Pre-discount stay total, on total's basis. Computed or quoted on the same rule as wasPerNight.
        - `promoCode` string — Promo code the traveller enters at checkout, when the offer carries one. Most live offers are provider discounts with no code.
      - `totalUsd` number, required — Stay total in USD, the cross-currency ranking key.
      - `currency` string, required — ISO 4217 currency of these amounts.
    - `cityName` string — City the hotel is in.
    - `districtName` string — District or neighbourhood the hotel is in.
    - `lat` number — Hotel latitude in decimal degrees. Use with lng to compute distance to any landmark you choose.
    - `lng` number — Hotel longitude in decimal degrees. Use with lat to compute distance to any landmark you choose.
    - `distanceToCityCentre` number — Kilometres from the city's place-record coordinate (the point GET /v1/places reports for the city), and the ?sort=distance_asc key. Where a city record covers an island, a city-state or a whole administrative area, that point can sit far from the commercial centre. For locality prefer the districts filter, or compute distance from lat/lng to a landmark you choose. Present when upstream reports it.
    - `image` string — Primary image URL, if any.
    - `badges` string[], required — Full-result-set badges this hotel wins (e.g. cheapest).

## Other responses

- `400` — Invalid request parameters.
- `401` — Missing or invalid bearer token.
- `404` — Unknown or expired search.
- `429` — Rate limit exceeded; retry after the `Retry-After` seconds.
- `502` — The upstream hotels service returned an invalid response.
- `503` — The hotels service is unavailable (`upstream_unavailable`) or rate-limited upstream (`upstream_rate_limited`); retry after the `Retry-After` seconds.

---

[API](https://skmtc.net/wego/apis/wego-api.md) · [All operations](https://skmtc.net/wego/apis/wego-api/llms.txt) · [OpenAPI document](https://skmtc-service-staging.skmtc.workers.dev/v1/apis/wego/wego-api/revisions/e5d7af502419/schema)
