Klook API & Scraper
The Klook API returns the largest marketplace for things to do in Asia as clean JSON in nine actions.
🤖 Using an AI assistant? Copy this link into ChatGPT / Claude / Cursor — it reads every endpoint and parameter instantly and tells you if this API fits your use case.
activities/search takes a keyword, a destination id, or both, and returns 20 rows a page filtered by category, price band, travel date, instant confirmation and five ranking orders, with each row carrying the activity id, title, url, category, city and country, coordinates, the price in the currency you asked for, the struck price where Klook publishes one, rating, review count and Klook's own tags. activities/list is the bulk shape for the same catalogue, 50 rows a page, walking a whole city's or country's listing to its end. activity/detail adds the summary, the highlights, the images, the street address and coordinates, the place id, the category path and the price span across the activity's packages. activity/packages is where the real money lives: Klook prices each package independently, and the same Mount Fuji tour sells as nine packages from 46.65 to 503.55 in one currency, so every package comes back with its own price, cancellation policy, confirmation type, ticket type, party-size limits, stock and the dates Klook currently publishes as available. activities/batch refreshes up to 50 activities in one call for a single credit. activity/reviews returns traveller reviews newest first with text, photos, the package booked and any operator reply, normalised to the 0 to 5 scale Klook itself displays rather than the 0 to 100 the source carries. destinations/resolve, destinations/browse and categories cost nothing and exist so nobody has to guess an id, which matters because Klook offsets country ids by 1000 in search and answers a bare country id with an empty result rather than an error. Every filter offered here was run against a same-run unfiltered control and proved to change the count: a 196-result control fell to 47 for one city, 14 for another, 91 for a country, 19 for attractions, 6 for hotels, 2 for transport, 35 for a 10 to 30 price band and 39 for 100 to 500. Filters Klook accepts and silently ignores are rejected before a request goes out instead of being passed through as if they worked. All nine actions were measured at 12 of 12 on twelve different real inputs each, on 2026-09-25. No Klook account, one ReefAPI key, and the standard { ok, data, meta, error } envelope.
Seven Klook values that are not what they look like
Klook publishes several fields that read as one thing and mean another: a price that moves between calls, a rating on a different scale to the rating beside it, an id that answers with a different product. These were read from live responses on 2026-09-24 and 2026-09-25.
| Field | Looks like | Actually is | What comes back |
|---|---|---|---|
| price on a search card | the price | one of several promotional prices Klook rotates: the same activity answered with 4 distinct prices across 12 calls in the same minutes, while activities/batch and activity/detail answered with 1 each | the price Klook really showed, plus price_note on every search response pointing at the two surfaces that did not move |
| price | an exact price | sometimes the cheapest package's price instead: 135 exact and 25 from-price across 160 search cards, and 175 exact against 25 from-price-only across 200 list rows | price_is_from on both surfaces, so a from-price is never read as an exact one and never lands as a null |
| a review's rating | 100 | Klook scores reviews on 0 to 100 and activities on 0 to 5, in the same document | normalised to the 0 to 5 scale Klook displays, with rating_scale stated alongside |
| packages | null, which reads as a failure | an open-dated ticket with no dated schedule to publish, returned in the same minute another activity returned 9 packages | is_open_dated true, an empty list and a sentence, not an error |
| a country id | an id activities/search takes | offset by 1000 there: Japan is 12 in the directory and 1012 in search, and the bare id returns total 0 with no error | search_location_id from destinations/resolve, already in the form search expects, validated against Klook's published country directory, or null with the reason |
| the activity id you asked for | the activity you get | 1 of 24 sampled ids was a superseded listing that resolved to a different product with HTTP 200 | requested_activity_id, a redirected flag and a sentence naming the id actually served |
| a search row | an activity | sometimes a hotel: vertical_type 102 rows carry activity-shaped ids and 3 of 3 returned NOT_FOUND from the activity endpoints | product_type and has_activity_detail on every row, and a NOT_FOUND message that says it was a hotel id |
Prices are returned in the currency the call asks for, and every price object repeats the code it belongs to. Klook's own default is HKD, so USD is sent unless you ask for something else. Round-trip on one activity: USD 38.29, HKD 294, JPY 6,049, EUR 33.49, SGD 42.09, THB 1,101.
Real request and response JSON
Captured from the indexed primary action, activities/search, on .
{
"method": "POST",
"url": "https://api.reefapi.com/klook/v1/activities/search",
"headers": {
"x-api-key": "$REEF_KEY",
"content-type": "application/json"
},
"body": {
"destination_id": 28,
"category": "attractions",
"currency": "USD"
}
}{
"ok": true,
"meta": {
"api": "klook",
"endpoint": "activities/search",
"mode": "live",
"latency_ms": 782.3,
"record_count": 20,
"bytes": 214373,
"cache_hit": false,
"stop_reason": "limit_reached",
"charged_credits": 2,
"version": "1.0.0"
},
"data": {
"query": null,
"destination_id": 28,
"category": "attractions",
"sort": "most_relevant",
"currency": "USD",
"language": "en-US",
"page": 1,
"page_size": 20,
"total": 76,
"total_is_capped": false,
"price_note": "Klook rotates a promotional price on search cards: the same activity answered with four different prices across twelve calls in the same minutes. These are prices Klook really showed, but for a stable figure use activities/batch (up to 50 ids in one call) or activity/detail, both of which returned one price on every repeat.",
"activities": [
{
"activity_id": 171099,
"product_type": "activity",
"has_activity_detail": true,
"title": "Tokyo Chiikawa Park Ticket",
"subtitle": "Attraction passes • Tokyo",
"url": "https://www.klook.com/en-US/activity/171099-chiikawa-park/",
"category": "Attraction passes",
"city_id": 28,
"city_name": "Tokyo",
"country_id": 12,
"coordinates": {
"latitude": 35.7300697,
"longitude": 139.7173049
},
"image_url": "https://res.klook.com/image/upload/activities/tieelp5srxuzhhgek6f1.jpg",
"price": {
"amount": 22.15,
"currency": "USD",
"formatted": "US$ 22.15"
},
"price_is_from": false,
"strike_price": null,
"rating": 4.8,
"review_count": 1174,
"booked_label": "70K+ booked",
"sold_out": false,
"promo_tags": [
"1GB eSIM worth JPY600",
"FreeGift"
],
"attribute_tags": [
"Instant confirmation"
]
},
{
"activity_id": 25300,
"product_type": "activity",
"has_activity_detail": true,
"title": "teamLab Planets TOKYO Ticket",
"subtitle": "Museums • Tokyo",
"url": "https://www.klook.com/en-US/activity/25300-teamlab-planets-toyosu-tokyo-ticket/",
"category": "Museums",
"city_id": 28,
"city_name": "Tokyo",
"country_id": 12,
"coordinates": {
"latitude": 35.649121,
"longitude": 139.789774
},
"image_url": "https://res.klook.com/image/upload/activities/mn4cemkgx5r6fy6rgie6.jpg",
"price": {
"amount": 26.45,
"currency": "USD",
"formatted": "US$ 26.45"
},
"price_is_from": true,
"strike_price": null,
"rating": 4.7,
"review_count": 21474,
"booked_label": "1M+ booked",
"sold_out": false,
"promo_tags": [
"5GB eSIM",
"Fall deal",
"FreeGift"
],
"attribute_tags": [
"Book now for tomorrow",
"Instant confirmation"
]
},
{
"activity_id": 20707,
"product_type": "activity",
"has_activity_detail": true,
"title": "teamLab Borderless Tickets: MORI Building DIGITAL ART MUSEUM",
"subtitle": "Museums • Tokyo",
"url": "https://www.klook.com/en-US/activity/20707-teamlab-borderless-admission-ticket-tokyo/",
"category": "Museums",
"city_id": 28,
"city_name": "Tokyo",
"country_id": 12,
"coordinates": {
"latitude": 35.6620689,
"longitude": 139.7432671
},
"image_url": "https://res.klook.com/image/upload/activities/jrj7lehqfkhqjxaxao9e.jpg",
"price": {
"amount": 27.99,
"currency": "USD",
"formatted": "US$ 27.99"
},
"price_is_from": true,
"strike_price": null,
"rating": 4.7,
"review_count": 23923,
"booked_label": "1M+ booked",
"sold_out": false,
"promo_tags": [
"Freebies",
"Fall deal",
"Klook's choice"
],
"attribute_tags": [
"Instant confirmation"
]
}
],
"available_categories": [
{
"value": "all",
"label": "All"
},
{
"value": "tours",
"label": "Tours & experiences"
},
{
"value": "attractions",
"label": "Attraction tickets"
}
],
"available_sorts": [
{
"value": "most_relevant",
"label": "Recommended"
},
{
"value": "price",
"label": "Lowest price"
},
{
"value": "participants",
"label": "Most booked"
}
]
}
}What the Klook API does
| Action | Description | Concrete use case | Key params |
|---|---|---|---|
| activities/search | Search or browse Klook activities by keyword and/or destination, with category, price, date, instant-confirmation and sort filters. Every filter here was proved to change the result count against a same-run unfiltered control; filters Klook silently ignores are not offered. | Travel apps call activities/search to search or browse Klook activities by keyword and/or destination, with category, price, date,…. | query, destination_id, category, sort, price_min, ... |
| activities/list | Destination browse in 50-result pages — the bulk-listing shape for walking a city's or a country's whole catalogue. Fewer filters than activities/search (Klook's list endpoint ignores category and sort), but 2.5x the rows per call. | Pricing monitors call activities/list to get destination browse in 50-result pages. | query, city_id, country_id, price_min, price_max, ... |
| activity/detail | The full published record for one activity: description, highlights, what's included, images, address and coordinates, category path, rating, review count and the price span across its packages. | Itinerary products call activity/detail to get the full published record for one activity. | activity_id, currency, language |
| activity/packages | Every bookable package (option) of one activity with its OWN price, cancellation policy, confirmation type, voucher type and party-size limits — plus the dates Klook currently publishes as available. Klook prices packages independently: the same attraction can run from 46 to 503 in one currency, so a headline price is not the price. | Hospitality analysts call activity/packages to get every bookable package (option) of one activity with its OWN price, cancellation policy, conf…. | activity_id, currency, language |
| activities/batch | Price, rating, coordinates, package count and availability for up to 50 activities in ONE upstream call. This is the efficient shape for refreshing a watchlist — 50 ids cost one call instead of 50. | Travel apps call activities/batch to get price, rating, coordinates, package count and availability for up to 50 activities in ONE ups…. | activity_id, currency, language |
| activity/reviews | Traveller reviews for one activity, newest first, with rating, text, photos, the package the reviewer booked and any operator reply. | Pricing monitors call activity/reviews to get traveller reviews for one activity, newest first, with rating, text, photos, the package the…. | activity_id, page, limit, language |
| destinations/resolve | Turn a typed place name into the ids the other actions need. Returns cities and countries, each with a search_location_id already in the form activities/search expects — Klook offsets country ids by 1000 there and answers a bare country id with an empty result, so this is the intended way in. | Itinerary products call destinations/resolve to turn a typed place name into the ids the other actions need. | query, language |
| destinations/browse | Klook's own destination directory — world regions with their featured cities, and every country Klook sells in, each already carrying the id activities/search needs. The starting point when you have no place name to resolve. Ask for include_all_cities to get the complete 1,400-plus city list instead of the featured ones. | Hospitality analysts call destinations/browse to get klook's own destination directory. | include_all_cities, language |
| categories | The category vocabulary: Klook's top-level product tabs (the values activities/search accepts) and the second-level activity categories with their ids and links. | Travel apps call categories to get the category vocabulary. | language |
Call activities/search from your stack
curl -X POST https://api.reefapi.com/klook/v1/activities/search \
-H "x-api-key: $REEF_KEY" \
-H "content-type: application/json" \
-d '{"destination_id":28,"category":"attractions","currency":"USD"}'import requests
r = requests.post(
"https://api.reefapi.com/klook/v1/activities/search",
headers={"x-api-key": REEF_KEY},
json={
"destination_id": 28,
"category": "attractions",
"currency": "USD"
},
)
print(r.json()["data"])const res = await fetch("https://api.reefapi.com/klook/v1/activities/search", {
method: "POST",
headers: {
"x-api-key": process.env.REEF_KEY,
"content-type": "application/json",
},
body: JSON.stringify({
"destination_id": 28,
"category": "attractions",
"currency": "USD"
}),
});
const { ok, data, meta, error } = await res.json();Ask your MCP-connected assistant: call reefapi.klook.activities/search with {"destination_id":28,"category":"attractions","currency":"USD"}.Who uses this API and why
- Track attraction and tour pricing across Asian cities week over week, reading the stable price from activities/batch rather than the rotating search card.
- Build a things-to-do module for a travel site: resolve the city, search by category and price band, and render title, rating, review count and image from one call.
- Compare a single attraction's packages before booking, each with its own price, cancellation policy, confirmation type and party-size limits.
- Refresh a watchlist of up to 50 activities in one call for one credit, catching price moves, rating changes and next available date.
- Mine traveller reviews for one activity with text, photos, the package booked and the operator's reply, on a 0 to 5 scale.
Questions developers ask before integrating
Which currency are Klook prices in?
Whichever one your call asks for. currency is a parameter on search, list, detail, packages and batch, it accepts 42 codes measured as working, and it defaults to USD here. Klook's own default is HKD, so leaving it out upstream would hand you Hong Kong dollars. Every price object in the response repeats the currency code it belongs to, so a number is never separated from its unit. A code Klook does not support is refused before the call goes out rather than answered with an empty list, which is what the source does with one.
Why does the same activity come back at a different price on a second call?
Because Klook rotates a promotional price on its search cards. The same activity answered with four distinct prices across twelve search calls in the same minutes, while activities/batch and activity/detail answered with one price on every repeat. Those are prices Klook genuinely showed, so they are returned as shown, and every search response carries price_note pointing at the two stable surfaces. If you are tracking a price over time, read it from batch or detail.
Do prices on klook.cn match the ones I get here?
No, and that is worth knowing. klook.cn is a separate storefront, not a mirror of klook.com, and it prices differently: on one activity in the same minute and the same requested currency it showed 46.65 against klook.com's 66.65, and 503.55 against 719.35. The schema is identical, which is exactly what would hide the difference. This API reads the global klook.com storefront only, on every action and every field, so the numbers you get are the ones a traveller on klook.com sees.
Why is the price on the activity not the price I pay?
Because Klook prices each package independently and the headline figure is only a starting point. One Mount Fuji day tour sells as nine packages running from 46.65 to 503.55 in a single currency. activity/packages returns each one with its own price, cancellation policy, confirmation type, ticket type, minimum and maximum participants, stock and the dates Klook publishes as available. Nothing is copied down from the parent listing, and a struck price is only emitted where Klook's market price is genuinely above what it is selling for, because Klook publishes packages where it is below.
How do I turn a place name into an id?
destinations/resolve, which costs nothing. Pass a place the way a traveller types it and it returns cities and countries with a search_location_id already in the form activities/search expects. This matters more than it sounds: Klook offsets country ids by 1000 in search, so Japan is 12 in its directory and 1012 in search, and passing the bare id returns total 0 with no error anywhere. The rule also has an exception Klook does not document, so every computed country id is checked against Klook's own published country directory and comes back as null with the reason rather than as an id that silently returns nothing. destinations/browse gives you the whole directory when you have no name to resolve, about 1,440 cities with include_all_cities.
Does sort actually sort?
Not strictly, and that was measured rather than assumed. Klook publishes five orderings and all five genuinely reorder page one: asking for lowest price moved the cheapest row on page one from 27.99 to 1.79. But promoted listings stay mixed in, and across six queries only 63% of adjacent price pairs on a page were ascending. So sort is a ranking, it is documented as one, and if you need a guaranteed order you sort the rows yourself. Values Klook does not publish are rejected here, because the site accepts them and changes nothing.
How many results can one Klook search return?
20 a page on activities/search and 50 a page on activities/list, both fixed by Klook, and a limit parameter on those endpoints is ignored. Klook stops counting broad queries at 1,000 matches, and serves at most 2,000 reviews for one activity even when the activity has far more, so both are flagged with total_is_capped rather than presented as the real total. Narrow by city, category, price band or date to reach past the ceiling. Row counts are also language-specific: the same search returned 1,412 results in English and 1,319 in Japanese.
What does Klook not publish?
A cancellation policy at the activity level, so activity.cancellation_policy is always null and the real terms are on every row of activity/packages, where they were present on every package measured. An inclusions list as a discrete field, because what a package includes is prose that comes back in highlights rather than being invented into a structured list. A struck price on most tours, 0% of tour rows had one and 14% overall, so a discount is never manufactured. A rating on most hotel rows, 23% against 92% overall, because hotel cards are a different Klook product. And Klook has no 404: an unpublished id answers HTTP 200 with an empty result, which is translated to NOT_FOUND so you can tell a missing activity from an outage.
What is the Klook API?
Klook API is a ReefAPI endpoint group for asian activities, attraction tickets and experiences, with packages, reviews and the price in the currency you ask for. It returns live JSON through POST requests under /klook/v1.
Is the Klook API free to try?
Yes. ReefAPI starts with 1,000 free credits, no card required. Klook calls use the same shared credit balance as every other ReefAPI engine.
Do I need a Klook login or account?
No login to Klook is needed for the API response. You call ReefAPI with your x-api-key header, and the playground can run live examples before you create a production key.
How fresh is the Klook data?
The page example is captured from a live activities/search call, and production requests fetch live data through ReefAPI rather than a static sample.
How many credits does the Klook API use?
Klook actions currently cost 1-2 credits per successful call. Failed or blocked calls are free. All APIs draw from one credit pool.
Can I call Klook from an AI assistant or MCP client?
Yes. Connect ReefAPI once through MCP and your assistant can call klook actions with the same key, credit pool and JSON envelope used by normal REST requests.