Hotels
Search stays for a place and a date range and get back normalized properties, cheapest TOTAL first — the whole-booking price AND the per-room per-night…
Search stays for a place and a date range and get back normalized properties, cheapest TOTAL first — the whole-booking price AND the per-room per-night rate, which of the many agencies reselling that hotel is quoting it and how many others also did, star class, guest score with its review count, property type, neighbourhood and distance from where you asked. Runs a real browser, so budget ~15s a search.
Also known as: hotel, stays, stay, accommodation, lodging, where to stay
Call it
bowmark.hotels.search(query: HotelQuery, limit?: number, options?: CallOptions): Promise<HotelSearchResult>Functions
| Function | What it does |
|---|---|
search | Searches stays for a place and a date range — { location: "SFO", checkIn: "2026-08-29", checkOut: "2026-09-01" } — and returns up to limit normalized properties (default and max 50),… |
Types
type HotelQuery = {
location: string // IATA airport code ("SFO") — the measured form — or a city
checkIn: string // ISO date "2026-08-29"
checkOut: string // strictly after checkIn
adults?: number // total guests; the sites' own default is 2
rooms?: number // defaults to 1; two rooms for four guests prices differently
}
// One normalized property. Nulls mean "this site does not report it for this
// property", never "we failed to read it" — so a filter can tell an unrated new
// hotel from a badly-rated one.
type Hotel = {
source: string // which site this came from
id: string // that site's id for the property
name: string // the property as that site lists it
price: number | null // the WHOLE booking (all nights, ALL rooms) — sorted on
nightPrice: number | null // per night PER ROOM, same seller as price
currency: string
seller: string // who sells that cheapest rate ("Priceline")
sellerCount: number | null // how many sellers quoted it — 1 means no competition
stars: number | null // property class 1-5, not a review score
score: number | null // guest score out of 10
reviewCount: number | null // how many reviews stand behind that score
propertyType: string // "Hotel", "Motel", "Apartment"
neighborhood: string | null // "Union Square Area"; null for most airport hotels
city: string | null
distance: string | null // "11.7 mi", as the site renders it
distanceFrom: string | null // what that distance is measured FROM
url: string // deep link to the property
}
type HotelSearchResult = {
hotels: Hotel[] // cheapest TOTAL first; unpriced properties last
warnings: string[] // always present; empty when nothing was dropped. A site
// named here returned NOTHING, which is a different fact
// from a destination that is genuinely sold out
}
type CallOptions = {
timeoutMs?: number // per-provider budget in ms, default 30000, clamped to 1000-55000.
// A provider slower than this is DROPPED from the results and
// NAMED in warnings — never silently absent
}Examples
// Where to stay for a conference: cheapest well-reviewed hotels near the
// airport, with the seller for each so the agent knows who it would book with.
const { hotels, warnings } = await bowmark.hotels.search({
location: "SFO", checkIn: "2026-08-29", checkOut: "2026-09-01", adults: 2,
}, 25);
for (const w of warnings) log(w);
// A score needs reviews behind it — 9.4 from 3 reviews is not a recommendation.
const trusted = hotels.filter(h => (h.score ?? 0) >= 8 && (h.reviewCount ?? 0) >= 500);
log(`${trusted.length} of ${hotels.length} properties are well-reviewed`);
return trusted.slice(0, 5).map(h => ({
name: h.name, stars: h.stars, score: h.score, reviews: h.reviewCount,
total: h.currency + h.price, perNight: h.nightPrice,
soldBy: h.seller, alsoQuotedBy: (h.sellerCount ?? 1) - 1,
where: h.neighborhood ?? h.city, distance: h.distance, url: h.url,
}));// A whole trip in one script: fly there, sleep there, drive while you are
// there — three capabilities, one comparison, same dates.
const [air, bed, wheels] = await Promise.all([
bowmark.flights.search({ from: "JFK", to: "SFO", depart: "2026-08-29", return: "2026-09-01" }),
bowmark.hotels.search({ location: "SFO", checkIn: "2026-08-29", checkOut: "2026-09-01" }, 10),
bowmark.cars.search({ pickup: "SFO", pickupDate: "2026-08-29", dropoffDate: "2026-09-01" }, 5),
]);
// Every capability carries its own `warnings`. Log all three: a trip priced
// while a site was down is not a cheaper trip, it is an incomplete one.
const warnings = [...air.warnings, ...bed.warnings, ...wheels.warnings];
for (const w of warnings) log(w);
const cheapestBed = bed.hotels[0] ?? null;
const cheapestCar = wheels.cars[0] ?? null;
// Every price here is a TOTAL for the whole trip, on all three capabilities.
log(`bed ${cheapestBed?.price ?? "?"} + car ${cheapestCar?.price ?? "?"}`);
return {
complete: warnings.length === 0, missed: warnings,
flight: air.flights[0] ?? null,
hotel: cheapestBed && { name: cheapestBed.name, total: cheapestBed.price, seller: cheapestBed.seller, url: cheapestBed.url },
car: cheapestCar && { car: cheapestCar.carName, total: cheapestCar.price, url: cheapestCar.url },
};Providers behind it
| Provider | |
|---|---|
kayak | Kayak |