# TripAdvisor Scraper — Hotels, Restaurants & Attractions (`thenetaji/tripadvisor-listings-scraper`) Actor

Export a city's TripAdvisor hotel, restaurant or attraction listings in TripAdvisor's own ranked order, with rating, review count, price band, contact and location on every row. List pages only — there is no detail or reviews endpoint behind this Actor.

- **URL**: https://apify.com/thenetaji/tripadvisor-listings-scraper.md
- **Developed by:** [The Netaji](https://apify.com/thenetaji) (community)
- **Categories:** Travel, Business, Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.02 / 1,000 listings

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## TripAdvisor Listings Scraper — Hotels, Restaurants & Attractions

The Actor reads one of a city's three TripAdvisor list pages — Hotels, Restaurants, or Attractions — and returns every row as a structured record, in TripAdvisor's own ranked order.

This Actor covers list pages only. It does not offer a detail lookup or a reviews export, for either kind, and none is planned: TripAdvisor's own review and detail pages refuse automated access at the source, independent of this Actor, so no service can reliably offer them today. What list pages publish is what this Actor returns — rank, rating, review count, price band, phone, address, and coordinates — not full review text.

### Accepted input

| Field | Type | Required | Description |
|---|---|---|---|
| `startUrl` | string | no | A TripAdvisor Hotels, Restaurants or Attractions listing URL. When it can be read, `geo_id`, `location_slug` and `kind` below are filled in from it; any of the three set directly is kept as given. |
| `geo_id` | integer | yes, unless `startUrl` supplies it | TripAdvisor's id for the city or region — the `g` number in its URL, e.g. `60713` for `/Hotels-g60713-San_Francisco_California-Hotels.html`. |
| `location_slug` | string | yes, unless `startUrl` supplies it | The underscored place name from the same URL, e.g. `San_Francisco_California`. There is no lookup that turns a geo id into a slug, so both are needed. |
| `kind` | enum | no, default `hotels` | `hotels`, `restaurants`, or `attractions`. |
| `offset` | integer | no, default `0` | Where to start reading. TripAdvisor pages in steps of 30 — page two is `30`, page three is `60` — up to `2970`. A value that is not a multiple of 30 is rejected rather than rounded. |
| `maxItems` | integer | no, default `30` | Maximum rows to save across pages. `0` removes the limit. |

```json
{
  "geo_id": 60713,
  "location_slug": "San_Francisco_California",
  "kind": "hotels",
  "maxItems": 90
}
```

Pasting a URL works the same way:

```json
{
  "startUrl": "https://www.tripadvisor.com/Hotels-g60713-San_Francisco_California-Hotels.html",
  "maxItems": 90
}
```

### Response fields

| Field | Contents |
|---|---|
| `position` | Row's rank on its page. Restarts at 1 on every page. |
| `schema_type` | `Hotel`, `Restaurant`, or `TouristAttraction` — the honest predictor of how populated the rest of the row is. |
| `name` | Listing's name. |
| `url` | TripAdvisor's own link for the row. |
| `geo_id` | The row's own `g` id, as a string. Usually the geo asked for, but not always — a listing can carry a neighbouring city's geo. |
| `location_id` | The row's `d` id, TripAdvisor's id for the property. `null` when the row's URL did not carry one. |
| `rating` | Average rating out of `rating_scale_max`. `null` on attractions. |
| `rating_scale_max` | Top of the rating scale as TripAdvisor states it, typically `5`. Not assumed, because not every rating site uses the same scale. |
| `review_count` | Exact review count. `null` on attractions. |
| `price_range` | Price band as TripAdvisor renders it, e.g. `$$$`. |
| `telephone` | Phone number, when published. |
| `image` | Lead photo, when the row carries one. |
| `address` | Structured street address. `null` for every attraction. |
| `geo` | Latitude and longitude. The one substantial field attractions do carry. |
| `kind` | Which of the three lists the row came from. |
| `coverage` | States why a row is thin, when it is. Populated for `kind=attractions`; `null` for hotels and restaurants. |

```json
{
  "position": 1,
  "schema_type": "Hotel",
  "name": "InterContinental Mark Hopkins",
  "rating": 4.5,
  "rating_scale_max": 5,
  "review_count": 3309,
  "price_range": "$$$",
  "kind": "hotels",
  "coverage": null
}
```

### Attractions carry less than hotels and restaurants

TripAdvisor publishes attraction rows as `TouristAttraction`, which states a name and coordinates and nothing else — no rating, no review count, no address. This is not a parsing gap; the site does not put more on the page for that kind. `coverage` states the reason directly on a `kind=attractions` response, so a page of `null` values is identifiable as expected rather than as a broken run.

### Behaviour on paging and limits

A page carries exactly 30 rows. `next_offset` on the response is where to continue; it is `null` once a page comes back short, which is the reliable end-of-list signal — TripAdvisor publishes no total count for a city, so a full page is a suggestion that more rows exist, not a guarantee. A run interrupted partway resumes at the exact row it stopped on rather than re-saving what already reached the dataset.

`offset` tops out at `2970`, so a single `kind` for a single city returns at most 3,000 rows. A city with fewer listings than that ends earlier, when TripAdvisor's own page does.

### Frequently asked questions

**Why do I get zero rows instead of an error for a valid geo id?**
A city can have no attractions worth listing, or fewer restaurants than the requested `offset` reaches. An empty `results` array with `next_offset: null` is TripAdvisor's own end of that list, not a failure of the request.

**Why did `startUrl` not fill in `location_slug`?**
The URL parser recognises a Hotels, Restaurants, or Attractions listing URL and reads `geo_id`, `location_slug`, and `kind` from its segments. A URL outside that shape — a detail page, a search results page, or a link with an unusual location name — is left unparsed, and `geo_id` and `location_slug` need to be set directly.

**Can I get reviews or the full detail page for one listing?**
No. TripAdvisor's review and detail pages are not reachable through this Actor and none is planned — see the note at the top. Rating, review count, price band, phone, address, and coordinates come from the list page itself, and that is the complete set this Actor can offer.

**Why is `rating` sometimes `null` on a hotel or restaurant row?**
TripAdvisor itself has not published a rating for that listing — a new or lightly reviewed property, most often. It is returned as `null` rather than a guessed value.

**How do I read every hotel in a large city?**
Set `maxItems` to `0` and leave `offset` at its default. The run walks forward in steps of 30 until TripAdvisor returns a page that is not full, or until the 3,000-row ceiling is reached.

# Actor input Schema

## `startUrl` (type: `string`):

A TripAdvisor Hotels, Restaurants or Attractions listing URL, e.g. one copied from your browser's address bar. Read locally, not sent anywhere.

## `geo_id` (type: `integer`):

TripAdvisor's id for the city or region — the g number in its URL, e.g. 60713 for /Hotels-g60713-San\_Francisco\_California-Hotels.html.

## `location_slug` (type: `string`):

The underscored place name from the same URL, e.g. San\_Francisco\_California. Required alongside Geo ID — there is no lookup that turns a geo id into a slug.

## `kind` (type: `string`):

Which of the city's three list pages to read. Hotels and Restaurants rows come back fully populated; Attractions rows carry only a name and coordinates — see the Coverage field.

## `offset` (type: `integer`):

TripAdvisor's own paging position. Moves in steps of 30 — page two is 30, page three is 60 — and must be a multiple of 30, up to 2970.

## `maxItems` (type: `integer`):

Maximum rows to save across pages. Set 0 for no limit.

## Actor input object example

```json
{
  "startUrl": "https://www.tripadvisor.com/Hotels-g60713-San_Francisco_California-Hotels.html",
  "geo_id": 60713,
  "location_slug": "San_Francisco_California",
  "maxItems": 30
}
```

# Actor output Schema

## `dataset` (type: `string`):

All records scraped 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 = {
    "startUrl": "https://www.tripadvisor.com/Hotels-g60713-San_Francisco_California-Hotels.html",
    "maxItems": 30
};

// Run the Actor and wait for it to finish
const run = await client.actor("thenetaji/tripadvisor-listings-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 = {
    "startUrl": "https://www.tripadvisor.com/Hotels-g60713-San_Francisco_California-Hotels.html",
    "maxItems": 30,
}

# Run the Actor and wait for it to finish
run = client.actor("thenetaji/tripadvisor-listings-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 '{
  "startUrl": "https://www.tripadvisor.com/Hotels-g60713-San_Francisco_California-Hotels.html",
  "maxItems": 30
}' |
apify call thenetaji/tripadvisor-listings-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,thenetaji/tripadvisor-listings-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/0rPD1JEic0fHEKEXe/builds/RaANkYrGgFuZrSsjA/openapi.json
