Booking.com Hotel Scraper

Scrape Booking.com hotel prices, availability, ratings, rooms, policies, facilities, photos, and review summaries. Export CSV, JSON, Excel, or use the API.

Data fields

FieldTypeDescription
searchstringValue exported as search.
destinationstringValue exported as destination.
checkInstring | nullValue exported as checkIn.
checkOutstring | nullValue exported as checkOut.
adultsintegerValue exported as adults.
childrenintegerValue exported as children.
childrenAgesarrayValue exported as childrenAges.
roomsRequestedintegerValue exported as roomsRequested.

Input preview

destinationDestination
destinationsDestinations
hotelUrlsDirect Booking.com hotel URLs
checkInCheck-in date
checkOutCheck-out date
checkInOffsetDaysCheck-in offset days

API and agents

This actor can be run through Apify API, datasets, webhooks, schedules, and the official Apify MCP server.

How this actor works

See example inputs, outputs, API usage, and practical limits before running this actor on Apify.

Open Apify page

Scrape Booking.com hotel listings for travel research, price monitoring, and structured hotel datasets.

What does this Actor do?

Booking.com Hotel Scraper returns structured hotel listings by destination or direct hotel URL. The default output is deliberately lean: identity, canonical URL, property type, location, rating, availability, and price context. Rooms, policies, facility highlights, photos, and review details are optional per-hotel enrichments.

Use it for hotel supply research, price snapshots, destination comparisons, lead lists, and travel-data pipelines. It does not sign in, reserve rooms, solve challenges, or collect private traveler data.

Who is it for?

  • Travel and hospitality analysts comparing public hotel supply and prices.
  • Revenue and market-research teams building repeatable destination snapshots.
  • Data engineers feeding hotel identity, availability, and price context into internal pipelines.
  • AI-agent builders who need schema-described Booking.com listing data through API or MCP.

Input example

{
  "destination": "Lisbon",
  "checkIn": "2026-08-15",
  "checkOut": "2026-08-17",
  "adults": 2,
  "children": 1,
  "childrenAges": [5],
  "rooms": 1,
  "currency": "USD",
  "language": "en-us",
  "maxHotelsPerSearch": 10,
  "maxCandidatesPerSearch": 50,
  "maxHotelsPerRun": 10
}

This saves at most 10 lean hotel rows. No per-hotel enrichment is requested.

Choose the product surface

Lean listing/search data is the default. Enable only the extra surfaces your workflow needs:

{
  "hotelUrls": [
    "https://www.booking.com/hotel/pt/chiado-arty-flats.html"
  ],
  "checkIn": "2026-08-15",
  "checkOut": "2026-08-17",
  "adults": 2,
  "children": 0,
  "childrenAges": [],
  "rooms": 1,
  "enrichRooms": true,
  "enrichPolicies": true,
  "enrichFacilities": true,
  "enrichPhotos": true,
  "enrichReviews": true,
  "maxHotelsPerRun": 1
}

Each selected enrichment runs independently. If rooms fail but photos succeed, the actor keeps the base hotel row, reports the rooms warning on that row, and keeps the successful photos. There is no enrichment-specific charge event, so a failed enrichment is never charged separately.

enrichReviews returns the review score/count, labels, secondary score, and available summary metadata. It does not return individual guest review text.

Dates for scheduled runs

Use fixed checkIn and checkOut dates for one-off snapshots. For schedules, use a relative pair that resolves once when each run starts:

{
  "destination": "Paris",
  "checkInOffsetDays": 14,
  "stayNights": 3,
  "currency": "EUR",
  "sortBy": "PRICE_ASC",
  "maxHotelsPerSearch": 5,
  "maxCandidatesPerSearch": 50,
  "maxHotelsPerRun": 5
}

Do not combine the two date modes. Every row records the mode, relative values, resolved dates, and resolution time in provenance.

Search targets

Provide at least one of:

  • destination: one city, region, landmark, or hotel phrase.
  • destinations: multiple independent destination searches.
  • hotelUrls: direct Booking.com /hotel/{country}/{slug}.html URLs.

Direct hotel URLs can be more deterministic than broad destination searches because the actor resolves and matches the stable hotel ID before accepting a row. Neither direct URLs nor destination searches should be treated as immune to Booking.com blocking, challenges, redirects, inventory changes, or schema changes.

Input settings

Input Type Meaning
destination string One destination search phrase.
destinations string[] Multiple destination searches.
hotelUrls string[] Direct Booking.com hotel URLs; each can return at most one hotel.
checkIn, checkOut string Optional YYYY-MM-DD range. Supply both or neither.
checkInOffsetDays, stayNights integer Relative date pair for schedules: offset 0–365 and stay 1–30 nights.
adults integer Adult guests; default 2.
children integer Child guests; default 0.
childrenAges integer[] Exactly one age, 0–17, for every child.
rooms integer Rooms requested; default 1.
currency string Requested three-letter currency. Returned currency is recorded separately.
language string Requested Booking.com locale, such as en-us or de-de.
countryCode string Optional country for the existing residential proxy route. It is not a blocking guarantee.
propertyTypes string[] Optional categories such as HOTEL, APARTMENT, or HOSTEL.
minReviewScore integer Optional minimum of 6, 7, 8, or 9.
starRatings integer[] Optional exact star ratings from 1 to 5.
freeCancellationOnly boolean Keep listings exposing free cancellation in the requested stay context.
minPricePerNight, maxPricePerNight number Optional bounds calculated from displayed stay total ÷ nights; dates are required.
sortBy string RECOMMENDED, PRICE_ASC, TOP_REVIEWED, STARS_DESC, or STARS_ASC.
maxCandidatesPerSearch integer Unpaid candidate rows evaluated per destination; 1–500 and at least the saved per-search cap.
maxHotelsPerSearch integer Maximum rows from each destination search; 1–100.
maxHotelsPerRun integer Hard saved-and-charged row cap across the whole run; 1–100.
enrichRooms boolean Room configurations and matched offer data.
enrichPolicies boolean Cancellation, prepayment, pet, and meal-plan signals.
enrichFacilities boolean Facility and property-USP highlights.
enrichPhotos boolean Available hotel photos.
enrichReviews boolean Review labels, secondary score, and summary metadata.

Backward compatibility: maxItems sets both limits and includeDetails enables all enrichments. Do not combine maxItems with either explicit limit. New integrations should use the explicit fields.

Limits and charging

The three limits have different scopes:

  • maxCandidatesPerSearch caps unpaid candidate rows evaluated for each destination while applying filters.
  • maxHotelsPerSearch stops each destination search independently.
  • maxHotelsPerRun is the total number of dataset rows that can be saved and charged across all destinations and direct hotel URLs.

Duplicate stable hotel IDs are saved once per run. Direct hotel URL targets return at most one row each. RUN_SUMMARY records all three limits plus candidatesEvaluated, filteredOutRows, saved, and chargedHotels. If filters consume the candidate allowance before any row can be saved, the run reports INPUT_CONFIGURATION_LIMIT_REACHED instead of a misleading empty success.

Output example

Example dataset item excerpt:

{
  "hotelName": "Chiado Arty Flats",
  "hotelId": 1814397,
  "canonicalUrl": "https://www.booking.com/hotel/pt/chiado-arty-flats.html",
  "childrenAges": [5],
  "price": "$400.03",
  "currency": "USD",
  "priceContext": {
    "status": "AVAILABLE",
    "basis": "TOTAL_STAY",
    "checkIn": "2026-08-15",
    "checkOut": "2026-08-17",
    "nights": 2,
    "requestedCurrency": "USD",
    "returnedCurrency": "USD",
    "total": { "amount": 400.03, "formatted": "$400.03", "currency": "USD" },
    "pricePerNight": { "amount": 200.02, "formatted": null, "currency": "USD" },
    "pricePerNightBasis": "DISPLAYED_TOTAL_DIVIDED_BY_NIGHTS",
    "excludedTaxesAndFees": { "amount": 42.31, "formatted": "$42.31", "currency": "USD" },
    "totalIncludesTaxesAndFees": false
  },
  "availabilityContext": {
    "status": "AVAILABLE",
    "adults": 2,
    "children": 1,
    "childrenAges": [5],
    "rooms": 1
  },
  "enrichment": {
    "rooms": { "requested": false, "status": "NOT_REQUESTED", "warning": null },
    "policies": { "requested": false, "status": "NOT_REQUESTED", "warning": null },
    "facilities": { "requested": false, "status": "NOT_REQUESTED", "warning": null },
    "photos": { "requested": false, "status": "NOT_REQUESTED", "warning": null },
    "reviews": { "requested": false, "status": "NOT_REQUESTED", "warning": null }
  },
  "warnings": [],
  "scrapedAt": "2026-07-20T12:00:00.000Z"
}

priceContext.total is the total Booking.com displayed for the requested stay. pricePerNight is that displayed total divided by the number of nights and rounded to two decimals; it is not a separately quoted rate. excludedTaxesAndFees remains separate and is never silently added.

Run outcomes

Read RUN_SUMMARY from the default key-value store. The status is explicit:

Status Meaning
SUCCEEDED Requested base rows were saved without warnings.
NO_MATCHES The target resolved but returned no matching hotels, or no destination/hotel could be resolved. A zero-row run fails instead of reporting success.
SOLD_OUT The requested dates/occupancy produced only sold-out results or no availability.
TARGET_CHALLENGE Booking.com returned an access challenge.
PARTIAL At least one base row was saved, but a target or optional enrichment failed, or the safe deadline was reached.
INPUT_CONFIGURATION_LIMIT_REACHED Input validation, a documented target/input limit, or the candidate allowance before the first filtered match was reached. Validation errors occur before the start event.
FAILED An unexpected target, transport, storage, or response error stopped the run.

When optional work fails, every retained row has a structured warning with scope, code, message, and retryable. Deadline-limited runs also write PENDING_WORK.

API usage

Node.js

import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('fetch_cat/booking-com-hotels-scraper').call({
  destination: 'Lisbon',
  maxHotelsPerSearch: 10,
  maxCandidatesPerSearch: 50,
  maxHotelsPerRun: 10,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items);

Python

from apify_client import ApifyClient

client = ApifyClient("$APIFY_TOKEN")
run = client.actor("fetch_cat/booking-com-hotels-scraper").call(run_input={
    "destination": "Lisbon",
    "maxHotelsPerSearch": 10,
    "maxCandidatesPerSearch": 50,
    "maxHotelsPerRun": 10,
})
items = client.dataset(run["defaultDatasetId"]).list_items().items
print(items)

cURL

curl -X POST 'https://api.apify.com/v2/acts/fetch_cat~booking-com-hotels-scraper/runs?token=$APIFY_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{"destination":"Lisbon","maxHotelsPerSearch":10,"maxCandidatesPerSearch":50,"maxHotelsPerRun":10}'

MCP and AI agents

The input, output, and dataset schemas are published for OpenAPI and Apify MCP use. Ask an agent to start with lean output and opt into only the enrichments it needs.

claude mcp add apify -- npx -y @apify/actors-mcp-server --actors fetch_cat/booking-com-hotels-scraper

Equivalent MCP JSON configuration:

{
  "mcpServers": {
    "apify": {
      "command": "npx",
      "args": ["-y", "@apify/actors-mcp-server", "--actors", "fetch_cat/booking-com-hotels-scraper"],
      "env": { "APIFY_TOKEN": "$APIFY_TOKEN" }
    }
  }
}

Example prompts:

  • “Collect 10 lean Booking.com hotel rows for Lisbon and preserve total-price/tax context.”
  • “Schedule a Paris snapshot 14 days ahead for a three-night stay, sorted by price.”
  • “Find 4- and 5-star Lisbon hotels rated 8+ and evaluate at most 50 candidates.”
  • “Resolve this direct Booking.com hotel URL and add rooms and policies.”
  • “Compare three destinations, cap each search at 5 hotels, and cap the whole charged run at 12 rows.”

Troubleshooting

  • TARGET_CHALLENGE: retry later or use a smaller target set. The actor does not solve target challenges or promise an unblock.
  • NO_MATCHES: verify spelling or use a direct hotel URL when you already know the property.
  • SOLD_OUT: verify dates, guest ages, and occupancy, or retry without dates to collect identity-only listings.
  • PARTIAL: inspect row warnings and each enrichment status; successful base data remains usable.
  • INPUT_CONFIGURATION_LIMIT_REACHED: check childrenAges, the chosen date mode, target count, price range, and all three explicit limits. With filters, raise maxCandidatesPerSearch or relax the filters.
  • Returned currency differs from requested currency: use provenance.requested.currency and priceContext.returnedCurrency rather than assuming conversion.

Legality and responsible use

Use this actor for lawful public-data research. Follow Booking.com’s terms, applicable laws, and privacy requirements. Do not use it for personal traveler data, reservations, account-only data, abusive traffic, or attempts to bypass access controls.

Support

For a reproducible issue, include the Apify run ID or run URL, exact input JSON, RUN_SUMMARY, expected output, actual output, and a reproducible public URL when one exists. Never post API tokens, cookies, or private traveler data.

Common questions

Questions and answers reused from the canonical actor README.

Can I use this through the API or MCP?

Yes. The Actor publishes input, output, and dataset schemas for Apify API, OpenAPI, and MCP clients.

Should I use a destination or a direct hotel URL?

Use a destination to discover supply and a direct URL when you already know the property. Direct URLs are generally more deterministic, but both modes can be redirected, challenged, or blocked.

Are optional enrichments charged separately?

No. The Actor charges the base row only when it is saved. There are no rooms, policies, facilities, photos, or reviews charge events.

Can scheduled runs keep dates current?

Yes. Use checkInOffsetDays with stayNights; each run records the resolved dates and time. Fixed and relative date modes cannot be combined.

Does enrichReviews return guest review text?

No. It returns listing-level review details and available summary metadata. Use the related reviews-specific Actor when individual public reviews are required.