Booking.com

search-stays

booking.com
InstallationInstalls just this skill for your agent
Side effectread
Authnone
Needsnavigate · evaluate
Summary

Search Booking.com hotels by place and dates, returning name, price and rating.

Params
{"location":"place name, e.g. 'Barcelona' or 'Barcelona, Spain'","checkin":"YYYY-MM-DD","checkout":"YYYY-MM-DD","adults":"number of adults (default 2)"}
Returns
{"count":"int","results":"array of { name, url, price, rating, ratingWord, reviews, location }"}
SKILL.md41 lines

Intent

Search Booking.com hotels by place name and dates. Navigates the search results page and reads the rendered property cards in the browser (Booking.com resolves the place from the query itself). Params: location (e.g. 'Barcelona, Spain'), checkin and checkout (YYYY-MM-DD), adults (default 2). Returns {count, results:[{name, url, price, rating, ratingWord, reviews, location}]}. Price is whatever currency Booking shows for the session. Read-only, it never books.

Execute

  1. navigate → https://www.booking.com/searchresults.html?ss={{location|encode}}&checkin={{checkin}}&checkout={{checkout}}&group_adults={{adults}}
  2. evaluate the Extractor below with params. It does the in-page work and returns the JSON in returns.

Extractor

( async (root, params) => { const doc = root && root.querySelectorAll ? root : document; const txt = (el) => el ? (el.innerText || el.textContent || '').trim() : null; const cards = Array.from(doc.querySelectorAll('[data-testid="property-card"]')); const seen = new Set(); const results = cards.map((c) => { const name = txt(c.querySelector('[data-testid="title"]')); if (!name) return null; const linkEl = c.querySelector('a[data-testid="title-link"], a[href*="/hotel/"]'); let url = linkEl ? linkEl.getAttribute('href') : null; if (url) url = url.split('?')[0]; if (url && url[0] === '/') url = 'https://www.booking.com' + url; const price = txt(c.querySelector('[data-testid="price-and-discounted-price"]')); const scoreRaw = txt(c.querySelector('[data-testid="review-score"] div')) || ''; const sm = scoreRaw.match(/([\d.,]+)/); const rating = sm ? parseFloat(sm[1].replace(',', '.')) : null; const reviewsRaw = txt(c.querySelector('[data-testid="review-score"] div:last-child')) || ''; const rm = reviewsRaw.replace(/[^\d]/g, ''); const ratingWordEl = c.querySelector('[data-testid="review-score"] div:nth-child(2)'); const location = txt(c.querySelector('[data-testid="address"]')); if (url) { if (seen.has(url)) return null; seen.add(url); } return { name, url, price: price || null, rating, ratingWord: txt(ratingWordEl) || null, reviews: rm ? parseInt(rm, 10) : null, location: location || null }; }).filter(Boolean); return { count: results.length, results }; } )

Success assertion

{
  "expr": "typeof result === 'object' && Array.isArray(result.results)",
  "type": "value"
}