Booking Search Scraper

Same city, same site.
Different columns.

We hold ninety rows from three successful runs over one city, and the finding that matters most is not about hotels. Three of the sixteen columns appeared or vanished depending on the presentation the site served that day - the currency, the distance unit, the tax line and the star field all moved together. Read this before you diff two exports.

one-time 500 free rowsthen $0.002 per row90 rows across 5 runsCSV · JSON · Excel
How it works

A search URL in, a spreadsheet out.

  1. STEP 1Sign in to the platform.
  2. STEP 2Open the Booking Search Scraper.
  3. STEP 3Paste the Booking search URLs you want, one per line - a results page for a destination. Both the short and the fully-qualified destination forms worked in our runs.
  4. STEP 4Choose your output format (CSV / JSON / XLSX).
  5. STEP 5Run the job.
  6. STEP 6Download the results - sixteen columns, one row per search result.
What to expect

The things worth knowing before you run it.

The presentation decides which columns you get

This is the finding to plan around. Two of our runs came back with prices in one currency and distances in miles; the third came back in another currency with distances in kilometres. That much is cosmetic. What is not cosmetic is that three columns moved with them: the run in the second currency carried a tax line on every single row and a star field on nearly all of them, and carried no was-price at all. The two runs in the first currency were the exact mirror - a was-price on some rows, and no tax line or star field anywhere. Same site, same city.

The stars column does not hold a number of stars

Across ninety rows the field took six values and no others: empty on more than half, and otherwise 4, 8, 12, 16 or 20 - always a multiple of four, never a 1, 2, 3 or 5. The two properties carrying the top value are the two luxury hotels in the set; the ones carrying the lowest are budget hotels. So the column is consistent with four times a one-to-five rating. We are not going to state that as fact, because we could not check it against the live listing - see the note on access below.

The price is for a night the export does not name

Every row carries a price and none carries a date. The check-in and check-out the price applies to live inside the link, in its querystring, and in our runs they landed about a fortnight after the run itself. So a price column compared across two exports is comparing two different nights unless you pull the dates out of the URL first. The occupancy column is no help either: it read the same on all ninety rows, because it is the search's default rather than anything about the hotel.

The same search twice returns different hotels

We ran the identical query four days apart and the two result sets shared no hotel pages at all. A third run shared four. That is what you would expect from a page ranking availability for a specific night rather than listing a city's hotels - but it means this export is a snapshot, not a register. Do not build a stable hotel list from it; build a time series and key it on the hotel page address with the tracking parameters stripped.

There is no status column, so an empty run is silent

Two of our five runs returned nothing at all - an empty file, no rows, no explanation. Every catalogue service we run writes a per-row flag that records a block or a rejected input; this one has no such column, so when a run comes back empty there is nowhere for a reason to live. Plan for it: check the row count yourself, and treat zero as something to retry rather than as an answer.

Two ways of saying empty, in one file

Most columns use an empty string when there is nothing to report. Two - the tax line and the deal label - use a JSON null instead, and never an empty string. A loader that tests only for one of the two will silently mis-handle the other, and these are exactly the fields you would filter on to find discounted rooms. It is a small thing that costs an afternoon if you meet it late.

What you get back

Sixteen columns, and nine of them always arrive.

Read from a real export, in sheet order, with fill counts measured across all ninety rows we hold. Where a column is described as sometimes absent, the note says what it depends on - because on this service that is usually the run rather than the hotel.

query
The Booking search URL you submitted, echoed back. Populated on all 90 rows. Both destination forms we tried worked, so this is how you tell which search a block of rows came from.
name
The property name as the results page states it. Populated on all 90 rows - the most reliable field in the export, and the one to key a human-readable report on.
stars
Populated on 39 of 90 rows, and never on the two runs in the first currency. It does not hold a number of stars: across every row it took only the values 4, 8, 12, 16 or 20. That is consistent with four times a one-to-five rating, and the properties at the top and bottom of the range fit that reading, but we could not confirm it against the live site. Divide with caution, and validate before you display.
rating
The guest score on a ten-point scale. Populated on 89 of 90 rows, ranging from 1.3 to 9.4 in what we hold. The single exception is a property that also had no review count and no label - a listing with nothing scored yet.
review_label
The word the site puts beside the score. Populated on 65 of 90 rows, with values such as the ones you would expect - plus one that is not a word at all but the site's own column heading, on five rows that had only a handful of reviews each. Filter that value out before you group on this.
reviews
How many reviews the score is built on. Populated on 89 of 90 rows, from a single review up to several thousand. Worth reading beside rating: the lowest score in our data sits on three reviews.
location
The neighbourhood and city as the results page labels it. Populated on all 90 rows. A district name rather than an address - there is no street, postcode or coordinate anywhere in this export.
distance
How far the property is from the centre, as free text. Populated on all 90 rows - but the unit follows the run, not the property: miles on two of our runs and kilometres on the third. Parse the unit, never assume it.
room_type
The specific room the quoted price is for. Populated on all 90 rows. Note that some values end in a stray label where the bed configuration has run into the room name - a capture artifact worth trimming.
occupancy
The stay the price covers. Populated on all 90 rows - with the identical value on every one. It reflects the search's default rather than anything about the listing, so it carries no information across rows. Treat it as a constant to check, not a field to group on.
price
The headline nightly rate, with a currency symbol. Populated on all 90 rows - and the currency is the run's, not the hotel's. Two of our runs quoted one currency and the third quoted another for the same city, so strip and convert before comparing anything across exports.
original_price
The struck-through was-price where the listing shows one. Populated on 13 of 90 rows, all of them in the first currency - the run in the second currency carried none at all. Eleven of the thirteen also carry a deal label and two do not, so a discount can exist without being named.
taxes
The tax and fee line shown under the rate. Populated on 42 of 90 rows - which is every row of one run and no row of the other two. This is the clearest case of a column depending on the presentation rather than the property. Absent values arrive as a JSON null, not an empty string.
deal
The promotion label where one is shown. Populated on 29 of 90 rows, with a small set of named campaign values. Like taxes, an absent value is a JSON null rather than an empty string.
url
The link to the property page. Populated on all 90 rows - and heavily decorated: an affiliate identifier, a tracking label, a session identifier, a timestamp and the block identifiers behind the quoted rate, all in the querystring. The check-in and check-out dates the price applies to are in here too, and nowhere else in the export. Strip to the path for a stable key; read the dates out before you do.
image
A thumbnail on the site's image host. Populated on all 90 rows, at a small fixed size. Fine for a listing view, too small to be a product image.

Sixteen columns per row · CSV, JSON or Excel

Two structural facts sit above every column note here. The first is that this export has no status column. Every other service we run writes a per-row flag recording whether the fetch succeeded, was refused or was given the wrong kind of input - and that is how two of our five runs would have explained themselves. They returned zero rows and nothing else, so the file cannot tell you whether the search was empty, the page did not load, or something in between. Check the row count in your own pipeline and treat a zero as a retry. The second is that three columns depend on the presentation rather than the property. The run served in the second currency carried a tax line on every row and a star value on nearly every row, and carried no was-price at all; the two runs served in the first currency were the exact mirror. The distance unit switched with them. That means a naive diff of two exports will report that hotels gained and lost attributes when nothing about them changed - so record which currency a run came back in and compare like with like. Everything else is ordinary: nine columns arrived on all ninety rows, the score and its review count on all but one, and the remaining fields are genuinely optional in the way you would expect.

Common workflows

What a search snapshot is good for.

Rate monitoring

Track what a city costs on a given night

Run the same destination on a schedule, pull the check-in date out of the link, and you have a rate series per night rather than per run. Key it on the property page with the tracking parameters stripped, and record the currency alongside - that pair is what makes two runs comparable.

Revenue · Pricing
Competitive set

See who is discounting, and by how much

The was-price and the deal label together tell you which listings are cutting rates and which are running a named campaign. Both are optional, and one of our rows was discounted without carrying a label - so compute the gap from the two price columns rather than trusting the label alone.

Hospitality · Competitive intel
Market survey

Profile a destination's supply and scores

Ninety rows over one city gave us scores from the low ones to the mid nines and review counts from a single review to several thousand. That is enough to describe a market's shape - as long as you remember you are describing the listings available for one night, not the city's hotel stock.

Research · Analysis
Room-level comparison

Compare like with like, not hotel with hotel

Each row names the specific room the price is for, so a comparison between two properties is really a comparison between two room types. Normalise that column first - some values carry a stray bed label - and the resulting table is far more honest than a bare price-per-hotel list.

Analysis · Data quality
Pricing

Rows returned, not attempts made.

Free tier

First 500 rows are free

One time, on signup. No card, and nothing to cancel afterwards.

One-time, on signup
Rate

then $0.002 per row

Billed on rows actually returned. One search URL produced forty-odd rows on two of our runs and none on two others, so the cost of a job follows what comes back rather than how many searches you queued.

Billed on rows returned
No subscription

Nothing recurring

Credits do not expire on a monthly cycle, so a schedule that runs weekly and occasionally comes back empty does not quietly drain anything.

No monthly expiry
10% off your first paid run.Use code LIVESCRAPER10 at checkout.
Register
Pairs well with

Other places people look for a room.

The legal bit

Is it legal to scrape Booking.com?

Rates, scores and room names on a public results page are ordinary commercial facts. What deserves care here is the layer in front of the site, and the fact that we could not get past it ourselves.

A nightly rate, a property name and a room type are facts about accommodation offered for sale, read from a page served to any visitor. Nothing in the sixteen columns describes a guest: no name, no email address, no phone number, no booking reference. The review counts and scores are aggregates the site publishes on the listing itself, not individual reviews attached to individual people - so the data-protection questions that shape our people-facing services stay out of this one. What the export does carry is a link decorated with an affiliate identifier and a session token, which is worth stripping before you store rows anywhere shared.

The site's stated preference is permissive on the paths this service uses. Its robots.txt answered a plain request with HTTP 200 on 12 August 2026, and read with a proper group matcher the general group carries seventy-one rules and no blanket exclusion; both the search-results path and the property pages our rows link to are allowed. The bare exclusions in that file belong to a handful of specifically named crawlers, not to general ones. The runtime is a different matter, and we would rather be plain about it than imply we walked in: an ordinary request from us for a property page and for the search path both came back with a challenge page rather than content, so we could not corroborate a single row against the live listing. Everything on this page therefore comes from the export itself. Our own catalogue records that this service needs a paid residential proxy, and names a different protection vendor than the one our probe encountered. We report both and pick neither.

Our own terms are the same as on every other service here. Publicly available pages only, nothing behind a login, no third-party trackers on the data layer, and exports auto-delete after 30 days. Your first 500 rows are free and need no credit card.

livescraper.app · what shapes a run
robots.txt allows the search path and the property pages
A plain request for content met a challenge, not a page
90 rows across 3 runs; 2 more returned nothing
No row could be checked against the live listing
Exports auto-delete after 30 days
Two names are on record for the layer in front of this site. We report both and choose neither.
Common questions

What people ask before signing up.

Why did two of my columns disappear between runs?+
Because the presentation changed. In our data the currency, the distance unit, the tax line and the star field all moved together: the run served in one currency carried a tax value on every row and a star value on nearly all of them and no was-price at all, while the runs served in the other currency were the exact mirror. Nothing about the hotels changed. Record which currency a run came back in and compare like with like.
What is the stars column actually holding?+
Not a number of stars. Across ninety rows it took only the values 4, 8, 12, 16 and 20, or nothing - never a 1, 2, 3 or 5. The properties carrying the top value are the luxury hotels in the set and the ones carrying the lowest are budget hotels, so it is consistent with four times a one-to-five rating. We will not state that as fact, because the live site served us a challenge page rather than content and we could not check it.
Which night does the price apply to?+
One that no column names. The check-in and check-out dates live inside the link's querystring, and in our runs they fell about a fortnight after the run. The occupancy column does not help - it read identically on all ninety rows because it reflects the search default. Extract the dates from the URL before you compare any two prices.
Why does the same search return different hotels each time?+
Because it ranks availability for a specific night rather than listing a city's hotels. We ran the identical query four days apart and the two result sets shared no property pages at all; a third run shared four. Treat each export as a snapshot and key your time series on the property page address with the tracking parameters stripped.
One of my runs came back empty. What happened?+
We cannot tell you, and neither can the file. Two of our five runs returned zero rows, and this service has no status column - so unlike every catalogue service we run, there is nowhere for a reason to be recorded. Check the row count yourself and treat a zero as something to retry.
Did you verify the rows against Booking.com?+
No, and we would rather say so. On 12 August 2026 an ordinary request for both a property page and the search path returned a challenge page instead of content. So everything on this page is measured from the export and nothing is presented as confirmed against the live listing. That is a real limit on what we can claim here, and it is unusual among our pages.
Is Booking.com blocked?+
Its robots.txt is permissive on the paths this service uses - seventy-one rules in the general group, no blanket exclusion, and both the search path and the property pages allowed. What sits in front of the site at runtime is a different layer. Our own catalogue records that this service needs a paid residential proxy and names one protection vendor; our probe encountered a different one. We report both and pick neither.
How do I handle the empty values?+
Carefully, because there are two conventions in one file. Most columns use an empty string when there is nothing to report, but the tax line and the deal label use a JSON null instead and never an empty string. Those are exactly the fields you would filter on to find discounted rooms, so a loader that checks for only one of the two will quietly get it wrong.
How many rows should I expect per search?+
Our three successful runs returned five, forty-two and forty-three rows from a single search URL each. We are not going to turn that into an average - two other runs returned nothing, and a sample of five is far too small to promise a yield from.
What formats can I export?+
CSV, JSON or Excel. The sixteen columns and their order are the same in all three. Note that the null-versus-empty distinction described above is visible in JSON and flattens in the spreadsheet formats.

Ninety rows, and an honest note on each.

Hotel, score, review count, location, room type, price and a link - one row per search result. Read the note on how the columns shift with the presentation before you diff two exports. Your first 500 rows are free, then $0.002 each.

Activates instantly · no card required

Scrape Booking search results

Booking.com is one of the largest accommodation marketplaces in the world, and this service turns one of its search-results pages into a table: one row per listing, sixteen columns, covering the property name, its guest score and review count, the neighbourhood, the distance from the centre, the specific room the quote is for, the nightly rate, any struck-through was-price, any tax line, any promotional label, a link and a thumbnail. We hold five runs over a single city, three of which returned rows - five, forty-three and forty-two of them - and two of which returned nothing at all. Ninety rows is a reasonable basis for describing what the export looks like, and it is what every figure on this page is measured from.

The most useful thing we found is not about hotels. Two of our runs came back with rates in one currency and distances in miles; the third came back in a different currency with distances in kilometres. That much is cosmetic. What is not cosmetic is that three columns moved with the presentation: the run in the second currency carried a tax line on every single row and a star value on nearly all of them, and carried no was-price anywhere, while the two runs in the first currency were the exact mirror - a was-price on some rows and no tax or star field at all. Nothing about the properties changed between those runs. If you diff two exports without recording which presentation each came back in, the diff will tell you that hotels gained and lost attributes, and it will be wrong.

Two more structural points are worth knowing before you write a loader. The first is the star column, which does not hold a number of stars: across all ninety rows it took only the values four, eight, twelve, sixteen and twenty, or nothing at all - never a one, a two, a three or a five. The properties carrying the highest value are the luxury hotels in the set and those carrying the lowest are budget ones, so the field is consistent with four times a conventional one-to-five rating. We stop short of stating that as fact, for a reason given below. The second is that the price has no date attached to it. Every row carries a rate and none carries a night; the check-in and check-out the rate applies to sit inside the link's querystring and nowhere else, and the occupancy column is identical on all ninety rows because it reflects the search's default rather than the listing. Two prices compared without first extracting those dates are two different nights.

There is also a limit on this page that we would rather state than paper over. On 12 August 2026 the site's robots.txt answered a plain request and is permissive on the paths this service uses - seventy-one rules in the general group, no blanket exclusion, and both the search path and the property pages allowed. But an ordinary request for actual content, on a property page and on the search path alike, came back as a challenge rather than a page. So unlike most of our catalogue pages, no row here has been corroborated against the live listing; everything is measured from the export. Our own service catalogue records that this scraper needs a paid residential proxy and names one protection vendor for the site, while our own probe met a different one - two names for the same layer, and we report both and pick neither. Finally, treat each export as a snapshot rather than a register: we ran the identical query four days apart and the two result sets shared no property pages at all, which is what a page ranking availability for one night will do. Publicly available pages only, no third-party trackers on the data layer, exports auto-delete after 30 days, and your first 500 rows are free with no credit card.