# Klook Reviews Scraper (`reviewly/klook-reviews-scraper`) Actor

Scrape every review from any Klook activity — star ratings, review text, English translations, photos and booked package. Export to CSV, JSON or Excel.

- **URL**: https://apify.com/reviewly/klook-reviews-scraper.md
- **Developed by:** [Reviewly](https://apify.com/reviewly) (community)
- **Categories:** Travel, Automation, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 1,000 reviews

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/platform/actors/running/actors-in-store#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

## Klook Reviews Scraper — Extract Every Klook Review in Seconds

**Scrape all traveler reviews from any Klook activity page — ratings, full review text, English translations, reviewer photos, and package names — as clean JSON, CSV, or Excel.**

- ⚡ **Fast** — a 4,500-review activity finishes in about 5 seconds
- 🎯 **Complete** — gets every review, not just the first page
- 🌍 **Multilingual** — original text *plus* Klook's English translation
- 📸 **Rich data** — review photos, star ratings, dates, and booked package
- 🔧 **Zero setup** — paste a Klook URL and run. No proxies or API keys needed

***

### 📌 What This Actor Does

Klook shows only a handful of reviews at a time, hidden behind endless "load more" clicks. There's no export button and no public API for the data.

This **Apify Actor** does the work for you. Give it a Klook activity URL and it returns **every review** as a structured dataset you can download as **JSON, CSV, or Excel**, or pull straight into your own app via API.

#### Who it's for

| You are… | You use it to… |
|---|---|
| **Travel & tour operators** | Track what customers say about your activities and your competitors' |
| **Market researchers** | Analyze traveler sentiment across attractions, cities, or regions |
| **Data analysts & scientists** | Build review datasets for sentiment analysis, NLP, or dashboards |
| **Agencies & consultants** | Produce reputation reports for travel clients |
| **Developers** | Feed review data into an app, a BI tool, or an AI pipeline |

***

### ✨ Key Features

- **Complete review extraction** — every review on the activity, not a capped sample
- **Original text + English translation** — reviews come in many languages; you get both
- **Review photos** — full-resolution image URLs travelers uploaded
- **Star ratings & dates** — numeric 1–5 rating and an ISO timestamp for every review
- **Package names** — see which ticket or option each reviewer actually booked
- **Date filtering** — scrape only reviews newer than a chosen date, and stop early
- **Sorting & filtering** — most relevant, newest, highest/lowest rated; photos-only or English-only
- **Bulk mode** — pass many activity URLs in one run
- **Clean, flat output** — one row per review, ready for Excel or pandas with no reshaping
- **Resilient** — automatic retries so a temporary hiccup never silently truncates your data

***

### 🧠 Why This Actor Is Different

Most review scrapers drive a headless browser: slow, expensive, and fragile. This one talks to Klook's own review endpoint directly.

**What that means for you:**

| | This Actor | Typical browser-based scraper |
|---|---|---|
| **Speed** | ~5s for 4,500 reviews | Several minutes |
| **Cost** | Minimal compute | High — browsers are heavy |
| **Proxies** | Not required | Usually residential proxies required |
| **Reliability** | No page layout to break | Breaks whenever the site redesigns |

**Three correctness details that quietly break naive scrapers** — all handled here:

1. Klook's "has more pages" flag is **always true**, even past the last page. A scraper that trusts it loops forever.
2. Requesting a page number beyond the limit **silently returns page 1 again** instead of an error — so naive scrapers re-collect the same reviews and report inflated counts.
3. Klook occasionally returns an **empty page at random**. Without retries, a run stops early and *looks* successful while missing thousands of reviews.

This Actor guards against all three and de-duplicates every review by ID, so your counts are real.

***

### ⚙️ Input Configuration

| Field | Type | Required | Default | Description |
|---|---|---|---|---|
| `startUrls` | Array | ✅ Yes | — | Klook activity URLs to scrape |
| `maxReviews` | Integer | No | `0` | Max reviews per activity. `0` = all |
| `targetDate` | String | No | — | Only reviews published on/after this date |
| `sortKey` | String | No | `sort_most_relevant` | Review sort order |
| `filterKey` | String | No | `all` | Which reviews to include |
| `proxyConfiguration` | Object | No | — | Optional proxy settings |

#### `startUrls`

Any Klook activity URL works, including regional variants:

```
https://www.klook.com/activity/363-dream-world-bangkok/
https://www.klook.com/en-US/activity/363-dream-world-bangkok/
https://www.klook.com/activity/363-dream-world-bangkok/?some=param
```

You can also pass a bare activity ID (`"363"`). Duplicate or regional variants of the same activity are automatically merged, so you're never charged twice for the same data.

#### `sortKey` options

| Value | Meaning |
|---|---|
| `sort_most_relevant` | Klook's default relevance ranking |
| `sort_review_time_desc` | Newest first |
| `sort_score_high_to_low` | Best rated first |
| `sort_score_low_to_high` | Worst rated first — great for finding complaints |

#### `filterKey` options

| Value | Meaning |
|---|---|
| `all` | Every review |
| `only_photo` | Only reviews containing photos |
| `read_lang` | Only English-language reviews |

#### Example input

Scrape everything:

```json
{
  "startUrls": [
    { "url": "https://www.klook.com/activity/363-dream-world-bangkok/" }
  ],
  "maxReviews": 0
}
```

Only negative reviews from the last year, for a complaint analysis:

```json
{
  "startUrls": [
    { "url": "https://www.klook.com/activity/363-dream-world-bangkok/" }
  ],
  "targetDate": "2026-01-01",
  "sortKey": "sort_score_low_to_high"
}
```

Compare several activities at once:

```json
{
  "startUrls": [
    { "url": "https://www.klook.com/activity/363-dream-world-bangkok/" },
    { "url": "https://www.klook.com/activity/1000-universal-studios-singapore/" }
  ],
  "maxReviews": 500
}
```

#### 💡 Tips for best results

- **Start small.** Set `maxReviews: 20` on your first run to preview the data shape before scraping everything.
- **Use `targetDate` for recurring runs.** Scheduling a weekly run with a recent `targetDate` collects only new reviews and finishes far faster.
- **Leave `sortKey` alone when using `targetDate`.** The Actor automatically switches to newest-first so it can stop at your cutoff instead of fetching every page.
- **Skip the proxy.** It isn't needed. Only enable it if you're running very large batches.

***

### 📤 Output Format

**One row per review** — no nested arrays to flatten, so CSV and Excel exports open ready to use.

#### Sample output

```json
{
  "activityId": 363,
  "activitySlug": "dream-world-bangkok",
  "activityUrl": "https://www.klook.com/activity/363-dream-world-bangkok/",
  "activityRating": 4.6,
  "activityReviewCount": 4557,
  "activityRatingDesc": "Fantastic",

  "reviewId": 19023109,
  "authorName": "LAM ********",
  "authorAvatar": "https://cdn.klook.com/upload/img200X200/zh_HK_02.png",
  "rating": 5,
  "ratingDesc": "Fantastic",
  "publishedAt": "2026-08-03T11:14:58.000Z",
  "text": "星期一遊人很少，幾乎都不用排隊，可以𣈱玩。",
  "textTranslated": "There were very few visitors on Monday, so we hardly had to wait in line.",
  "packageId": 157247,
  "packageName": "Super Visa",
  "likedCount": 0,
  "hasReply": false,
  "images": [
    "https://cdn.klook.com/user_review/product/1949610/b57d8533.jpeg"
  ],
  "scrapedAt": "2026-08-09T14:02:11.000Z"
}
```

#### Field reference

**Activity fields** (repeated on every row so each row stands alone)

| Field | Type | Description |
|---|---|---|
| `activityId` | Number | Klook's internal activity ID |
| `activitySlug` | String | URL slug, e.g. `dream-world-bangkok` |
| `activityUrl` | String | Canonical activity URL |
| `activityRating` | Number | Overall average rating, e.g. `4.6` |
| `activityReviewCount` | Number | Total reviews Klook reports |
| `activityRatingDesc` | String | Klook's label, e.g. `Fantastic` |

**Review fields**

| Field | Type | Description |
|---|---|---|
| `reviewId` | Number | Unique review ID — use this as your primary key |
| `authorName` | String | Reviewer name as Klook displays it |
| `authorAvatar` | String | Avatar image URL |
| `rating` | Number | Star rating, `1`–`5` |
| `ratingDesc` | String | Rating label, e.g. `Fantastic` |
| `publishedAt` | String | ISO 8601 publish timestamp |
| `text` | String | Review in its **original language** |
| `textTranslated` | String | Klook's **English translation** |
| `packageId` | Number | ID of the package the reviewer booked |
| `packageName` | String | Package name, e.g. `Super Visa` |
| `likedCount` | Number | Helpful votes the review received |
| `hasReply` | Boolean | Whether the operator replied |
| `images` | Array | Full-resolution photo URLs |
| `scrapedAt` | String | When this row was collected |

> **Note on names:** Klook itself anonymizes many reviewers (`Klook User`, `LAM ********`). That masking comes from Klook — no real names are hidden or altered by this Actor.

***

### ▶️ How to Use

#### Option A — Apify Console (no code)

1. **Open the Actor** in the Apify Store and click **Try for free**.
2. **Paste your Klook activity URL** into the *Klook activity URLs* field.
3. *(Optional)* Set **Max reviews** to `20` for a quick test run.
4. Click **Start**.
5. When it finishes, open the **Storage → Dataset** tab and **Export** as CSV, Excel, or JSON.

That's it — no proxy setup, no API key, no configuration.

#### Option B — API

Run it from your own code with the Apify client:

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

const client = new ApifyClient({ token: 'YOUR_APIFY_TOKEN' });

const run = await client.actor('reviewly/klook-reviews-scraper').call({
    startUrls: [
        { url: 'https://www.klook.com/activity/363-dream-world-bangkok/' },
    ],
    maxReviews: 0,
});

const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(`Collected ${items.length} reviews`);
```

```python
from apify_client import ApifyClient

client = ApifyClient("YOUR_APIFY_TOKEN")

run = client.actor("reviewly/klook-reviews-scraper").call(run_input={
    "startUrls": [
        {"url": "https://www.klook.com/activity/363-dream-world-bangkok/"}
    ],
    "maxReviews": 0,
})

for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item["rating"], item["text"][:80])
```

#### Option C — Scheduled monitoring

1. Run the Actor once to confirm your settings.
2. Open the **Schedules** tab in Apify and create a schedule (e.g. daily).
3. Set `targetDate` to a recent date so each run collects only fresh reviews.
4. Connect a **webhook** to push new reviews into Slack, a database, or a Google Sheet.

***

### 📈 Use Cases

#### 1. Competitor reputation analysis

Scrape reviews for your activity and your three closest competitors. Compare average ratings, complaint themes, and review volume over time to see exactly where you're losing bookings.

#### 2. Customer sentiment & AI analysis

Export thousands of reviews and run them through an LLM or sentiment model. Because you get both original text and English translations, you can analyze a global customer base in one language without a separate translation step.

#### 3. Product & operations improvement

Sort by `sort_score_low_to_high` and pull only 1–2 star reviews. The `packageName` field shows which specific ticket option generates the most complaints, so fixes land where they matter.

#### 4. Market research & travel trends

Scrape reviews across many attractions in a destination to identify demand patterns, seasonality, and what travelers value most — useful for pricing, planning, and investment decisions.

#### 5. Marketing content & social proof

Filter with `only_photo` to find your best photo-rich, 5-star reviews, then use them (with proper attribution) as testimonials and user-generated content for ads and landing pages.

***

### 🛠️ Advanced Tips

**Speed.** Runs are network-bound, so most activities finish in seconds. The main cost driver is total review count, not page count.

**Incremental scraping.** Rather than re-scraping everything, schedule runs with a rolling `targetDate`. Deduplicate on `reviewId` — it's stable across runs.

**Bulk scraping.** Pass many URLs in `startUrls` for a single run. If one URL is invalid, the Actor logs a warning and continues with the rest, so one bad link never kills the batch.

**Proxies.** Genuinely optional. Klook's review endpoint has no rate limiting in our testing. Enable Apify Proxy only if you're running unusually large or frequent batches.

**Large activities.** For activities with tens of thousands of reviews, use `maxReviews` to cap the run, or use `sortKey` variants to sample specific slices (best-rated, worst-rated, newest).

***

### ❓ FAQ & Troubleshooting

**Does this need proxies or an API key?**
No. It works out of the box. Proxy configuration is available but not required.

**Why doesn't the output include the activity's name?**
Klook serves the activity title only on its bot-protected HTML page. Fetching it would require a full headless browser, making every run dramatically slower and more expensive. Since `activityId`, `activitySlug`, and `activityUrl` identify the activity precisely, we deliberately left it out to keep runs fast and cheap. The slug is human-readable (`dream-world-bangkok`).

**The run returned 0 reviews. What happened?**
Almost always the activity genuinely has no reviews yet. Open the URL in a browser to confirm. Also check the log for a `Skipping "…": Unrecognised Klook activity URL` warning, which means the URL wasn't an activity page.

**Why is `activityReviewCount` higher than the rows I received?**
If you set `maxReviews` or `targetDate`, that's expected — those cap the run. Without them, the row count should match. `activityReviewCount` is Klook's own reported total.

**Can I scrape Klook hotels or car rentals?**
Not currently. This Actor targets **activity** pages (`/activity/...`). Other Klook verticals use a different internal structure. Email us if you need one — it's a small extension.

**Can I get the operator's reply to a review?**
No. Klook's endpoint exposes a `hasReply` flag but never the reply text itself, so it isn't available to scrape.

**Are reviewer names anonymized?**
Klook anonymizes many names before serving them (`Klook User`, `LAM ********`). You get exactly what Klook displays publicly.

**How do I get only recent reviews?**
Set `targetDate` (e.g. `2026-01-01`). Leave `sortKey` at its default and the Actor automatically sorts newest-first so it stops as soon as it passes your cutoff.

**Can I export to Excel or Google Sheets?**
Yes. In **Storage → Dataset**, export as CSV, XLSX, JSON, or HTML. The flat one-row-per-review format opens cleanly in any spreadsheet tool.

**Is web scraping public review data legal?**
This Actor collects only publicly visible data and no personal login-protected information. You are responsible for complying with Klook's terms and applicable law (including GDPR) in your jurisdiction and use case. If in doubt, consult a lawyer.

***

### 📞 Support

Questions, bug reports, or a custom scraping need?

- 📧 **Email:** <me@ahmedhrid.com>
- 🐞 **Issues:** Use the **Issues** tab on the Actor's Apify Store page

When reporting a problem, please include the **run ID** and the **input** you used — it makes diagnosis much faster.

Need a scraper for another travel platform, or a custom data pipeline? Get in touch — custom Actors are available on request.

***

*Keywords: Klook scraper, Klook reviews API, Klook data extraction, travel review scraper, Apify Actor, web scraping, tourism data, activity reviews, travel analytics, review sentiment analysis, competitor review monitoring.*

# Actor input Schema

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

Klook activity pages to scrape reviews from. Locale prefixes such as /en-US/ are accepted. A bare activity id (e.g. 363) also works.

## `maxReviews` (type: `integer`):

Stop after this many reviews per activity. Set to 0 to scrape all of them.

## `targetDate` (type: `string`):

Skip reviews published before this date (ISO 8601). Unless you pick a sort order yourself, the run switches to 'Newest' so it can stop as soon as it passes the cutoff.

## `sortKey` (type: `string`):

Order in which Klook returns the reviews.

## `filterKey` (type: `string`):

Restrict which reviews are returned.

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

Optional. Klook's review API has no bot-wall and no rate limiting, so datacenter proxies are plenty — residential is not needed.

## Actor input object example

```json
{
  "startUrls": [
    {
      "url": "https://www.klook.com/activity/197681-phuket-adventure-day-tour-rafting-atv-zipline-elephant-trekking/"
    }
  ],
  "maxReviews": 0,
  "sortKey": "sort_most_relevant",
  "filterKey": "all",
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

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

Each record is a single review. Activity context is repeated on every row so each row stands alone: activityId, activitySlug, activityUrl, activityRating, activityReviewCount and activityRatingDesc. The review itself: reviewId (stable, use it as your primary key for de-duplication across runs), authorName and authorAvatar as Klook displays them, rating from 1 to 5 with its ratingDesc label, publishedAt as an ISO 8601 timestamp, text in the reviewer's original language, textTranslated holding Klook's English translation, packageId and packageName for the ticket option actually booked, likedCount for helpful votes, a hasReply flag indicating the operator responded, images\[] with full-resolution photo URLs, and scrapedAt. Note that Klook itself anonymizes many reviewer names before serving them.

# 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 = {
    "startUrls": [
        {
            "url": "https://www.klook.com/activity/197681-phuket-adventure-day-tour-rafting-atv-zipline-elephant-trekking/"
        }
    ],
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("reviewly/klook-reviews-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 = {
    "startUrls": [{ "url": "https://www.klook.com/activity/197681-phuket-adventure-day-tour-rafting-atv-zipline-elephant-trekking/" }],
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("reviewly/klook-reviews-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 '{
  "startUrls": [
    {
      "url": "https://www.klook.com/activity/197681-phuket-adventure-day-tour-rafting-atv-zipline-elephant-trekking/"
    }
  ],
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call reviewly/klook-reviews-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,reviewly/klook-reviews-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/r39ENJ02pKEW8Hjjd/builds/cNubvx5PIbbYZzoVw/openapi.json
