# Booking.com Reference

> **Proxy required.** Without a residential proxy, Booking.com strips search URL parameters
> and won't return availability or prices. Always open with `PROXY_URL=$(tc proxy)`.

---

## URL Patterns

### Hotel detail page

```
https://www.booking.com/hotel/{country_code}/{hotel-slug}.en-gb.html
```

**Note:** URL query params for dates/guests (checkin, checkout, group_adults) are always stripped by Booking.com's bot-detection — do NOT rely on them. Navigate to the hotel page and use the **availability section date picker** to set dates (see Workflow 4).

**Country codes:** `es` Spain · `gb` UK · `fr` France · `it` Italy · `gr` Greece · `pt` Portugal · `th` Thailand · `us` USA

**Hotel slug:** the hotel name in kebab-case (e.g. `sol-barbados`, `hotel-florida-magalluf`, `innside-by-melia-wave-calvia`). If unknown, use the city page to find it from the listing URL.

### City landing page

Use this to browse hotels for a destination (shows "from" per-night prices):

```
https://www.booking.com/city/{country_code}/{city-slug}.en-gb.html
```

Known examples:
- Magaluf, Majorca: `https://www.booking.com/city/es/magalluf.en-gb.html` (35+ hotels listed)

### Search results page

```
https://www.booking.com/searchresults.en-gb.html?dest_id={id}&dest_type=city
```

**Warning:** Booking.com strips all params except `dest_id` and `dest_type` regardless of proxy, cookies, or session. Do not attempt to pass dates or guest counts in the URL. Use the city page instead.

Known `dest_id` values:
- Magaluf, Majorca: `-402490`

---

## Reusable Helpers

Add these to your shell session before running workflows:

```bash
# Advance the calendar N months forward without snapshot round-trips between clicks
advance_calendar_months() {
  local n=$1
  for i in $(seq 1 $n); do
    NM=$(agent-browser snapshot -i 2>/dev/null | grep "Next month" | grep -o "e[0-9]\+" | head -1)
    agent-browser click @$NM
    sleep 0.7
  done
}

# Usage: advance 3 months forward (March → June/July view)
advance_calendar_months 3
```

---

## Workflow 1 — Hotel by Name (Direct)

Use when you know the hotel name and just want to get to the hotel page.

```bash
PROXY_URL=$(tc proxy)
agent-browser close
agent-browser --proxy "$PROXY_URL" open "https://www.booking.com/hotel/es/sol-barbados.en-gb.html"
sleep 4

# Accept cookies if banner appears
COOKIE_REF=$(agent-browser snapshot -i | grep "Accept" | grep -o "e[0-9]\+" | head -1)
[ -n "$COOKIE_REF" ] && agent-browser click @$COOKIE_REF && sleep 1

# Dismiss sign-in modal if it appears
agent-browser press "Escape"
sleep 1

agent-browser screenshot /workspace/group/hotel.png
```

Then use Workflow 4 to get exact nightly prices.

---

## Workflow 2 — Browse Hotels by City

Use when you don't have a specific hotel in mind. Shows "from" per-night prices for all hotels.

```bash
PROXY_URL=$(tc proxy)
agent-browser close
agent-browser --proxy "$PROXY_URL" open "https://www.booking.com/city/es/magalluf.en-gb.html"
sleep 4

# Accept cookies
COOKIE_REF=$(agent-browser snapshot -i | grep "Accept" | grep -o "e[0-9]\+" | head -1)
[ -n "$COOKIE_REF" ] && agent-browser click @$COOKIE_REF && sleep 1

# Dismiss any modal
agent-browser press "Escape"
sleep 1

# Scroll to load hotel listings
agent-browser scroll down 500
sleep 1

# Extract hotel page URLs directly from the listing (most reliable way to get slugs)
# ⚠️ Do NOT click hotel card refs — clicks are intercepted inconsistently by bot-detection
# Instead, extract the href and navigate directly:
agent-browser snapshot | grep -oE 'https://www\.booking\.com/hotel/[a-z]{2}/[^"]+\.en-gb\.html' | head -20

# If snapshot doesn't show full URLs, get them via evaluate:
agent-browser evaluate "JSON.stringify([...document.querySelectorAll('a[data-testid="property-card-desktop-single-image"], a[data-testid="title-link"]')].map(a => a.href).filter(Boolean).slice(0, 20))"

agent-browser screenshot /workspace/group/city-hotels.png
```

This gives you hotel page URLs. Navigate to each one directly with `agent-browser navigate <url>` then use Workflow 4 for exact prices.

> ⚠️ **Do NOT click hotel card link refs** — bot-detection intercepts these clicks inconsistently and the page stays on the listing. Always extract the href and use `agent-browser navigate` instead.

---

## Workflow 3 — Search via Homepage

> ⚠️ **Unreliable — avoid if possible.** The top search bar calendar is intercepted by bot-detection: clicking any date cell causes the calendar to close immediately without selecting a date. Use **Workflow 2** (city page) + **Workflow 4** (hotel page availability calendar) instead — those are the only reliable paths.

If you still need the homepage search (e.g. to get a search results URL for a destination you don't have a city page for):

```bash
PROXY_URL=$(tc proxy)
agent-browser close
agent-browser --proxy "$PROXY_URL" open "https://www.booking.com"
sleep 3

# 1. Dismiss cookie banner
COOKIE_REF=$(agent-browser snapshot -i | grep "Accept" | grep -o "e[0-9]\+" | head -1)
agent-browser click @$COOKIE_REF && sleep 1

# 2. Dismiss sign-in modal
agent-browser press "Escape" && sleep 1

# 3. Fill destination
agent-browser fill "[name='ss']" "Magaluf"
sleep 2

# 4. Select first autocomplete result
DEST_REF=$(agent-browser snapshot -i | grep "Magaluf" | grep "option\|suggestion" | grep -o "e[0-9]\+" | head -1)
agent-browser click @$DEST_REF && sleep 1

# 5. Skip date selection entirely — bot-detection closes the calendar on every click
#    Instead go straight to Search, then handle dates on individual hotel pages (Workflow 4)
SEARCH_REF=$(agent-browser snapshot -i | grep -i "Search" | grep "button" | grep -o "e[0-9]\+" | head -1)
agent-browser click @$SEARCH_REF
sleep 5

# 6. Extract hotel URLs from results — do NOT click card refs (also intercepted)
agent-browser evaluate "JSON.stringify([...document.querySelectorAll('a[data-testid="title-link"]')].map(a => a.href).slice(0, 20))"

agent-browser screenshot /workspace/group/results.png
```

---

## Workflow 4 — Get Exact Nightly Prices for a Hotel ✅ (Recommended)

This is the **only reliable way** to get per-night prices. Prices appear inline in the availability section calendar — no need to click "Show prices", "See availability", or "Apply changes".

```bash
# 1. Navigate to hotel page
PROXY_URL=$(tc proxy)
agent-browser close
agent-browser --proxy "$PROXY_URL" open "https://www.booking.com/hotel/es/sol-barbados.en-gb.html"
sleep 5

# 2. Accept cookies, dismiss modal
COOKIE_REF=$(agent-browser snapshot -i | grep "Accept" | grep -o "e[0-9]\+" | head -1)
[ -n "$COOKIE_REF" ] && agent-browser click @$COOKIE_REF && sleep 1
agent-browser press "Escape" && sleep 1

# 3. Scroll to the Availability section (separate from the top search bar)
agent-browser scroll down 1600
sleep 1

# 4. Click the AVAILABILITY SECTION's Check-in date button
#    Top bar label contains "—"; availability section label does not — filter on that
AVAIL_CHECKIN=$(agent-browser snapshot -i | grep "Check-in date" | grep -v "—" | grep -o "e[0-9]\+" | head -1)
if [ -z "$AVAIL_CHECKIN" ]; then
  # Fallback: CSS selector (scroll to availability section first)
  agent-browser click 'css=[data-testid="availability-dates-check-in"], [data-testid="hp_availability_check-in-date"]'
else
  agent-browser click @$AVAIL_CHECKIN
fi
sleep 2

# 5. Navigate calendar to target month
advance_calendar_months 3   # from March 2026 → June/July view
# Verify we're at the right month:
agent-browser snapshot | grep "July 2026"

# 6. Click the check-in date — this reveals per-night prices in calendar cells
CHECKIN_REF=$(agent-browser snapshot -i | grep "We 1 July 2026" | grep -o "e[0-9]\+" | head -1)
agent-browser click @$CHECKIN_REF
sleep 2

# 7. Extract per-night prices from calendar snapshot
# Pattern: StaticText with day number followed by StaticText with price
agent-browser snapshot | grep -A80 '"July 2026"' \
  | grep -E 'StaticText "[0-9]+"$' \
  | awk '{gsub(/[^0-9]/, "", $NF); print $NF}'
# Output: alternating day / price pairs, e.g.:
# 1
# 421     <- Jul 1 price (€/night)
# 2
# 433     <- Jul 2 price
# 3
#         <- blank = unavailable

agent-browser screenshot /workspace/group/prices.png
```

> ⚠️ **Never click "Show prices", "Apply changes", or "See availability"**
> - "Show prices" — triggers a redirect that hangs the browser for 30–60s, requiring a restart
> - "Apply changes" — navigates away from the hotel page to the city landing page
> - "See availability" — scrolls to the room table but still shows hanging "Show prices" buttons
>
> Always extract prices directly from the calendar snapshot after clicking the check-in date.

---

## Multi-Hotel Session Pattern

When checking prices across multiple hotels, keep the same browser session to preserve cookies:

```bash
# First hotel — open with proxy (starts browser session + accepts cookies once)
PROXY_URL=$(tc proxy)
agent-browser close
agent-browser --proxy "$PROXY_URL" open "https://www.booking.com/hotel/es/sol-barbados.en-gb.html"
sleep 4
COOKIE_REF=$(agent-browser snapshot -i | grep "Accept" | grep -o "e[0-9]\+" | head -1)
[ -n "$COOKIE_REF" ] && agent-browser click @$COOKIE_REF && sleep 1

# --- run Workflow 4 price check here ---

# Subsequent hotels — navigate within same session (cookies already accepted)
agent-browser navigate "https://www.booking.com/hotel/es/hotel-florida-magalluf.en-gb.html"
sleep 4
# Cookie banner should NOT reappear — if it does, dismiss it:
COOKIE_REF=$(agent-browser snapshot -i | grep "Accept" | grep -o "e[0-9]\+" | head -1)
[ -n "$COOKIE_REF" ] && agent-browser click @$COOKIE_REF && sleep 1

# --- run Workflow 4 price check here ---
```

> ⚠️ **Use `agent-browser navigate` (not `agent-browser open`) for subsequent hotels** — `open` starts a new browser context and loses cookies, causing the consent modal to reappear on every hotel.

---

## Two Date Pickers on Hotel Pages

Hotel pages have **two separate date pickers** — do not confuse them:

| Picker | Snapshot label | Purpose |
|---|---|---|
| Top search bar | `"Check-in date — Check-out date"` | Filters search results; does NOT update the room price table |
| Availability section | `"Check-in date"` (no suffix) | Shows per-night prices in calendar cells — **use this one** |

```bash
# Top search bar date button (affects search, NOT room prices):
agent-browser snapshot -i | grep "Check-in date — Check-out date"

# Availability section date button (use for per-night prices):
agent-browser snapshot -i | grep "Check-in date" | grep -v "—"
```

---

## Page Interaction Patterns

### Cookie banner (appears on every fresh session)

```bash
# By ref (most reliable):
COOKIE_REF=$(agent-browser snapshot -i | grep "Accept" | grep -o "e[0-9]\+" | head -1)
agent-browser click @$COOKIE_REF

# By CSS (fallback):
agent-browser click "#onetrust-accept-btn-handler"
```

Snapshot label: `button "Accept" [ref=eN]`

### Sign-in modal

```bash
agent-browser press "Escape"
# OR by ref:
agent-browser snapshot -i | grep "Dismiss" | grep -o "e[0-9]\+" | head -1 | xargs -I{} agent-browser click @{}
```

Snapshot label: `button "Dismiss sign-in info." [ref=eN]`

### Calendar navigation

- **From March 2026 baseline:** need 3 "Next month" clicks to reach June/July view
- Gridcell labels: `"We 1 July 2026"` (day-abbrev + day + month + year)
- Use `advance_calendar_months N` helper to batch clicks without intermediate snapshots

---

## Key Selectors

```bash
# Hotel name
agent-browser get text "h2[data-testid='title']"

# Review score
agent-browser get text "[data-testid='review-score-right-component']"

# Location
agent-browser get text "[data-testid='PropertyHeaderAddressDesktop']"

# Amenities/highlights
agent-browser get text "[data-testid='property-most-popular-facilities-wrapper']"

# Search result cards
agent-browser snapshot -s "[data-testid='property-card']"
agent-browser get text "[data-testid='price-and-discounted-price']"
agent-browser get text "[data-testid='title']"
```

---

## Tips

- **Proxy is mandatory** — without it, Booking.com blocks all requests
- **URL date params never work** — always stripped by bot-detection; use the city page to browse hotels, then Workflow 4 for exact prices
- **Two date pickers on hotel pages** — top bar label contains `—`, availability section label does not; use `grep "Check-in date" | grep -v "—"` to reliably find the right one
- **Never click "Show prices", "Apply changes", or "See availability"** — these navigate away or hang the browser; extract prices directly from the calendar snapshot after clicking check-in date
- **Calendar prices appear after clicking check-in date** — alternating day/price `StaticText` nodes; blank after a day number = unavailable
- **Unavailable nights = can't book a continuous stay** — if Jul 2–4 have no price, the hotel can't be booked Jul 1–7; move to next hotel
- **Cookie banner on every fresh session** — always dismiss before interacting
- **Hotel slugs** — kebab-case hotel name (e.g. `hotel-florida-magalluf`, `sol-barbados`); if 404, use city page to find the correct slug from the listing URL
- **City page** — lists 35+ hotels with "from" prices; use for shortlisting before checking exact prices hotel by hotel
- **Never click hotel card link refs** — bot-detection intercepts these inconsistently; always extract hrefs via `evaluate` and use `agent-browser navigate <url>` to reach hotel pages
- **Top search bar calendar is broken** — clicking any date cell closes the calendar immediately (bot-detection); skip date entry in the top bar entirely and use Workflow 4's availability section calendar instead
