---
title: "Search real, bookable hotel rates — look-to-book priced"
method: POST
path: "/hotels/search"
tags: ["hotels"]
---

# Search real, bookable hotel rates — look-to-book priced

`POST /hotels/search`

## Request

- Base URL: `https://letsfg.co/developers/api/v1`
- URL: `https://letsfg.co/developers/api/v1/hotels/search`
- Auth: API key in header `X-API-Key`

Every rate type is returned, refundable and non-refundable; each offer says which (`refundable`, `free_cancellation_until`, `cancellation_policy`). `price` is what the guest is charged, in the requested `currency` (USD by default). Nothing is added at booking.

Look-to-book billing: free up to 1,000 searches since your last successful hotel booking. Past that allowance, searches are sold in blocks of 1,000 for $5.00, debited from your prepaid developer balance the moment you cross the current allowance. Booking a hotel resets the free hotel allowance back to 1,000 (the flight allowance is separate); a search counts only when it returns bookable rates. A connected payment method is required for every search regardless of allowance.

## Request body

- HotelSearchRequest
  - `city_id` integer, required: TBO city id, from POST /hotels/destinations.
  - `city_name` string, required
  - `check_in` string, date, required
  - `check_out` string, date, required
  - `adults` integer
  - `children` integer
  - `child_ages` integer[], nullable
  - `nationality` string: Guest nationality, used for rate eligibility.
  - `currency` string: ISO code every `price` is quoted in. USD unless asked; PLN is the supplier currency and needs no conversion.
  - `limit` integer
  - `with_images` boolean
  - `amenities` string[], nullable: Canonical amenity ids, e.g. ["spa", "parking"]. A hotel must have all of them.
  - `max_total_price` number, nullable: Drop offers whose `price` is above this.
  - `max_distance_km` number, nullable: Keep hotels within this radius of `near` (the city centre when `near` is omitted).
  - `near` string, nullable: Landmark or place name to measure max_distance_km from.
  - `dietary_hint` string, nullable: Free text such as "vegan". A best-effort text match against hotel descriptions, not verified data; see `caveats`.
  - `room_flexible` boolean: Also try splitting the party across several rooms.

## Response `200`

Bookable hotel offers.

- HotelSearchResponse
  - `session_id` string: The search session. Each offer carries its own `session_id`; send the one on the chosen offer to POST /hotels/book.
  - `currency` string: The currency every `price` is in.
  - `supplier_currency` string
  - `fx_rate` number, nullable
  - `fx_as_of` string, nullable: When the exchange rate was published.
  - `count` integer
  - `hotels` object[]
    - `hotel_code` integer
    - `name` string
    - `stars` integer, nullable
    - `address` string
    - `city` string
    - `country` string
    - `lat` number, nullable
    - `lon` number, nullable
    - `guest_rating` number, nullable: The hotel's Google rating, 1-5 (one decimal). Null when Google has no rating for this hotel yet; a search a few seconds later usually has it.
    - `review_count` integer, nullable: How many Google reviews guest_rating is based on, when Google says.
    - `rating_source` 'google': Whose rating guest_rating is. Present only when there is one.
    - `chain` string, nullable
    - `amenities` string[]
    - `images` string[]
    - `offers` HotelOffer[]
      - `combination_id` integer, nullable
      - `combination_id_v2` string: Stable content hash for this exact rate. Send back verbatim to POST /hotels/book.
      - `session_id` string: The supplier session this offer came from. Send this one to POST /hotels/book.
      - `room` string
      - `board` string: Meal plan, e.g. "Room Only", "Breakfast Included".
      - `price` number: What the guest is charged, in `currency`. Nothing is added at booking. Send back as `expected_price`.
      - `currency` string: The currency of `price`, as requested (USD by default). Send back as `currency`.
      - `fx_rate` number, nullable: The rate `price` was converted at (guest currency per 1 unit of supplier_currency); null when no conversion was needed. Send back as `fx_rate`.
      - `supplier_price` number: The same price in supplier_currency.
      - `supplier_currency` string
      - `expected_cost` number: Always 0 - LetsFG keeps the rate on its side. Send it back as returned (optional).
      - `refundable` boolean
      - `free_cancellation_until` string, nullable: The supplier free-cancellation deadline, on refundable rates only (null otherwise). Cancel before it and the whole price comes back.
      - `cancellation_policy` string, nullable: The supplier cancellation terms, as text.
      - `room_size_m2` number: Present when known.
      - `max_occupancy` integer: Present when known.
      - `bed` string: Present when known.
      - `room_view` string: Present when known.
      - `room_images` string[]: Up to 3, present when known.
    - `booking_url` string, uri: This hotel's letsfg.co page for these dates and this party, safe to show a guest. A stay booked there counts as your hotel booking and resets your hotel search allowance; leave its `dref` parameter as it is. Developer API keys only.
  - `terms` object: Cancellation ladder and booking terms shown to the guest.
  - `caveats` string[]: Anything a filter could not honour, in plain words.

## Other responses

- `402`: No payment method connected, or the free search allowance is used up and the prepaid balance cannot cover the $5.00 block of the next 1,000 searches.

---

[API](https://skmtc.dev/letsfg/apis/letsfg-public-developer-api.md) · [All operations](https://skmtc.dev/letsfg/apis/letsfg-public-developer-api/llms.txt) · [OpenAPI document](https://skmtc.dev/letsfg/apis/letsfg-public-developer-api/revisions/516ab6ba1d2e?raw)
