# Tripadvisor Scraper — Attractions, Restaurants & Hotels (`zinin/tripadvisor-scraper`) Actor

Search any place on Tripadvisor by name and get its attractions, restaurants or hotels: name, rating, review count, Tripadvisor ranking, price and photo, with the direct Tripadvisor URL for each listing. Public data only, no login required.

- **URL**: https://apify.com/zinin/tripadvisor-scraper.md
- **Developed by:** [Tim Zinin](https://apify.com/zinin) (community)
- **Categories:** Travel, E-commerce
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per event

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Tripadvisor Scraper — Attractions, Restaurants & Hotels by Place Name

Type a place name — "Paris", "Kyoto", "Rome" — and get its top attractions, restaurants or
hotels back as structured rows: name, rating, review count, Tripadvisor's own ranking, price,
a photo and the canonical Tripadvisor URL, ready to sort, filter or join into your own data.

![Tripadvisor Scraper — what goes in and what comes out](https://raw.githubusercontent.com/TimmyZinin/apify-actor-assets/edf228c422129f57e34bbc7ffa4f3551f101b7db/travel-mkt-10/tripadvisor-scraper/readme-hero.webp)

No Tripadvisor account, API key or login is needed — the Actor resolves the place through
Tripadvisor's own public place search (the same lookup that runs when you type into the site's
search box) and then reads the public listing pages that follow, one page per 30 results, for
as many pages as it takes to reach the number of results you asked for.

![Tripadvisor Scraper — automation workflow](https://raw.githubusercontent.com/TimmyZinin/apify-actor-assets/edf228c422129f57e34bbc7ffa4f3551f101b7db/travel-mkt-10/tripadvisor-scraper/readme-workflow.webp)

### What you get

- **A place name in, structured listings out.** No need to find Tripadvisor's internal geo ID
  or URL slug yourself — pass `"Paris"` or `"Kyoto"` and the Actor resolves it the same way
  Tripadvisor's own search box does, then builds the right listing URLs from the result.
- **Three listing types in one run.** Attractions ("Things to Do"), restaurants and hotels,
  each with its own category so a place can be queried for all three at once.
- **Tripadvisor's own ranking, when the source shows one.** Attractions carry the exact
  "#1, #2, #3…" order Tripadvisor renders in its ranked grid — not a rating sort we invented.
- **Rating and review count on every row that has one.** The same 0–5 bubble score and review
  total a visitor sees on the card, parsed as numbers so you can sort and filter immediately.
- **Price where Tripadvisor shows it.** A nightly rate for hotels (e.g. `"$272"`) or a price
  band for restaurants (e.g. `"$$ - $$$"`) — `null` when the source card doesn't carry one
  (this is normal for the attractions grid, which Tripadvisor doesn't price at all).
- **A photo and the direct review-page URL for every listing**, so you can build a visual list
  or click straight through to the source for verification.
- **Pay only for delivered rows.** Blocked requests, unresolved place names and empty
  categories come back as free rows with an `error` field — never billed.
- Runs on Apify: schedule it, monitor it, call it from the API, export to JSON/CSV/Excel or
  push straight into your own pipeline.

### Who uses it

- **Travel content and SEO teams** building "best things to do in X" or "best hotels in X"
  pages who need real, current rankings and review counts instead of writing them from memory —
  and a photo and source link for every entry they publish.
- **Tour operators and DMOs (destination marketing organizations)** who want to see, at a
  glance, which attractions in a market are rated highest and how many reviews back that
  rating up, before deciding where to build a partnership or a new package.
- **Hospitality analysts and revenue managers** tracking how a set of competitor hotels in one
  city compares on rating, review volume and the "from" price Tripadvisor is currently showing,
  refreshed on a schedule instead of checked by hand.
- **Lead-generation and market-research teams** building destination datasets — top-rated
  restaurants by city for a franchise scouting report, or top attractions by region for a
  travel-media pitch deck — where the deliverable is a spreadsheet, not a subscription.
- **Developers building travel apps or chatbots** who need a lightweight, pay-per-call source
  of "what's good here" data without negotiating a Tripadvisor Content API partnership.
- **No-code and low-code builders** wiring a Make.com, Zapier or n8n scenario where a user
  types a destination and the flow needs a ranked list of things to do back within the same
  automation — this Actor's synchronous run-and-fetch endpoint (see Integration recipes) drops
  straight into that kind of scenario without any custom code.

### Numbers, for planning

A single attractions page returns roughly 30 rows; a place-and-category pull that needs more
than that simply reads more pages, in order, until it has enough. As a planning reference from
real cloud runs of this Actor: a 10-row pull for one place and one category
finishes in well under a minute, and a 120-row pull across four pages for one place and one
category — enough for a genuinely deep "Things to Do" export — finishes in under a minute as
well, since each page load is the only slow step and pages load one after another inside the
same run. Multiply by the number of places and categories in your `searchQueries` ×
`categories` combination to estimate a larger job's wall-clock time.

### How to run it

1. Click **Try for free** — no card needed on the free plan.
2. Paste one or more place names into **Place names**, e.g. `Paris`, `Kyoto`, `New York City`.
   Each name is resolved through Tripadvisor's own place search, so ordinary spellings work —
   you don't need the exact "Paris\_Ile\_de\_France" slug Tripadvisor uses internally.
3. Pick **Categories** (attractions, restaurants, hotels — any combination) and set **Max items
   per category** if you want more or fewer than the default.
4. Hit **Start**. Results land in the dataset as soon as each page is read — pull them from the
   UI, the API, or a webhook once the run finishes.

### Pricing

Pay-per-event, no monthly seat: **$0.02 per run start + $0.003 per result**
(one result = one attraction, restaurant or hotel listing delivered to the dataset).
**Example:** with the default 2 GB memory, a run that delivers 100 listings costs 2 × $0.02 + 100 × $0.003 = **$0.34**; 1,000 listings cost **$3.04**. The same price applies on every Apify plan.

You are not charged for rows that carry no data. A place name Tripadvisor's search can't
resolve, a category with no listings for that place, and a request the site blocked even after
retrying with a fresh residential proxy session all come back as a single free row with an
`error` field explaining what happened — the run still finishes as `SUCCEEDED` so your
pipeline doesn't have to treat it as a crash. If you set a **Max total charge** on the run and
the run reaches it mid-page, delivery stops before the next row would be charged and a final
free row tells you how many results you got and that raising the limit would get you more.

### Input

| Field | Required | What it does |
|---|---|---|
| `searchQueries` | yes | Place names to look up, one per line — up to 50 per run. Each is resolved through Tripadvisor's own place search (`"Paris"`, `"Kyoto"`, `"New York City"` all work; the more specific the name, the more certain the match). |
| `categories` | no | Which listing types to collect for every place: any combination of `attractions`, `restaurants`, `hotels`. Default `["attractions", "restaurants"]`. |
| `maxItemsPerCategory` | no | Maximum listings to collect per place **and** category. Tripadvisor shows about 30 per page, so this also controls how many pages are fetched (e.g. 90 fetches 3 pages). Default 30, maximum 300. |

This is the exact input the Actor's own default test run uses — press **Try for free** with it
untouched and you'll get real Paris attractions back in well under a minute:

```json
{
    "searchQueries": ["Paris"],
    "categories": ["attractions"],
    "maxItemsPerCategory": 10
}
```

Another valid input — restaurants and hotels for one place, a slightly larger pull:

```json
{
    "searchQueries": ["Paris"],
    "categories": ["restaurants", "hotels"],
    "maxItemsPerCategory": 6
}
```

A multi-place, multi-category input for a market-comparison run:

```json
{
    "searchQueries": ["Paris", "Rome", "Kyoto"],
    "categories": ["attractions", "hotels"],
    "maxItemsPerCategory": 30
}
```

### Output

One JSON object per attraction, restaurant or hotel in the default dataset. These three rows
are unedited output from real cloud runs of this Actor — an attraction, a restaurant and a
hotel for Paris:

```json
{
    "name": "Eiffel Tower",
    "category": "attractions",
    "rank": 1,
    "rating": 4.6,
    "reviewCount": 144089,
    "priceLevel": null,
    "description": "Completed in 1889, this colossal landmark, although initially hated by many Parisians, is now a famous symbol of French civic pride.",
    "imageUrl": "https://dynamic-media-cdn.tripadvisor.com/media/photo-o/1a/9e/7f/9d/eiffeltoren.jpg?w=500&h=400&s=1",
    "url": "https://www.tripadvisor.com/Attraction_Review-g187147-d188151-Reviews-Eiffel_Tower-Paris_Ile_de_France.html",
    "place": "Paris, Ile-de-France, France",
    "query": "Paris",
    "page": 1,
    "position": 1,
    "scrapedAt": "2026-09-25T16:21:19.158Z"
}
```

```json
{
    "name": "Louisette",
    "category": "restaurants",
    "rank": null,
    "rating": 4.7,
    "reviewCount": 1061,
    "priceLevel": "$$ - $$$",
    "description": null,
    "imageUrl": "https://dynamic-media-cdn.tripadvisor.com/media/photo-o/2c/0b/59/41/banquette-velours-et.jpg?w=400&h=400&s=1",
    "url": "https://www.tripadvisor.com/Restaurant_Review-g187147-d27142285-Reviews-Louisette-Paris_Ile_de_France.html",
    "place": "Paris, Ile-de-France, France",
    "query": "Paris",
    "page": 1,
    "position": 1,
    "scrapedAt": "2026-09-25T16:25:52.596Z"
}
```

```json
{
    "name": "Hôtel Astra Opéra - Astotel",
    "category": "hotels",
    "rank": 1,
    "rating": 5,
    "reviewCount": 1002,
    "priceLevel": "$522",
    "description": null,
    "imageUrl": "https://dynamic-media-cdn.tripadvisor.com/media/photo-o/2d/73/c8/9e/privilege-room-eiffel.jpg?w=400&h=-1&s=1",
    "url": "https://www.tripadvisor.com/Hotel_Review-g187147-d229968-Reviews-Hotel_Astra_Opera_Astotel-Paris_Ile_de_France.html",
    "place": "Paris, Ile-de-France, France",
    "query": "Paris",
    "page": 1,
    "position": 2,
    "scrapedAt": "2026-09-25T16:25:59.112Z"
}
```

### Field dictionary

| Field | Meaning |
|---|---|
| `name` | The listing's name, exactly as Tripadvisor displays it, with any "N." ranking prefix already stripped out into `rank`. |
| `category` | `attractions`, `restaurants` or `hotels` — which of your requested categories this row came from. |
| `rank` | Tripadvisor's own position number for this place and category, when the page shows one. Attractions almost always carry it (Tripadvisor's "Things to Do" list is fully numbered); restaurants and the first handful of featured hotels on a page often don't — `null` there is the source's own layout, not a missing value. |
| `rating` | Tripadvisor's bubble score, 0–5, as a number (e.g. `4.6`). `null` if the listing has no rating yet. |
| `reviewCount` | Number of Tripadvisor reviews behind that rating, as a number. `0` is a real value (a rated-but-unreviewed edge case); `null` means no review count was shown at all. |
| `priceLevel` | Price where Tripadvisor shows one: a nightly rate for hotels (e.g. `"$272"`) or a price band for restaurants (e.g. `"$$ - $$$"`, `"$$$$"`). Almost always `null` for attractions — Tripadvisor's "Things to Do" grid is not priced. |
| `description` | Tripadvisor's own short blurb for the listing, when the card carries one. Populated on most attractions, usually `null` for restaurants and hotels, whose cards don't carry this text. |
| `imageUrl` | Direct URL of the listing's primary photo, straight from Tripadvisor's CDN. |
| `url` | Canonical `tripadvisor.com/..._Review-...` page for the listing — click through to verify anything in the row. |
| `place` | The resolved place name Tripadvisor returned for your query, e.g. `"Paris, Ile-de-France, France"` — useful for confirming the Actor matched the place you meant. |
| `query` | Your original input string for this row, e.g. `"Paris"`. |
| `page` / `position` | Which listing page (1-indexed, ~30 results per page) and position on that page this row was read from — reproduces the order Tripadvisor showed it in. |
| `scrapedAt` | ISO-8601 timestamp of when this row was collected. |
| `error` | Present only on free, unbilled rows — explains what went wrong (unresolved place, blocked request, or a category with no listings for that place). All other fields are `null` on an error row. |

### Output examples: happy, partial, failure

Only rows with real data are billed; rows that carry an `error` field instead are free. All
three examples below are unedited rows from real cloud runs — nothing here is invented.

**Happy** — a fully populated attraction row, description and photo included:

```json
{"name": "Musée d'Orsay", "category": "attractions", "rank": 3, "rating": 4.7, "reviewCount": 67977, "priceLevel": null, "description": "Musée d'Orsay in Paris is a must-visit for art lovers and travelers alike. It's famous for its impressive collection of Impressionist and post-Impressionist art, making it one of the largest in the…", "imageUrl": "https://dynamic-media-cdn.tripadvisor.com/media/photo-o/13/26/b3/6e/20180601-122841-largejpg.jpg?w=500&h=-1&s=1", "url": "https://www.tripadvisor.com/Attraction_Review-g187147-d188150-Reviews-Musee_d_Orsay-Paris_Ile_de_France.html", "place": "Paris, Ile-de-France, France", "query": "Paris", "page": 1, "position": 3, "scrapedAt": "2026-09-25T16:21:19.391Z"}
```

**Partial** — real fields Tripadvisor's own restaurant cards simply don't carry (`rank` and
`description`) come back explicitly `null` instead of being guessed or omitted:

```json
{"name": "Gusto Italia 218", "category": "restaurants", "rank": null, "rating": 4.2, "reviewCount": 260, "priceLevel": "$$ - $$$", "description": null, "imageUrl": "https://dynamic-media-cdn.tripadvisor.com/media/photo-o/17/dc/1c/fe/pizza-parma.jpg?w=400&h=400&s=1", "url": "https://www.tripadvisor.com/Restaurant_Review-g187147-d8510311-Reviews-Gusto_Italia_218-Paris_Ile_de_France.html", "place": "Paris, Ile-de-France, France", "query": "Paris", "page": 1, "position": 2, "scrapedAt": "2026-09-25T16:25:52.847Z"}
```

**Failure** — never billed. This is the actual row returned for a place name Tripadvisor's own
search could not resolve:

```json
{"name": null, "category": "attractions", "rank": null, "rating": null, "reviewCount": null, "priceLevel": null, "description": null, "imageUrl": null, "url": null, "place": null, "query": "Zzxqvblorpnonexistentplace123", "page": null, "position": null, "error": "Zzxqvblorpnonexistentplace123 — Attractions: Place not found on Tripadvisor: \"Zzxqvblorpnonexistentplace123\". Try a more specific or differently spelled name.", "scrapedAt": "2026-09-25T16:23:44.479Z"}
```

### Evidence and boundaries

- **Source:** public pages on `tripadvisor.com` only — the place-search lookup and the
  attractions / restaurants / hotels listing pages for the resolved place. No login, no
  Tripadvisor account, no API key.
- **No personal data.** The Actor reads listing-level facts (name, rating, review count,
  price, photo) — it does not collect reviewer names, review text, or any other
  user-submitted content.
- **Anti-bot reality, stated plainly.** Tripadvisor blocks plain HTTP requests and
  TLS-fingerprint-spoofing clients outright, even from residential IP ranges — this is not a
  quirk of one proxy vendor, it held across every plain-HTTP approach tested. What gets through
  is a real anti-detect browser (Camoufox, an anti-fingerprinting build of Firefox) running
  behind an Apify **RESIDENTIAL** proxy exit in the United States — the exit country this
  Actor uses for every place it looks up worldwide, because that combination is what was
  verified to reach `tripadvisor.com`'s real pages instead of its anti-bot challenge. Running
  this Actor anywhere else (your own datacenter server, a bare `fetch`) will not reproduce
  these results — it needs the Apify platform's residential proxy pool, which is enabled by
  default when you run it here.
- **Retries are automatic and free.** A single blocked or slow-loading page opens a fresh
  proxy session and tries again (up to three attempts) before it is reported as a failure —
  you never pay for the retry itself, only for the row it eventually delivers or the single
  free error row if all attempts fail.
- **Field completeness reflects the source, not a scraping gap.** Tripadvisor's own card
  layouts differ by category: the attractions grid is fully ranked and richly described but
  never priced; restaurant and hotel cards are priced and rated but rarely ranked or
  described. Every `null` in the output above is what Tripadvisor itself did not show on that
  card — the Actor does not infer, average or backfill a value Tripadvisor didn't display.
- This README's evidence was gathered from the Actor's own accepted cloud runs on the Apify
  platform — the exact rows quoted above, not local or mocked test fixtures.

### Data quality notes

- **Ranking order was cross-checked against Tripadvisor's own structured data**, not just the
  visible card layout. The same "Paris" attractions run that produced the Eiffel Tower / Louvre
  Museum / Musée d'Orsay rows above matches, position for position, the ranked list Tripadvisor
  embeds in its own page metadata for search engines — two independent parts of the same page
  agreeing is a stronger signal than either alone.
- **Review counts and ratings are read as text and parsed as numbers on every row**, not
  estimated from a visual star width or a CSS class name — `"144,089"` becomes `144089`,
  `"4.6 of 5 bubbles"` becomes `4.6`. A listing with a rating but literally zero reviews
  returns `reviewCount: 0`, which is deliberately different from `null` (no review count shown
  at all).
- **Photo URLs point at Tripadvisor's own CDN** (`dynamic-media-cdn.tripadvisor.com`) and are
  the same image a visitor sees on the card — this Actor does not download, re-host, or
  otherwise take possession of the image file; you receive a URL to Tripadvisor's own asset.
- **Every row is deduplicated within its own place-and-category pull** by its canonical
  Tripadvisor URL, so a listing that happens to appear in more than one place on a page (a
  "Travelers' Choice" badge slot and the main grid both featuring the same attraction, for
  instance) is only delivered — and billed — once.

### Decision routing

- **Want Tripadvisor's own ranking and review volume for a place's top things to do,
  restaurants or hotels?** This is the right tool — it reads exactly what Tripadvisor shows a
  visitor, structured for a spreadsheet or a pipeline.
- **Need live, bookable prices and availability for tours or activities**, not just a rating
  and a ranking? Look at a dedicated tour-inventory source instead — this Actor deliberately
  reads Tripadvisor's ranked "Things to Do" grid, not its Viator-powered bookable-tour shelf,
  because the grid is what carries the ranking and description data most buyers of this Actor
  actually want.
- **Need nightly hotel rates across many dates, not a single "from" price snapshot?** Pair
  this Actor's rating/review/ranking data with a dedicated rate-shopping source for the
  date-by-date pricing side — this Actor's `priceLevel` on hotels is the single "from" figure
  Tripadvisor shows on the listing card at scrape time, not a calendar of rates.
- **Need the review text itself, not just the rating and count?** Out of scope by design —
  this Actor is a listing-level tool. Review text carries far more personal and moderation
  risk than aggregate ratings, and most buyers of destination data want the ranked list, not
  a review-mining pipeline.

### Commercial playbooks

- **"Best of" content at scale.** A travel-content team covering 40 destinations runs this
  Actor with `categories: ["attractions"]` and `maxItemsPerCategory: 20` once a month per
  city, and rebuilds each "Top 20 things to do in X" page from Tripadvisor's own current
  ranking, rating and photo — instead of a writer copying numbers off the live site by hand.
- **Competitive hotel tracking.** A revenue manager tracks 15 competitor hotels' rating,
  review count and headline nightly rate weekly by running `categories: ["hotels"]` for their
  market and matching results back to a known property list by name and `url` — catching a
  rating slide or a price move the same week it happens, not at the next quarterly review.
- **Restaurant scouting for a franchise or delivery launch.** A market-research team compares
  restaurant density, price bands and review volume across 10 candidate cities in one run
  (`searchQueries` with all 10 place names, `categories: ["restaurants"]`), turning a week of
  manual browsing into a single spreadsheet.
- **Destination-marketing benchmarking.** A DMO (destination marketing organization) runs
  attractions and hotels for its own city alongside two or three competing destinations on a
  schedule, tracking review-count growth and rating trends as a proxy for visitor sentiment
  over time — each scheduled run appends a fresh, timestamped snapshot to the same dataset.
- **Enrichment step in a larger pipeline.** A travel-app backend calls this Actor via the API
  whenever a user opens a new city for the first time, caches the attractions/restaurants/hotels
  rows, and refreshes them on a schedule — paying only for the destinations users actually ask
  about instead of pre-scraping the whole world.

### Integration recipes

Run it from code with the Apify API (Python):

```python
from apify_client import ApifyClient

client = ApifyClient("YOUR_API_TOKEN")
run = client.actor("zinin/tripadvisor-scraper").call(run_input={
    "searchQueries": ["Paris", "Rome"],
    "categories": ["attractions", "restaurants"],
    "maxItemsPerCategory": 30,
})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item["place"], item["category"], item["name"], item["rating"], item["reviewCount"])
```

Or from Node.js:

```javascript
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: 'YOUR_API_TOKEN' });
const run = await client.actor('zinin/tripadvisor-scraper').call({
    searchQueries: ['Kyoto'],
    categories: ['hotels'],
    maxItemsPerCategory: 25,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items.length, 'hotels for', items[0]?.place);
```

Or trigger a run directly over HTTP — useful from a webhook, Zapier, Make, or n8n:

```bash
curl -X POST "https://api.apify.com/v2/acts/zinin~tripadvisor-scraper/run-sync-get-dataset-items?token=YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"searchQueries": ["New York City"], "categories": ["attractions"], "maxItemsPerCategory": 20}'
```

`run-sync-get-dataset-items` blocks until the run finishes and returns the dataset rows
directly in the response — convenient for small pulls from a script or a low-code automation
tool without a separate polling step. For larger pulls, start the run with the regular `/runs`
endpoint, poll for `SUCCEEDED`, then read `defaultDatasetId` — the same pattern the SDK
snippets above use internally. For scheduled destination refreshes, attach a **Schedule** to
this Actor in the Apify console instead of triggering it from your own cron.

### Operating guide

- **How place resolution works.** Every place name is looked up once, through the same public
  place-search endpoint Tripadvisor's own search box calls, and the first location match is
  used to build the listing URLs for every category you requested for that place. If you run
  the same place name across several categories in one job, the lookup happens once and the
  result is reused — you are not charged extra for it either way, since only delivered result
  rows are billed.
- **Pagination.** Tripadvisor shows roughly 30 listings per page. `maxItemsPerCategory` is
  translated into "however many pages of ~30 it takes" — asking for 90 fetches 3 pages, asking
  for 10 still fetches one full page and returns only the first 10. A category genuinely
  running out of listings before your limit (a very small town's hotel list, for instance)
  ends early rather than retrying empty pages.
- **Rank is category-specific, not padded.** Attractions carry Tripadvisor's real "Things to
  Do" ranking almost every time, because that page is a single numbered list. Restaurants and
  the first handful of hotels on a page come from Tripadvisor's featured/"Best of" card
  layouts, which are not numbered on the source page itself — `rank: null` there is accurate,
  not a gap in extraction. If you need a stable sort for those categories, sort the rows you
  get back by `rating` and `reviewCount` yourself.
- **Ambiguous place names resolve to Tripadvisor's own top match.** `"Paris"` resolves to
  Paris, France (not Paris, Texas) because that is what Tripadvisor's own search ranks first
  for that query. The resolved name is always returned in the `place` field so you can confirm
  the match — for a genuinely ambiguous name, add disambiguating words to your query (e.g.
  `"Paris, Texas"` instead of `"Paris"`).
- **Country/region-only place names work too**, and return that broader area's top attractions,
  restaurants or hotels rather than a specific city's — useful for a country-level overview
  pull before drilling into individual cities.
- **Every page load runs through a real, anti-detect browser behind a United States
  residential proxy exit**, whatever place is being looked up — this is why the Actor requires
  the Apify platform to run (see Evidence and boundaries above) and why very large pulls
  (hundreds of results across many places) take real wall-clock time rather than the seconds a
  raw HTTP scraper would promise and then fail to deliver on.
- **Typeahead resolution is cached per place, per run.** If the same place name is queried in
  more than one category in the same run (e.g. `attractions` and `hotels` for `"Paris"`), the
  place-search lookup happens once and the result is reused for every category — this saves
  wall-clock time but has no effect on price, since only delivered result rows are billed.
- **One dataset row per listing, written as soon as it's read.** You don't have to wait for
  the whole run to finish to start consuming results — poll the dataset mid-run, or use a
  webhook on the `ACTOR.RUN.SUCCEEDED` event if you only care about the final, complete set.

### Other tools we built

Need the rest of the picture beyond one destination's Tripadvisor listings?

### More travel scrapers from the same author

| Actor | What it gives you |
|---|---|
| [Expedia Hotel Scraper](https://apify.com/zinin/expedia-scraper) | Hotel prices per night and guest ratings |
| [Viator Tours Scraper](https://apify.com/zinin/viator-scraper) | Tours with from-prices, ratings and durations |
| [GetYourGuide Scraper](https://apify.com/zinin/getyourguide-scraper) | Activities with prices, ratings and durations |
| [Airbnb Listings & Prices Scraper](https://apify.com/zinin/airbnb-listings-prices-scraper) | Airbnb listings with nightly prices |

### FAQ / Limitations

**How many listings can one place and category return?** Up to `maxItemsPerCategory` (default
30, maximum 300), paginated roughly 30 per page in the order Tripadvisor itself shows them.

**Does this need my own Tripadvisor account or API key?** No. It reads Tripadvisor's own
public pages the way a visitor's browser would; no login or API credentials are used or
required.

**Why did I get fewer rows than I asked for?** Either the place genuinely has fewer listings
in that category than your `maxItemsPerCategory`, or your run's spending limit was reached
mid-page — both cases return a free row explaining which, so your pipeline can tell the
difference from a crash.

**Can I pass an exact Tripadvisor URL instead of a place name?** Not in this version — every
run resolves place names through Tripadvisor's own search. If you have a specific geo ID or
URL slug already and need the Actor to accept it directly, open an issue on the Actor page
describing your use case.

**What does `rank: null` mean — did the scrape fail?** No. It means Tripadvisor's own card for
that listing doesn't carry a visible rank number on the page this Actor read — true for most
restaurant and some hotel cards by design of Tripadvisor's own layout (see Operating guide).
It is not an extraction failure and the row is billed normally if it has a name, rating or
review count.

**Why is `priceLevel` `null` for almost every attraction?** Tripadvisor's own "Things to Do"
ranked grid does not show a price on the card — pricing on that page only appears for a
separate row of bookable-tour products, which this Actor does not read (see Decision
routing). Restaurants and hotels are priced by Tripadvisor and come through populated.

**Will re-running the same place name give me the exact same rows?** Very likely for the top
of the list — Tripadvisor's top-ranked attractions in a well-covered city change rarely — but
review counts, ratings and hotel "from" prices update continuously on the source, so exact
values can differ run to run. Treat each run as a fresh snapshot, not a cached copy.

**What happens if I request an obscure village with no restaurants listed on Tripadvisor?**
The place itself still resolves (if Tripadvisor's search finds it at all), but the restaurants
category for it returns a single free row noting no listings were found — you are not charged
for an empty category.

**Can I run attractions, restaurants and hotels for ten cities in one call?** Yes — put all
ten place names in `searchQueries` and all three categories in `categories`; the Actor works
through every place-and-category combination in the same run and writes rows to one dataset as
it goes, so you don't need to script ten separate calls.

**Does `maxItemsPerCategory` apply per place or across all of them?** Per place and category.
`maxItemsPerCategory: 30` with 3 places and 2 categories can deliver up to 30 × 3 × 2 = 180
rows in one run, not 30 total.

**Why does the same hotel sometimes show a different `priceLevel` on two different days?**
Tripadvisor's "from" price for a hotel reflects current availability and demand at the moment
the page was read, the same way it would for a visitor loading the page live — it is a
snapshot, not a fixed list price. Run the Actor on a schedule if you need a price history.

**Is the data suitable for automated decisions (pricing, booking) without a human check?**
Treat it as research and monitoring input, not a booking-grade feed — prices in particular are
a snapshot of what Tripadvisor displayed at scrape time and can change before a guest reaches
checkout on the operator's own site.

**What this is NOT.** This Actor reads public Tripadvisor listing pages only. It does not log
in, does not read or return review text (ratings and counts only), does not bypass CAPTCHAs
or paywalls, does not book anything, and does not guarantee real-time hotel availability or
that a price shown at scrape time will still be offered when a guest tries to book it.

**Legal note.** Data is collected from publicly available Tripadvisor pages. You are
responsible for complying with Tripadvisor's own terms of service and any law applicable to
your use case, including how you store, use and redistribute the results.

Found a bug, or need this adapted to a workflow it doesn't cover yet? Open an issue on the
Actor page.

### Sources & rights

Data is read from public pages on `tripadvisor.com` only: the site's own place-search lookup
and its attractions, restaurants and hotels listing pages for the resolved place. Ratings,
review counts, ranking, price and photo URLs only — review text and any other user-submitted
content is not collected. No login, no API credentials and no personal data are accessed at
any point in the run. You are responsible for complying with Tripadvisor's terms of service
and any law applicable to your jurisdiction and use case, including redistribution of the
collected data.

# Actor input Schema

## `searchQueries` (type: `array`):

Places to look up on Tripadvisor by name, e.g. "Paris", "Kyoto" or "New York City". Each name is resolved through Tripadvisor's own place search, same as typing it into the site's search box.

## `categories` (type: `array`):

What to collect for each place.

## `maxItemsPerCategory` (type: `integer`):

Maximum listings to collect per place and category (attractions, restaurants or hotels). Tripadvisor shows about 30 per page, so higher values fetch more pages.

## Actor input object example

```json
{
  "searchQueries": [
    "Paris"
  ],
  "categories": [
    "attractions"
  ],
  "maxItemsPerCategory": 20
}
```

# Actor output Schema

## `results` (type: `string`):

API URL for the default dataset items produced by this run.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "searchQueries": [
        "Paris"
    ],
    "categories": [
        "attractions"
    ],
    "maxItemsPerCategory": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("zinin/tripadvisor-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "searchQueries": ["Paris"],
    "categories": ["attractions"],
    "maxItemsPerCategory": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("zinin/tripadvisor-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "searchQueries": [
    "Paris"
  ],
  "categories": [
    "attractions"
  ],
  "maxItemsPerCategory": 20
}' |
apify call zinin/tripadvisor-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,zinin/tripadvisor-scraper"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/eOLJmSRORVOtY85aI/builds/o6V8tPgX3b8XxKn6P/openapi.json
