# Viator Scraper - Tours, Prices, Ratings & Duration (`scrapewise/viator-scraper`) Actor

Scrape Viator tours and activities by destination, keyword or link: price and discount, rating, review count, duration, free cancellation, badges, languages, photos and, on demand, supplier, inclusions, meeting point and itinerary stops. Error rows are free.

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

## Pricing

from $2.47 / 1,000 activity delivereds

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?

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

## Viator Scraper: tours and activities with prices, ratings, reviews and duration

Viator Scraper collects **tours, tickets and activities from Viator** for any destination or keyword, without an
account, cookies or a browser. Each activity comes back as one clean row: the title and link, the "from" price and
the price before a discount, whether it is priced per person or per group, the exact rating and review count, the
duration, free cancellation, badges such as Likely to Sell Out, the guide languages, a short and a long description
and the main photo. Turn on `includeDetails` and each row also gets the supplier company, highlights, what is
included and excluded, additional info, meeting points with coordinates, the itinerary stops, age bands, the
maximum group size and the tour options.

It is used to monitor prices and ratings of tours in a destination, to benchmark a tour operator against its
competitors, to find the best rated activities for a travel guide or app, to research what sells in a city before
launching a product, and to feed AI travel assistants with fresh activity data.

### At a glance

| What | Price |
|---|---|
| Activity delivered | **US$ 2.90 per 1,000 activities** (US$ 0.0029 each) |
| Run start | US$ 0.005 per run |

- 500 activities of one city = **US$ 1.46**.
- The tour page details (`includeDetails`) cost nothing extra per activity.
- Error rows and duplicates are never charged. `maxItems` is a hard cap, and a spending limit set on the run stops it
  cleanly with everything delivered so far.

### Quick start

```json
{
  "searchQueries": ["Lisbon"],
  "maxItems": 100
}
```

A destination name lists every activity Viator has there (Lisbon has more than 3,000). A keyword gives Viator's
search results across the world (up to 200):

```json
{
  "searchQueries": ["kayak tour", "food tour Rome"],
  "minRating": 4.5,
  "minReviews": 50,
  "includeDetails": true,
  "maxItems": 60
}
```

Links work too:

```json
{
  "startUrls": [
    { "url": "https://www.viator.com/Rome/d511-ttd" },
    { "url": "https://www.viator.com/searchResults/all?text=sunset%20cruise" },
    { "url": "https://www.viator.com/tours/Paris/Golden-Versailles-Palace-and-Garden-Tour/d479-67584P2" }
  ]
}
```

An empty input runs the example destination (Lisbon) so you can see the output right away.

### Input

| Field | What it does |
|---|---|
| `searchQueries` | Destination names or activity keywords, one per line. Each line is one search. |
| `startUrls` | Destination pages (`/Lisbon/d538-ttd`), search pages (`/searchResults/all?text=...`) or tour pages (`/tours/...`). |
| `minRating` | Keep only activities rated at least this, on Viator's 0 to 5 scale (for example 4.5). |
| `minReviews` | Keep only activities with at least this many reviews. |
| `maxPrice` | Keep only activities whose "from" price is at or below this value. |
| `maxDurationHours` | Keep only activities that last at most this many hours. |
| `freeCancellationOnly` | Keep only activities with free cancellation. |
| `includeDetails` | Open each tour page for supplier, inclusions, meeting points, itinerary and more (default off; about 3 times slower). |
| `onlyNew` | Skip activities delivered in earlier runs of the same search, for schedules. Skipped rows are not charged. |
| `maxItems` | Cap for the whole run (default 50; 0 = no cap). |
| `proxyConfiguration` | Apify datacenter proxy by default. US residential is used only if datacenter IPs are refused. |

`searchQuery`, `keyword`, `destination`, `location`, `maxResults` and `urls` are accepted too, so inputs written
for other Viator tools keep working.

### Output

| Field | What it is |
|---|---|
| `productCode`, `title`, `url` | Viator product code, title and tour page |
| `destination`, `destinationId`, `country` | Where it happens (`country` comes on destination searches) |
| `price`, `originalPrice`, `discountPercent`, `currency` | "From" price, price before the discount and the discount |
| `pricingType`, `priceVariesByGroupSize` | `PER_PERSON` or `PER_GROUP`, and whether the price changes with the group size |
| `rating`, `ratingRounded`, `reviewCount` | Exact rating (for example 4.8), the rounded stars Viator shows, and the number of reviews |
| `durationMinutes`, `durationMaxMinutes`, `duration` | Duration in minutes (with the upper end for flexible durations) and as text |
| `freeCancellation`, `likelyToSellOut`, `isNew`, `isPrivateTour`, `badges` | Flags and badges shown on the listing |
| `languages` | Guide languages (codes) |
| `summary`, `description` | Short and long description |
| `image`, `photoCount` | Main photo (largest size) and number of listing photos |
| `position` | Position in Viator's list for the search |
| `supplierName`, `productType`, `timeZone` | The company that runs the activity, its type and local time zone (details) |
| `highlights`, `inclusions`, `exclusions`, `additionalInfo` | What you get, what you do not, and the practical notes (details) |
| `meetingPoints` | Start and end points with name, address, coordinates and instructions (details) |
| `itineraryStops` | Stops with name, time spent, admission and coordinates (details) |
| `ageBands`, `maxTravelersPerBooking`, `attributes`, `options` | Ages accepted, group size, ticket attributes and tour options (details) |
| `isExcellent`, `isHopOnHopOff`, `tripadvisorLocationId` | Extra flags and the matching Tripadvisor id (details) |
| `sourceQuery`, `scrapedAt` | The search or link you sent, and when the row was collected |

One real row with details (some fields shortened):

```json
{
  "type": "activity",
  "productCode": "313166P2",
  "title": "Kayak tour in Capri between caves and beaches. Photos included.",
  "url": "https://www.viator.com/tours/Capri/Capri-Island-Kayak-Tour-between-History-and-Legend/d4223-313166P2",
  "destination": "Capri",
  "price": 256.45,
  "currency": "BRL",
  "pricingType": "PER_PERSON",
  "rating": 4.9,
  "reviewCount": 451,
  "durationMinutes": 90,
  "duration": "1 h 30 min",
  "freeCancellation": true,
  "badges": ["RECOMMENDED_BY_TRAVELLERS"],
  "languages": ["en", "it"],
  "supplierName": "Capri Hydro",
  "productType": "ACTIVITIES",
  "inclusions": ["GoPro Photos", "Kayak, Paddle", "Life Jacket", "Kayak Instructor and tour guide"],
  "exclusions": ["Tips/Gratuities", "Beach towel"],
  "meetingPoints": [
    {
      "kind": "START",
      "name": "Capri Hydro - Watersports & Yacht Services",
      "address": "Via Marina Piccola, 80073 Capri NA, Italy",
      "latitude": 40.5451555,
      "longitude": 14.234998
    }
  ],
  "maxTravelersPerBooking": 30
}
```

This row was collected from a Brazilian IP, so the price is in BRL. Viator prices follow the country of the IP:
from a United States proxy they come in USD.

### Errors you may see

Every error is a row with `errorCode` and is never charged:

| errorCode | Meaning |
|---|---|
| `INVALID_INPUT` | A field has a wrong value (for example `minRating` above 5) |
| `INVALID_URL` | Not a Viator destination, search or tour link |
| `NOT_FOUND` | The destination or tour does not exist or was removed |
| `NO_RESULTS` | Nothing matches the search and filters |
| `BLOCKED` | Viator refused every attempt; run it again in a few minutes |
| `NOT_REACHED` | `maxItems`, the spending limit or the run timeout came before this search or link |

### Integrations, scheduling and AI agents

- **Schedule** the same destinations weekly in [Apify schedules](https://docs.apify.com/platform/schedules) to
  follow prices, ratings and review counts, or turn on `onlyNew` to get only the activities added since the last run.
- **Export** as JSON, CSV, Excel or XML, or read the dataset from the [Apify API](https://docs.apify.com/api/v2).
- **Connect** it to Make, n8n, Zapier or Google Sheets through
  [Apify integrations](https://docs.apify.com/platform/integrations).
- **AI agents:** call it as a tool through the [Apify MCP server](https://docs.apify.com/platform/integrations/mcp)
  in Claude, ChatGPT, Cursor or any MCP client, for example "list the 20 best rated activities under 3 hours in
  Barcelona with free cancellation".

### FAQ

**Do I need a Viator account?** No. Everything comes from public pages, without login or cookies.

**How many activities can I get?** For a destination, all of them: the Actor pages through Viator's full list until
the end or your `maxItems`. For a keyword, Viator returns up to 200 results; to go deeper, search a destination
name or add the city to the keyword.

**Which currency are the prices in?** Viator picks the currency from the country of the visitor's IP (USD for a
United States IP). Every row carries its `currency`, so you never mix them.

**Why is `originalPrice` empty on most rows?** It is filled only when Viator shows a discount on the listing.

**Do you collect reviews or reviewer names?** No. The rating and the review count are included, but review texts and
reviewer profiles are left out on purpose.

**Is it legal?** The Actor reads public listing pages only and returns business information, not personal data. You
are responsible for how you use the data under the laws that apply to you.

**Something broke or a field is missing?** Open an issue on the Actor page; issues are answered within a day.

### Limitations

- Prices are Viator's "from" price for the cheapest option, not a quote for a given date.
- Filters are applied to Viator's list; rows that do not pass are not delivered and not charged.
- Tour pages are large (about 3 MB each), so `includeDetails` makes runs about three times slower.
- `country` is filled on destination searches; keyword searches across many places leave it empty.

### Changelog

- **16 Oct 2026**: first version. Destinations, keywords and links, full pagination of destinations, rating,
  review and duration filters, optional tour page details, `onlyNew` for schedules, free error rows.

# Actor input Schema

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

City or destination names (Lisbon, Bali, New York City) or activity keywords (kayak tour, food tour Rome), one per line. A destination name lists every activity there; a keyword gives Viator's search results (up to 200). Leave empty when you paste links below.

## `startUrls` (type: `array`):

Destination pages (https://www.viator.com/Lisbon/d538-ttd), search result pages (https://www.viator.com/searchResults/all?text=kayak) or tour pages (https://www.viator.com/tours/...).

## `minRating` (type: `number`):

Keep only activities rated at least this (0 to 5, e.g. 4.5).

## `minReviews` (type: `integer`):

Keep only activities with at least this many reviews.

## `maxPrice` (type: `number`):

Keep only activities whose 'from' price is at or below this value, in the currency of the row.

## `maxDurationHours` (type: `number`):

Keep only activities that last at most this many hours (e.g. 4).

## `freeCancellationOnly` (type: `boolean`):

Keep only activities with free cancellation.

## `includeDetails` (type: `boolean`):

Opens each tour page for the supplier, highlights, inclusions, exclusions, additional info, meeting points, itinerary stops, age bands and options. Same price per activity, but about 3 times slower.

## `onlyNew` (type: `boolean`):

Skip activities delivered in earlier runs of the same search (for schedules). They are not charged.

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

Cap for the whole run (0 = no cap).

## `proxyConfiguration` (type: `object`):

Apify datacenter proxy by default. The Actor moves to US residential only if datacenter IPs are refused. Prices come in the currency of the proxy country (USD for US IPs).

## Actor input object example

```json
{
  "searchQueries": [
    "Lisbon"
  ],
  "startUrls": [],
  "freeCancellationOnly": false,
  "includeDetails": false,
  "onlyNew": false,
  "maxItems": 50,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

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

No description

## `resultsCsv` (type: `string`):

No description

# 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": [
        "Lisbon"
    ],
    "startUrls": [],
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("scrapewise/viator-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": ["Lisbon"],
    "startUrls": [],
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("scrapewise/viator-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": [
    "Lisbon"
  ],
  "startUrls": [],
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call scrapewise/viator-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,scrapewise/viator-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/LHALhvdRNh2JLPcdc/builds/g8q4n65qI28L4K4x3/openapi.json
