Here is the thing most people hit when they try to scrape TripAdvisor reviews: the official TripAdvisor Content API returns only 3 reviews per location. Three. For a hotel with eight thousand reviews, that is not a dataset, that is a teaser. And it is gated behind an approval process, with no owner responses and no subratings.
This guide covers what you can pull from a TripAdvisor review, why the DIY route is fiddly, working Python, a no-code shortcut, and a plain comparison. If you want the deeper reference, there is a full guide to scraping TripAdvisor reviews too.
What you can pull from a review
Per review you can get:
- The overall star rating (1 to 5), the review title, and the full review text with its language
- Per-review subratings: cleanliness, service, value, location, rooms, sleep quality, where the reviewer left them
- Dates: the published date and the travel date, plus helpful votes
- Owner / management responses, with responder name and date
- Reviewer profile: username, home location, contribution count
- An LLM-ready markdown block per review
And this works across all three TripAdvisor place types: hotels, restaurants, and attractions. Every run also returns a place record with property details (rating, structured city ranking, price range, hotel class, amenities), TripAdvisor's official category subratings, and its own AI review summary.
Why scraping TripAdvisor is hard
- The official API caps at 3 reviews. So for anything at scale, scraping the public data is the route, and there is no owner-response or subrating data in that API anyway.
-
Three different place types, three URL formats. Hotels use
Hotel_Review, restaurants useRestaurant_Review, attractions useAttraction_Review. A scraper built for one type misses the others. - Reviews are paginated and loaded dynamically, not sitting in the initial HTML, so a plain request gets you a shell.
- Bot protection and rate limits on top.
So the work is handling each place type, paging through every review, pulling the nested subratings and owner responses, and staying unblocked, on repeat.
Three ways to get the data
| DIY Python | TripAdvisor Hotel Reviews API (actor) | Official Content API | |
|---|---|---|---|
| Reviews per location | All, if you build paging | All | Only 3 |
| Setup time | Hours to days | ~30 seconds | Approval required |
| Hotels + restaurants + attractions | Build per type | One run | Limited |
| Subratings + owner responses | Extra parsing | Yes | No |
| Property details + AI summary | Separate scrape | Same run | No |
| Cost | Proxies + eng time | Pay-per-result | Gated |
| Best for | One-off | Scheduled, at scale | Not much |
Option A: DIY in Python
A plain request to a hotel page returns a JavaScript shell, not the reviews:
import httpx
url = "https://www.tripadvisor.com/Hotel_Review-g187497-d1465497-Reviews-W_Barcelona-Barcelona_Catalonia.html"
r = httpx.get(url, headers={"User-Agent": "Mozilla/5.0"})
print("reviewText" in r.text) # usually False; reviews load separately, paginated
To get the real data you page through every review, normalize hotels vs restaurants vs attractions, dig the subratings and owner responses out of nested structures, and keep requests unblocked. Fine for one place, a lot of moving parts for a portfolio or a whole city.
Option B: the no-code / API shortcut
When you want clean rows, the TripAdvisor Hotel Reviews API on Apify takes any TripAdvisor URL (or a bare location ID), and returns structured JSON: every review plus the place record. No login, no proxy setup, it handles the blocking for you.
from apify_client import ApifyClient
client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("factden/tripadvisor-hotel-reviews-api").call(run_input={
"mode": "reviews",
"startUrls": [
{"url": "https://www.tripadvisor.com/Hotel_Review-g187497-d1465497-Reviews-W_Barcelona-Barcelona_Catalonia.html"}
],
"locationIds": ["1465497"],
"maxReviews": 500,
"reviewLanguages": ["all"],
"minRating": 1,
"maxRating": 5,
})
for review in client.dataset(run["defaultDatasetId"]).iterate_items():
print(review["rating"], review["title"], review["text"][:60])
Paste URLs or bare location IDs (the d-number in the URL), mix them freely. Set maxReviews high for all of them. Filter by reviewLanguages, bound the star range with minRating / maxRating, and window the dates with fromDate / toDate (both YYYY-MM-DD).
Don't know the places yet? Switch to Discover mode and search a whole city:
run = client.actor("factden/tripadvisor-hotel-reviews-api").call(run_input={
"mode": "discover",
"searchTerms": ["Barcelona"],
"placeTypes": ["hotels"],
"maxPlaces": 20,
"discoveryDepth": "quick",
})
That returns a list of the city's places with full details. Take the returned location IDs back into Reviews mode to pull their reviews.
Track reputation over time
The fromDate filter is the one to know if you care about reputation monitoring. Set a Schedule in Apify, point fromDate at your last run, and each run brings back only the new reviews and rating changes since then. That is how teams track new reviews and ratings over time without re-pulling the whole history every time, one cheap incremental pull on a cron.
What comes back: 16 fields per review
| Group | Fields |
|---|---|
| Core |
placeId, placeType, placeName, reviewId, url
|
| Rating |
rating (1 to 5), title, subratings (cleanliness, service, value, location, rooms, sleep quality) |
| Text |
text, language
|
| Dates & signals |
publishedDate, travelDate, helpfulVotes
|
| Owner |
ownerResponse (responder, text, date) |
| Reviewer |
user (username, userLocation, contributions) |
| AI-ready | markdownContent |
A trimmed sample row:
{
"placeId": 1465497,
"placeType": "hotel",
"placeName": "W Barcelona",
"reviewId": "1070232616",
"rating": 5,
"title": "Incredible stay",
"text": "The rooftop and the service were exceptional...",
"publishedDate": "2026-07-26T23:11:20-04:00",
"travelDate": "2026-07",
"helpfulVotes": 3,
"subratings": [{ "name": "Service", "value": 5 }, { "name": "Location", "value": 5 }],
"ownerResponse": { "responder": "GM W Barcelona", "text": "Thank you...", "publishedDate": "2026-07-28" },
"user": { "username": "traveler_bcn", "userLocation": "London, UK", "contributions": 42 },
"markdownContent": "# Incredible stay, W Barcelona\n**Rating:** 5/5 ..."
}
Every run also emits a place record per place: name, geo, overall rating, structured city ranking, price range, hotel class, amenities, TripAdvisor's official category subratings, and its AI review summary. Full field list and snippets are in the GitHub repo.
Grab a free sample dataset
Want to see the data first? There is a free TripAdvisor sample (CSV/JSON) here: factden.com/sample-tripadvisor. Load it into pandas and the ratings and subratings are ready to plot.
FAQ
Is scraping TripAdvisor legal? The reviews are publicly visible. As with any scraping, check TripAdvisor's Terms of Service and your local rules (including GDPR/CCPA for the pseudonymous reviewer fields), and use the data responsibly.
Is there an official TripAdvisor reviews API? Yes, the Content API, but it is gated and returns only 3 reviews per location, with no owner responses or subratings. Scraping the public data is the practical route.
Can I scrape restaurants and attractions too, not just hotels? Yes. One actor covers hotels, restaurants, and attractions, with the same schema, tagged by placeType.
Are ratings on the 5-star scale? Yes, TripAdvisor uses 1 to 5 stars, and that is what rating returns, with per-review subratings on the same scale.
Can I monitor a place for new reviews on a schedule? Yes. Set a Schedule and use fromDate so each run only returns reviews posted since the last pull, ideal for ongoing reputation monitoring.
How do I stop getting blocked? The actor handles it for you and reads the data directly. Plain requests get a JavaScript shell, not the reviews.
Related
- Doing B2B software instead of hotels? See how to scrape G2 reviews.
- Other FactDen hotel scrapers: Expedia hotel reviews and Trip.com and Ctrip hotel reviews.
Questions, or a field you wish it extracted? Drop a comment.
Top comments (0)