Search hotels and stays with total price, rating and cancellation terms.
Searches Booking.com like its search page, for a destination, dates and guests: name, total price for the stay and the price before discounts with currency, review score, number of reviews and rating word, star rating, address and coordinates, distance to the centre, free cancellation (and until when), sold-out flag and link. Filters: budget, minimum review score, stars, free cancellation; sort by popularity, price, rating or distance. Sponsored results are dropped by default.
- Site
- Booking.com, Netherlands
- Version
- v1
- Runs, last 7 days
- No reports yet
- sha256
- fae43b40ccb1…
Run it with your agent
after installing WieldAsk in plain words. Your agent finds this recipe and calls it:
“Search hotels and stays with total price, rating and cancellation terms.”
Read-only: it changes nothing on the site. No login needed.
{ "tool": "run", "arguments": { "recipe_id": "booking.com/search-stays", "params": { "adults": 2, "checkin": "2026-12-11", "checkout": "2026-12-13", "destination": "Lisbon" } } }
Requests
sent from your browser, in order- 1GET/searchresults.{{params.language}}.html?age&checkin&checkout&group_adults&group_children&nflt&no_rooms&offset&order&selected_currency&ssThe search results page. Results come from the page state (script[data-capla-store-data], an Apollo cache): ROOT_QUERY.searchQueries holds one entry whose results are the property cards. The one-item foreach builds Booking's filter string (nflt), sort code and offset.
Parameters
from your agentReturns
to your agentWhere it can send
- www.booking.com
Anything else is blocked by the extension.
Reads from your session
Nothing. Your browser attaches the site's cookies by itself.
Used only in requests to the addresses on the left. Never sent to wield.run or your agent.
Checks
a run passes when- length(output.stays) > `5`
- output.stays[0].price_total > `0`
- length(output.stays[?currency == 'EUR']) == length(output.stays)
- length(output.stays[?sponsored]) == `0`
- output.nights == `2`
- starts_with(output.stays[0].url, 'https://www.booking.com/hotel/')
Raw recipe
Served as canonical JSON from /api/v1/recipes/booking.com/search-stays, so its sha256 matches the ETag and the index.
curl -s https://wield.run/api/v1/recipes/booking.com/search-stays | shasum -a 256 # fae43b40ccb1133aba0110f06ca55831a82a4221e1951c6b54ecb65538149801
{ "description": "Searches Booking.com like its search page, for a destination, dates and guests: name, total price for the stay and the price before discounts with currency, review score, number of reviews and rating word, star rating, address and coordinates, distance to the centre, free cancellation (and until when), sold-out flag and link. Filters: budget, minimum review score, stars, free cancellation; sort by popularity, price, rating or distance. Sponsored results are dropped by default.", "effect": "read", "id": "booking.com/search-stays", "keywords": [ "booking", "hotel", "unterkunft", "übernachtung", "ferienwohnung", "reise", "hotel buchen", "hotels", "stay", "accommodation", "travel", "where to stay", "price per night", "free cancellation" ], "output": { "schema": { "properties": { "checkin": { "type": "string" }, "checkout": { "type": "string" }, "destination": { "type": "string" }, "nights": { "type": [ "integer", "null" ] }, "page": { "type": "integer" }, "stays": { "items": { "properties": { "address": { "type": [ "string", "null" ] }, "center_distance": { "type": [ "string", "null" ] }, "charges_excluded": { "description": "Taxes/fees payable on top, when Booking lists them", "type": [ "number", "null" ] }, "city": { "type": [ "string", "null" ] }, "country": { "type": [ "string", "null" ] }, "currency": { "type": [ "string", "null" ] }, "deals": { "items": { "type": "string" }, "type": "array" }, "district": { "type": [ "string", "null" ] }, "free_cancellation": { "type": "boolean" }, "free_cancellation_until": { "type": [ "string", "null" ] }, "id": { "type": "string" }, "latitude": { "type": [ "number", "null" ] }, "longitude": { "type": [ "number", "null" ] }, "name": { "type": "string" }, "no_prepayment": { "type": "boolean" }, "price_before_discount": { "type": [ "number", "null" ] }, "price_total": { "description": "Total for the stay incl. taxes Booking includes", "type": [ "number", "null" ] }, "rating_word": { "type": [ "string", "null" ] }, "review_score": { "description": "0–10", "type": [ "number", "null" ] }, "reviews": { "type": "integer" }, "sold_out": { "type": "boolean" }, "sponsored": { "type": "boolean" }, "stars": { "type": [ "integer", "null" ] }, "url": { "type": "string" } }, "type": "object" }, "type": "array" }, "total": { "description": "Matching stays (Booking's count)", "type": [ "integer", "null" ] } }, "type": "object" }, "value": "let $s = steps.search[0].s, $inc = params.include_sponsored == `true`, $lang = params.language || 'en-gb', $ci = params.checkin, $co = params.checkout, $ad = to_string(params.adults || `2`), $ro = to_string(params.rooms || `1`) in {destination: params.destination, checkin: params.checkin, checkout: params.checkout, nights: $s.searchMeta.dates.lengthOfStayInDays || `null`, total: $s.pagination.nbResultsTotal || `null`, page: params.page || `1`, stays: map(&(let $r = @, $b = @.basicPropertyData, $p = @.priceDisplayInfoIrene in {id: to_string($b.id), name: $r.displayName.text, price_total: ($p.displayPrice.amountPerStay.amountUnformatted && floor(($p.displayPrice.amountPerStay.amountUnformatted) * `100` + `0.5`) / `100`) || `null`, price_before_discount: ($p.priceBeforeDiscount.amountPerStay.amountUnformatted > `0` && floor(($p.priceBeforeDiscount.amountPerStay.amountUnformatted) * `100` + `0.5`) / `100`) || `null`, currency: $p.displayPrice.amountPerStay.currency || `null`, charges_excluded: ($p.excludedCharges.excludeChargesAggregated.amountPerStay.amountUnformatted > `0` && floor(($p.excludedCharges.excludeChargesAggregated.amountPerStay.amountUnformatted) * `100` + `0.5`) / `100`) || `null`, review_score: ($b.reviews.totalScore > `0` && $b.reviews.totalScore) || `null`, reviews: $b.reviews.reviewsCount || `0`, rating_word: $b.reviews.totalScoreTextTag.translation || `null`, stars: ($b.starRating.value > `0` && $b.starRating.value) || `null`, address: $b.location.address || `null`, city: $b.location.city || `null`, country: $b.location.countryCode || `null`, latitude: $b.location.latitude || `null`, longitude: $b.location.longitude || `null`, district: $r.location.displayLocation || `null`, center_distance: $r.location.mainDistance || `null`, free_cancellation: $r.policies.showFreeCancellation == `true`, free_cancellation_until: ($r.blocks || `[]`)[0].freeCancellationUntil || `null`, no_prepayment: $r.policies.showNoPrepayment == `true`, sold_out: $r.soldOutInfo.isSoldOut == `true`, deals: ($p.badges || `[]`)[].name.translation, sponsored: $r.sponsoredListingData != `null` || $r.persuasion.showNativeAdLabel == `true` || $r.showAdLabel == `true`, url: join('', ['https://www.booking.com/hotel/', $b.location.countryCode || 'xx', '/', $b.pageName || '', '.', $lang, '.html?checkin=', $ci, '&checkout=', $co, '&group_adults=', $ad, '&no_rooms=', $ro])}), $s.results || `[]`)[?$inc || !sponsored]}" }, "params": { "additionalProperties": false, "properties": { "adults": { "default": 2, "maximum": 30, "minimum": 1, "type": "integer" }, "checkin": { "description": "YYYY-MM-DD", "format": "date", "type": "string" }, "checkout": { "description": "YYYY-MM-DD, after checkin", "format": "date", "type": "string" }, "children_ages": { "default": [], "items": { "maximum": 17, "minimum": 0, "type": "integer" }, "maxItems": 10, "type": "array" }, "currency": { "default": "EUR", "pattern": "^[A-Z]{3}$", "type": "string" }, "destination": { "description": "City, region, landmark or hotel name, e.g. 'Lisbon', 'Porto Ribeira', 'Berlin Mitte'", "maxLength": 100, "minLength": 2, "type": "string" }, "free_cancellation": { "default": false, "type": "boolean" }, "include_sponsored": { "default": false, "type": "boolean" }, "language": { "default": "en-gb", "enum": [ "en-gb", "en-us", "de", "fr", "es", "it", "nl", "pt-pt" ], "type": "string" }, "max_price": { "description": "Budget per night in the chosen currency", "minimum": 1, "type": "integer" }, "min_review_score": { "description": "6 = pleasant, 7 = good, 8 = very good, 9 = wonderful", "enum": [ 6, 7, 8, 9 ], "type": "integer" }, "page": { "default": 1, "maximum": 20, "minimum": 1, "type": "integer" }, "rooms": { "default": 1, "maximum": 30, "minimum": 1, "type": "integer" }, "sort": { "default": "popularity", "enum": [ "popularity", "price", "rating", "distance" ], "type": "string" }, "stars": { "description": "Exact star rating", "maximum": 5, "minimum": 1, "type": "integer" } }, "required": [ "destination", "checkin", "checkout" ], "type": "object" }, "provenance": { "method": "browser-inspect", "notes": "Servers get a JavaScript challenge (HTTP 202), so this runs only in the user's browser; there a same-origin fetch of /searchresults.<lang>.html returns the full page (200, ~1.7 MB, 20 cards) with the Apollo cache in script[data-capla-store-data] (its namespace attribute carries a build hash, so the recipe selects by the data attribute). Results: ROOT_QUERY.searchQueries[<search(...) key>].results (SearchResultProperty): basicPropertyData (id, location, reviews, starRating, pageName), priceDisplayInfoIrene (displayPrice.amountPerStay, priceBeforeDiscount, excludedCharges, badges), policies, blocks[].freeCancellationUntil, soldOutInfo, sponsoredListingData / persuasion.showNativeAdLabel / showAdLabel (sponsored). URL filters: nflt (price=EUR-min-<max>-1;review_score=80;class=4;fc=2), order (price, bayesian_review_score, distance_from_search), offset, selected_currency.", "recorded_at": "2026-11-07T18:46:00+01:00", "recorded_by": "C4" }, "requires_login": false, "schema": "https://wield.run/schema/recipe/v1.json", "site": { "country": "NL", "id": "booking.com", "language": "en", "name": "Booking.com", "origin": "https://www.booking.com", "origins": [ "https://www.booking.com" ] }, "steps": [ { "description": "The search results page. Results come from the page state (script[data-capla-store-data], an Apollo cache): ROOT_QUERY.searchQueries holds one entry whose results are the property cards. The one-item foreach builds Booking's filter string (nflt), sort code and offset.", "expect": { "assert": "contains(body, 'data-capla-store-data')", "content_type": "html", "status": [ 200 ] }, "extract": { "embedded_json": { "selector": "script[type=\"application/json\"][data-capla-store-data]", "vars": { "s": "values(ROOT_QUERY.searchQueries)[?results] | [0]" } } }, "foreach": { "as": "f", "in": "[{offset: (params.page - `1`) * `25`, children: length(params.children_ages || `[]`), order: (params.sort == 'price' && 'price') || (params.sort == 'rating' && 'bayesian_review_score') || (params.sort == 'distance' && 'distance_from_search') || `null`, nflt: join(';', [params.max_price && join('', ['price=', params.currency || 'EUR', '-min-', to_string(params.max_price), '-1']) || '', params.min_review_score && join('', ['review_score=', to_string(params.min_review_score * `10`)]) || '', params.stars && join('', ['class=', to_string(params.stars)]) || '', params.free_cancellation == `true` && 'fc=2' || ''][?@ != '']) || `null`}]", "max": 1 }, "id": "search", "request": { "headers": { "Accept": "text/html" }, "method": "GET", "query": { "age": "{{params.children_ages}}", "checkin": "{{params.checkin}}", "checkout": "{{params.checkout}}", "group_adults": "{{params.adults}}", "group_children": "{{f.children}}", "nflt": "{{f.nflt}}", "no_rooms": "{{params.rooms}}", "offset": "{{f.offset}}", "order": "{{f.order}}", "selected_currency": "{{params.currency}}", "ss": "{{params.destination}}" }, "url": "/searchresults.{{params.language}}.html" } } ], "task": "Search hotels and stays with total price, rating and cancellation terms", "tos": { "notes": "Booking.com's terms prohibit scraping and automated bulk access. The recipe loads one search page per call in the user's own browser session, as the user would, and never books.", "reviewed": false }, "verify": { "checks": [ "length(output.stays) > `5`", "output.stays[0].price_total > `0`", "length(output.stays[?currency == 'EUR']) == length(output.stays)", "length(output.stays[?sponsored]) == `0`", "output.nights == `2`", "starts_with(output.stays[0].url, 'https://www.booking.com/hotel/')" ], "example_params": { "adults": 2, "checkin": "2026-12-11", "checkout": "2026-12-13", "destination": "Lisbon" }, "max_ms": 6000 }, "version": 1 }