Booking.com Hotel Prices Scraper
Give it Booking.com hotel links or IDs, or a city, plus check-in dates and nights, and it returns one JSON row per hotel and stay: the cheapest total price, every room rate (meal plan, cancellation, taxes and fees), a sold-out flag, stars and review score. $2.70 per 1,000 rows on the free plan, less on paid plans; you pay only for rows you get.
$2.70 per 1,000 price rows on the Free plan, down to $2.58 on higher plans
Why this actor
- Many stays in one run. Explicit date lists, a date range with a step, and several stay lengths at once.
- Watch mode: pay only for changes. With
watchand awatchKey, later runs return and bill only the hotel and stay rows whose price, availability or rates moved. - Sold-out rows are data. You see when a hotel has no rate for your dates.
- Clear errors. An input or date that cannot be read comes back as a free
errorrow with acode.
Drop-in input
Drop-in input. Input written for voyager/booking-scraper (search, checkIn, checkOut, minScore, starsCountFilter, minMaxPrice, sortBy, search-result startUrls), santamaria-automations/booking-com-scraper (destination, checkin, checkout) or rl1987/booking-api-scraper (search, hotel_ids, windowDays, stayNights) works as it is.
As stated in the actor’s README. The full list of accepted input names is on the Store page.
Switching from another actor
- From Booking Scraper voyager/booking-scraper, 14 input fields work as they are
What you get
| Field | What it holds |
|---|---|
hotelId, hotelName, hotelUrl | Booking.com id, name and page link. |
stars, reviewScore, reviewCount | Star rating, guest score on the 0-10 scale, number of reviews. |
propertyType, city, countryCode, address | Where it is and what kind of property it is (hotel lookups give all four; destination rows give city, country and address). |
checkIn, checkOut, nights, adults, children, rooms | The stay this row is about. |
currency | Currency of every price in the row. |
available, soldOut | Whether the hotel can be booked for the stay. A sold-out row has no price. |
minPrice, minPricePerNight | Cheapest total for the whole stay (all rooms) and per night. |
originalPrice | The same rate before discounts. |
taxesAndFeesExcluded | Taxes and fees shown as "excluded" on the site (for example VAT and city tax paid on top). |
mealPlan, freeCancellation, cancellationPolicy, cheapestRoom | Meal plan (ROOM_ONLY, BREAKFAST, HALF_BOARD, ...), whether the cheapest rate has free cancellation, free_cancellation or non_refundable, and the room it is for. |
roomRates | Every rate for the stay: room, meal plan, price, per night, original price, taxes, cancellation (with the site's wording), prepayment. Cheapest first. |
rateCount, source | How many rates, and where they come from: roomTable (every rate) or search (the cheapest rate of each hotel). |
searchDestination, searchRank | Destination searches: the text you gave and the hotel's position in the results. |
change | Watch mode: type (new, price_drop, price_rise, availability, rates), fields (old -> new), previousCheckAt, previousMinPrice, priceChange. |
scrapedAt | When the row was read. |
Sample row (from the README example; long text shortened, lists cut to two items):
{
"rowType": "price",
"platform": "booking",
"hotelId": "55103",
"hotelName": "Hôtel De La Paix",
"hotelUrl": "https://www.booking.com/hotel/fr/de-la-paix-paris1.html",
"stars": 3,
"reviewScore": 8.5,
"reviewCount": 2112,
"city": "Paris",
"countryCode": "fr",
"checkIn": "2026-12-10",
"checkOut": "2026-12-12",
"nights": 2,
"adults": 2,
"children": 0,
"rooms": 1,
"currency": "EUR",
"available": true,
"soldOut": false,
"minPrice": 236.73,
"minPricePerNight": 118.37,
"originalPrice": 338.18,
"taxesAndFeesExcluded": 45.79,
"mealPlan": "ROOM_ONLY",
"freeCancellation": false,
"cancellationPolicy": "non_refundable",
"cheapestRoom": "Standard Double Room",
"roomRates": [
{
"roomName": "Standard Double Room",
"mealPlan": "ROOM_ONLY",
"price": 236.73,
"pricePerNight": 118.37,
"cancellation": "non_refundable",
"cancellationText": "Non-refundable",
"payment": "prepayment_required"
}
],
"rateCount": 12,
"source": "roomTable",
"scrapedAt": "2026-10-21T08:00:00.000Z"
}
Price
Live Apify price on 2026-11-01, per 1,000 rows, by Apify plan. You pay only for rows delivered; error and summary rows marked free in the README cost nothing. Plus $0.001 per GB of memory when a run starts.
| Per 1,000 | Free | Bronze | Silver | Gold | Platinum | Diamond |
|---|---|---|---|---|---|---|
| Price row (main) | $2.70 | $2.67 | $2.64 | $2.58 | $2.58 | $2.58 |
Use cases
- Rate shopping: compare your property with the competing set for the next 60 days, every night.
- Price alerts: schedule watch-mode runs on the hotels you track and act on
price_droprows. - Destination research: how expensive is a city on a given weekend, by stars and review score.
- Travel and comparison sites: fill price calendars for many hotels, dates and stay lengths.
Run it from code or an AI agent
Actor ID arcadium/booking-prices. Set APIFY_TOKEN to your Apify API token. The input below is the actor’s prefilled example; see the guide for every field.
curl
curl -X POST "https://api.apify.com/v2/acts/arcadium~booking-prices/run-sync-get-dataset-items" \
-H "Authorization: Bearer $APIFY_TOKEN" \
-H "Content-Type: application/json" \
-d '{"startUrls":[{"url":"https://www.booking.com/hotel/fr/de-la-paix-paris1.html"}],"destinationType":"CITY","maxHotels":20,"checkInDates":["+30d"],"nights":[1],"childrenAges":[],"currency":"EUR","includeRoomRates":false,"watch":false,"watchKey":"default","digest":false,"digestDays":7}'JavaScript
// npm install apify-client
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('arcadium/booking-prices').call({
"startUrls": [
{
"url": "https://www.booking.com/hotel/fr/de-la-paix-paris1.html"
}
],
"destinationType": "CITY",
"maxHotels": 20,
"checkInDates": [
"+30d"
],
"nights": [
1
],
"childrenAges": [],
"currency": "EUR",
"includeRoomRates": false,
"watch": false,
"watchKey": "default",
"digest": false,
"digestDays": 7
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items);Python
# pip install apify-client
import os
from apify_client import ApifyClient
client = ApifyClient(os.environ["APIFY_TOKEN"])
run = client.actor("arcadium/booking-prices").call(run_input={
"startUrls": [{
"url": "https://www.booking.com/hotel/fr/de-la-paix-paris1.html",
}],
"destinationType": "CITY",
"maxHotels": 20,
"checkInDates": ["+30d"],
"nights": [1],
"childrenAges": [],
"currency": "EUR",
"includeRoomRates": False,
"watch": False,
"watchKey": "default",
"digest": False,
"digestDays": 7,
})
items = client.dataset(run.default_dataset_id).list_items().items # apify-client 3.x returns a Run object
print(items)MCP
{
"mcpServers": {
"apify": {
"url": "https://mcp.apify.com/?tools=arcadium/booking-prices",
"headers": {
"Authorization": "Bearer <APIFY_TOKEN>"
}
}
}
}LangChain
# pip install langchain-apify (reads APIFY_API_TOKEN from the environment)
from langchain_apify import ApifyActorsTool
tool = ApifyActorsTool("arcadium/booking-prices")
rows = tool.invoke({"run_input": {
"startUrls": [{
"url": "https://www.booking.com/hotel/fr/de-la-paix-paris1.html",
}],
"destinationType": "CITY",
"maxHotels": 20,
"checkInDates": ["+30d"],
"nights": [1],
"childrenAges": [],
"currency": "EUR",
"includeRoomRates": False,
"watch": False,
"watchKey": "default",
"digest": False,
"digestDays": 7,
}})
# or give `tool` to an agent: create_react_agent(llm, [tool])LlamaIndex
# pip install llama-index-readers-apify
import os
from llama_index.core import Document, VectorStoreIndex
from llama_index.readers.apify import ApifyActor
reader = ApifyActor(os.environ["APIFY_TOKEN"])
documents = reader.load_data(
actor_id="arcadium/booking-prices",
run_input={
"startUrls": [{
"url": "https://www.booking.com/hotel/fr/de-la-paix-paris1.html",
}],
"destinationType": "CITY",
"maxHotels": 20,
"checkInDates": ["+30d"],
"nights": [1],
"childrenAges": [],
"currency": "EUR",
"includeRoomRates": False,
"watch": False,
"watchKey": "default",
"digest": False,
"digestDays": 7,
},
dataset_mapping_function=lambda item: Document(text=str(item.get("title") or ""), metadata={"url": item.get("url")}),
)
index = VectorStoreIndex.from_documents(documents)CrewAI
# pip install crewai crewai-tools langchain-apify (reads APIFY_API_TOKEN from the environment)
from crewai import Agent
from crewai_tools import ApifyActorsTool
tool = ApifyActorsTool(actor_name="arcadium/booking-prices")
researcher = Agent(
role="Research analyst",
goal="Answer with data collected by the tool",
backstory="Uses public web data.",
tools=[tool],
)n8n
{
"nodes": [
{
"name": "Run arcadium/booking-prices",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4,
"position": [
0,
0
],
"parameters": {
"method": "POST",
"url": "https://api.apify.com/v2/acts/arcadium~booking-prices/run-sync-get-dataset-items",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "{\"startUrls\":[{\"url\":\"https://www.booking.com/hotel/fr/de-la-paix-paris1.html\"}],\"destinationType\":\"CITY\",\"maxHotels\":20,\"checkInDates\":[\"+30d\"],\"nights\":[1],\"childrenAges\":[],\"currency\":\"EUR\",\"includeRoomRates\":false,\"watch\":false,\"watchKey\":\"default\",\"digest\":false,\"digestDays\":7}",
"options": {
"timeout": 300000
}
}
}
],
"connections": {}
}Google Sheets
// Extensions > Apps Script. Store your token once: Project Settings > Script properties > APIFY_TOKEN.
function importLastRun() {
const token = PropertiesService.getScriptProperties().getProperty('APIFY_TOKEN');
const url = 'https://api.apify.com/v2/acts/arcadium~booking-prices/runs/last/dataset/items?status=SUCCEEDED&clean=true&format=json';
const res = UrlFetchApp.fetch(url, { headers: { Authorization: 'Bearer ' + token } });
const rows = JSON.parse(res.getContentText());
if (!rows.length) return;
const keys = Object.keys(rows[0]);
const values = rows.map((r) => keys.map((k) => (typeof r[k] === 'object' ? JSON.stringify(r[k]) : r[k])));
const sheet = SpreadsheetApp.getActiveSheet();
sheet.clearContents();
sheet.getRange(1, 1, 1, keys.length).setValues([keys]);
sheet.getRange(2, 1, values.length, keys.length).setValues(values);
}Tutorials
Limitations
- Public prices as an anonymous visitor sees them. No login, no member or Genius prices, no mobile-only rates.
- Prices depend on the currency, the guests and the dates you ask for. Booking.com may show slightly different prices to different countries; the actor reads them from a US connection.
- Destination searches return the cheapest rate of each hotel (set
includeRoomRatesfor all rates). Booking.com returns at most about 1,000 hotels per search; split a bigger market withstarsor the price filters. popularityorder changes from request to request; useprice(default) when you need stable ranks.- Dates up to about 16 months ahead; later dates come back sold out. A past date gives a free error row; stays above 30 nights are rejected when the input is checked.
taxesAndFeesExcludedis what the site lists as taxes and fees on top of the price (for example VAT and city tax); some rates include them already.- Names that exist in several places use the site's best match. Add the country or give the destination id.
FAQ
Why is a date missing for a hotel? Either it did not match your filters (no error row is added) or the date was rejected (past, too far ahead); rejected dates get a free error row.
How do I find a hotel id? It is the number in the hotelId field of any row, and the actor accepts the hotel page link instead.
What does a sold-out row mean? Booking.com had no rate for the dates and guests you asked for. The row is delivered (and charged) because the availability is the information; turn on watch to get it only when it changes.
Something looks wrong in the output. Who do I tell? Open an issue on the actor page with the run link. We read them all.
Related actors
Agoda and Trip.com Reviews Scraper
Agoda and Trip.com hotel reviews for hotel links or IDs: score, original text, English translation if any, hotel reply; $5.40 per 1,000 Agoda reviews.
$5.40 per 1,000 Agoda reviews on the Free plan, down to $2.70 on higher plans
Booking.com Reviews Scraper
Booking.com hotel reviews for hotel URLs: score, liked and disliked text, hotel reply, reviewer country, traveler type, stay details; $1.80 per 1,000 reviews.
$1.80 per 1,000 reviews on the Free plan, down to $0.81 on higher plans
Google Hotel Reviews Scraper
Google hotel reviews for hotel links or names: rating, text, translation, date, trip type, room/service/location scores, owner reply; $0.54 per 1,000 reviews.
$0.54 per 1,000 reviews on the Free plan, down to $0.27 on higher plans
Google Hotels Scraper
Google Hotels prices for a city or hotel and dates, $4.49 per 1,000 hotels; zerobreak/google-hotels-scraper input works as is (23% of its runs failed, Oct 2026).
$4.49 per 1,000 hotel rows on the Free plan, down to $2.69 on higher plans
Hotel Reviews Scraper
Hotel reviews from Tripadvisor, Booking.com, Google Hotels, Agoda and Trip.com in one dataset, ratings on one 1-5 scale; $4.50 per 1,000 reviews.
$4.50 per 1,000 reviews on the Free plan, down to $0.80 on higher plans
Tripadvisor Reviews Scraper
Tripadvisor reviews for hotel, restaurant and attraction URLs or place names: rating, text, date, owner reply, sub-ratings; $4.50 per 1,000 reviews.
$4.50 per 1,000 reviews on the Free plan, down to $0.80 on higher plans