Klook All-in-One API avatar

Klook All-in-One API

Pricing

from $15.60 / 1,000 searches

Go to Apify Store
Klook All-in-One API

Klook All-in-One API

Unofficial always-on REST API for live Klook data: Home feed, category tree, search across every vertical, activity detail/reviews, car rentals, airport transfers, eSIM, dining, deals, destination guides & recently-viewed — in any currency. No account needed.

Pricing

from $15.60 / 1,000 searches

Rating

0.0

(0)

Developer

Romy

Romy

Maintained by Community

Actor stats

0

Bookmarked

1

Total users

0

Monthly active users

13 hours ago

Last modified

Categories

Share

What does Klook All-in-One API do?

Klook All-in-One API is a REST endpoint for Klook's live travel data — the Home tab's content feed (Recommended, Nearby, seasonal rails), the full nested category tree, search with the app's entire real filter panel across every vertical (not just "Things to do"), activity detail/reviews/similar, car rental search, airport transfer flight lookup, eSIM plans, Food & dining city info, and Sale-tab deals. All from a single always-on API powered by Apify Standby.

It talks directly to the same internal APIs the official Klook Android app uses, reverse-engineered by disassembling the app's native request-signing library and validated against live traffic captured from a real device. No Klook account, no API key — call the endpoint, get JSON back.

Why use Klook All-in-One API?

  • Every vertical, not just activities — confirmed live: the same signing scheme covers car rentals, airport transfers, eSIM, dining, and deals, each a genuinely separate backend subsystem, not a re-skin of the activity search
  • Real car rental search, not a stub — a full search-then-poll flow (matching the app's own real behavior) returning real, bookable cars with real suppliers and prices
  • The whole Home tab — Recommended, Nearby (location-based), and whatever seasonal rail Klook is currently running, discoverable live via /home/feed/tabs rather than hardcoded
  • The complete category tree/categories/tree mirrors the app's "All" screen exactly, all the way down (e.g. Transport's rail passes split into Japan/Europe/Metro, not merged into one)
  • Every real search filter — main category (own filter set per tab, confirmed live), branch category, dates, guided language, services, departure time, duration, minimum review score, price range, and more — every one confirmed live by tapping the actual control in the app
  • Real, working pagination — confirmed live on the Home feed, search, and reviews, with real result counts
  • Always-on — Standby mode means no cold start, responds in milliseconds
  • No account needed — every endpoint works fully anonymously
  • Use cases: travel content aggregation, price/rating monitoring, activity discovery bots, review analysis, car rental price comparison

Endpoints

MethodPathDescription
GET/categories/treeFull nested category tree (the app's "All" screen)
GET/categoriesHome-tab quick-link categories ("More to explore")
GET/home/feed/tabsLive list of Home-tab content-feed tabs
GET/home/feedContent for one Home-tab feed rail (Recommended, Nearby, ...)
GET/home/nearbyHome tab's "Nearby gems" widget
GET/home/nearby/moreFull nearby-activities list behind /home/nearby's "See more"
GET/home/where-to-nextHome tab's "Where to next?" destination suggestions
GET/trips/inspirationTrips tab's destination inspiration carousel
GET/recently-viewedHome tab's "Recently viewed" rail (device-scoped, self-populating)
GET/destinations/{id}A city's full destination guide
GET/destinations/{id}/trending-sightsA city's trending points of interest, with full descriptions
GET/search/initSearch screen's init state (top searches, trending, new)
GET/search/suggestAs-you-type search suggestions for a partial query
GET/searchFull-text search with the app's full filter panel — every vertical
GET/search/filtersLive filter schema (ids + labels) for a search query
GET/activities/{id}Full activity/experience detail
GET/activities/{id}/similarRecommended/similar activities
GET/activities/{id}/reviewsCustomer reviews for one activity, with real filters
GET/activities/{id}/esim-packageseSIM data plan options for an eSIM activity
GET/esim/destinationseSIM homepage: popular destinations
GET/transport/car-rentals/countriesCountries/cities available for car rentals
GET/transport/car-rentals/locationsReal pickup/drop-off locations for a city
GET/transport/car-rentals/search-locationsFree-text location search for car rentals
GET/transport/car-rentals/searchSearch car rentals (real results, not a stub)
GET/transport/airport-transfers/searchLook up a flight for airport transfer booking
GET/dining/homeFood & dining city info
GET/dealsSale-tab deals page for a country
GET/deals/moreReal pagination for the Deals page's promotions grid

How to use Klook All-in-One API

  1. Open this Actor's Standby API URL (shown on the Actor's page, under the API tab).
  2. Call any endpoint — for example:
    curl "https://romy--klook-all-in-one-api.apify.actor/categories/tree"
    curl "https://romy--klook-all-in-one-api.apify.actor/home/feed/tabs"
    curl "https://romy--klook-all-in-one-api.apify.actor/search/suggest?query=osa"
    curl "https://romy--klook-all-in-one-api.apify.actor/search?query=tokyo&main_category=1&sort=price"
    curl "https://romy--klook-all-in-one-api.apify.actor/activities/93901"
    curl "https://romy--klook-all-in-one-api.apify.actor/activities/93901?currency=USD"
    curl "https://romy--klook-all-in-one-api.apify.actor/activities/93901/reviews?star_num=5"
    curl "https://romy--klook-all-in-one-api.apify.actor/esim/destinations"
    curl "https://romy--klook-all-in-one-api.apify.actor/deals?country_id=1012"
    curl "https://romy--klook-all-in-one-api.apify.actor/deals/more?country_id=1012&page=2"
    curl "https://romy--klook-all-in-one-api.apify.actor/trips/inspiration"
    curl "https://romy--klook-all-in-one-api.apify.actor/home/nearby/more?lat=-6.2884&lng=106.7183"
    curl "https://romy--klook-all-in-one-api.apify.actor/destinations/28"
    curl "https://romy--klook-all-in-one-api.apify.actor/destinations/28/trending-sights"
    curl "https://romy--klook-all-in-one-api.apify.actor/recently-viewed"
    # Car rentals: resolve a location first, then search
    curl "https://romy--klook-all-in-one-api.apify.actor/transport/car-rentals/locations?city_id=28"
    curl "https://romy--klook-all-in-one-api.apify.actor/transport/car-rentals/search-locations?q=Osaka"
    curl "https://romy--klook-all-in-one-api.apify.actor/transport/car-rentals/search?pickup_country_code=JP&pickup_location_code=HND&pickup_location_id=50036510&pickup_location_name=Tokyo%20Haneda%20International%20Airport&pickup_city_id=28&pickup_city_name=Tokyo&pickup_lng_lat=139.7783379,35.5495626&pickup_date_time=2026-08-27%2010:00&return_date_time=2026-08-30%2010:00"
    Every call needs your Apify API token, either as Authorization: Bearer <token> or ?token=<token> — the Actor's page API tab has a ready-to-copy version with your token filled in.
  3. Read the JSON response — no setup required.

Parameters

GET /home/feedtab required (get valid values from /home/feed/tabs, e.g. 1=Recommended, 6=Nearby).

ParamTypeExampleMeaning
tabstring1A tab value from /home/feed/tabs
pageint, default 111-indexed page — real pagination
sizeint, default 1010Results per page
latnumber-6.2884Required for location-dependent tabs like Nearby — confirmed live
lngnumber106.7183See lat

GET /home/nearbylat/lng required.

ParamTypeExampleMeaning
latnumber-6.2884Latitude
lngnumber106.7183Longitude
limitint, default 1212Number of cards to return

GET /home/nearby/morelat/lng required. Full list, not paginated — one call returns everything (confirmed live: 30 items in a single response).

GET /home/where-to-next — no required parameters.

ParamTypeExampleMeaning
latnumber-6.2884Narrows suggestions by location
lngnumber106.7183Longitude
limitint, default 1212Number of destinations to return

GET /search/suggestquery required (partial text, e.g. osa). Distinct from /search/init, which is the state before typing anything.

GET /searchquery required. Every filter param below is real and was confirmed live — see Data notes.

ParamTypeExampleMeaning
querystringtokyoSearch text
startint, default 111-indexed result offset — real pagination
sizeint, default 1010Results per page
sortenumpricemost_relevant | price | participants | publish_time | review_score
datestring (date)2026-08-28A single date — confirmed live this is one day, not a real range
main_categorystring11=Tours & experiences, 2=Attraction tickets, 30=Transport, 54=Hotels, 70=More
branch_categorystring14Sub-category id — see /categories/tree or /search/filters
guided_languagestring3029Tour guide language id (3029=English guided)
servicescomma-separated3044,3013Small group, hotel pick-up, free cancellation, guaranteed departure, ...
departure_timecomma-separated54883Evening/afternoon departure
durationcomma-separated79590-3 hours, 3-5 hours, full day, 2+ days, ...
review_scorestring4.5Minimum score — 3, 3.5, 4, or 4.5
price_minnumber0Minimum price
price_maxnumber100Maximum price
othercomma-separatedother_instant_keyother_instant_key=Instant confirmation, 36822=Halal-friendly
locationstring1012Narrow results to a location id (1012=Japan)
currencystring, default GBPUSDISO currency code for every result's price — confirmed live, see Data notes

GET /search/filtersquery required.

ParamTypeExampleMeaning
querystringtokyoSame search text as /search
main_categorystring1Get the filter set for one tab instead of the merged All-tab set — the set genuinely differs

GET /activities/{id}, GET /activities/{id}/esim-packages — path param id required. Both also take currency (string, default GBP) — confirmed live to change every returned price.

GET /activities/{id}/similar

ParamTypeExampleMeaning
lengthint, default 88Number of recommendations to return
currencystring, default GBPUSDISO currency code — confirmed live to change each card's price

GET /activities/{id}/reviews

ParamTypeExampleMeaning
pageint, default 111-indexed page — real pagination
limitint, default 1010Reviews per page
star_numint 1-55Filter to one star rating — confirmed real
only_imagebooleantrueOnly reviews with a photo — confirmed real

GET /transport/car-rentals/locationscity_id required (from /transport/car-rentals/countries).

GET /transport/car-rentals/search-locationsq required (free-text, e.g. Osaka); source_country_code optional, default GB.

GET /transport/car-rentals/search — every pickup_* field required, resolved from /transport/car-rentals/locations.

ParamTypeExampleMeaning
pickup_country_codestringJPFrom a location's country_code
pickup_location_codestringHNDFrom a location's iata_code
pickup_location_idstring50036510From a location's poi_id
pickup_location_namestringTokyo Haneda International AirportFrom a location's poi_name
pickup_city_idstring28
pickup_city_namestringTokyo
pickup_lng_latstring139.7783379,35.5495626"longitude,latitude" from a location
pickup_date_timestring2026-08-27 10:00"YYYY-MM-DD HH:mm"
return_date_timestring2026-08-30 10:00
return_*Optional — omit all to return to the same pickup location
ageint, default 3030Driver's age
license_country_codestring, default GBGB
pageint, default 12Real pagination — confirmed live: distinct pages return distinct cars
page_sizeint, default 1010
currencystring, default GBPUSDISO currency code — confirmed live to change filter_group.currency and every listed car's price

GET /transport/airport-transfers/searchflight_number/departure_date required.

ParamTypeExampleMeaning
flight_numberstringCX123
departure_datestring (date)2026-08-27
directionarrival|departure, default arrivalarrival

GET /dining/homecity_id required.

GET /dealscountry_id required. currency optional (string, default GBP) — confirmed live to change coupons' discount_currency and related price fields.

GET /deals/morecountry_id required. Real pagination — confirmed live: distinct page values return distinct activities.

ParamTypeExampleMeaning
pageint, default 121-indexed page — real pagination, confirmed live
limitint, default 1010Results per page
category_idstring, default 00
category_typestring
currencystring, default GBPUSDISO currency code — confirmed live to change each card's display_price

GET /destinations/{id}, GET /destinations/{id}/trending-sightsid required (path param): a city id, same as city_id elsewhere in this API (e.g. 28=Tokyo, 29=Osaka).

GET /recently-viewed — no required parameters. limit optional (int, default 50). currency optional (string, default GBP) — confirmed live to change each card's price.

GET /categories/tree, GET /categories, GET /home/feed/tabs, GET /esim/destinations, GET /transport/car-rentals/countries, GET /search/init, GET /trips/inspiration — no parameters.

Output

Every response is returned directly over HTTP — this Actor does not write to an Apify dataset. Responses pass through Klook's own real (deeply nested) JSON data. Real responses (trimmed for readability):

GET /transport/car-rentals/search?... (Tokyo Haneda, 27–30 Aug):

{
"success": true,
"results": {
"result": {
"car_count": 214,
"car_card_list": [
{ "car_brand_name": "Toyota", "car_name": "Vitz", "car_group_type": "Compact car", "seat": 5, "luggage": 2 }
]
}
}
}

GET /transport/airport-transfers/search?flight_number=CX123&departure_date=2026-08-27:

{ "success": true, "results": { "result": { "found": false, "flights": null } } }

GET /esim/destinations (trimmed):

{ "success": true, "destinations": { "result": { "regions": [{ "name": "Japan", "activity_id": 109393 }] } } }

GET /categories/tree (trimmed to two sections):

{
"success": true,
"tree": {
"result": {
"category": {
"menus": [
{
"title": "Things to do",
"sub_menu_list": [
{
"title": "Tours & experiences",
"sub_menu_list": [{ "title": "Tours", "business_name": "9_Tours" }]
}
]
},
{
"title": "Transport",
"sub_menu_list": [{ "title": "Japan rail passes", "business_name": "35_Japan trains" }]
}
]
}
}
}
}

GET /home/nearby?lat=-6.2884&lng=106.7183&limit=1:

{
"success": true,
"nearby": {
"result": {
"title": "Nearby gems in South Tangerang",
"recommend_cards": [{ "data": { "title": "Up at Thamrin Nine Observatory Deck in Jakarta" } }]
}
}
}

GET /home/where-to-next:

{
"success": true,
"next": {
"result": {
"title": "Where to next?",
"items": [{ "data": { "title": "Tokyo", "sub_title": "1191 activities" } }]
}
}
}

GET /search?query=tokyo&main_category=1&sort=price&size=1:

{
"success": true,
"results": {
"result": {
"search_result": {
"total": 835,
"cards": [
{
"data": {
"city_id": 28,
"title": "Tokyo Chopstick Making Experience in Ginza",
"deep_link": "https://www.klook.com/en-GB/activity/183178-tokyo-chopstick-making-experience-in-ginza/"
}
}
]
}
}
}
}

GET /activities/93901/reviews?star_num=1&limit=1:

{
"success": true,
"reviews": {
"result": {
"show_total": 33758,
"score": 4.8,
"item": [{ "author": "Klook User", "rating": 20, "content": "..." }]
}
}
}

Data notes

  • Every endpoint requires Klook's own request signature (x-signature), computed with a from-scratch reimplementation of the algorithm the app's native signing library uses — reverse-engineered and validated byte-for-byte against real signatures captured from the live app. This Actor computes it itself, entirely server-side; nothing here depends on a live device or the native library at runtime.
  • The signing scheme covers far more than the activities API — confirmed live across real car rental, airport transfer, eSIM, dining, and deals flows, watching the same HMAC construction apply to each. These are genuinely separate backend subsystems, not a re-skin of one API.
  • Car rental search mirrors the app's real two-step flow internally. The app itself kicks off an async search (returns a job id) then polls for results repeatedly (confirmed live: ~10 polls before real results appear) — /transport/car-rentals/search does that polling server-side so callers get one clean response with real results, not a job id to manage.
  • /categories/tree matches the app's own "All" screen exactly, verified section by section against a real screenshot. It only works with an empty request query string — replaying the app's own captured params gets rejected for reasons unrelated to the request signature (which is separately fully solved); the empty-param response is identical to what the app gets either way.
  • /search/filters's branch_category options are query-scoped — a narrow query silently omits categories with zero matching results (confirmed live: query=tokyo drops 3 of Attraction tickets' 12 categories). For category ids independent of any query, use /categories/tree instead.
  • Every /search filter param was confirmed live, not guessed, by tapping the actual control in the app's Filters dialog and reading the resulting request off the wire. The filter set itself was confirmed to differ by main_category — Tours & experiences exposes 10 filter dimensions, Attraction tickets only 5.
  • /search genuinely covers every vertical — confirmed live: main_category=30 (Transport) returns real Tokyo subway/Shinkansen tickets, main_category=54 (Hotels) returns real bookable listings, through the exact same endpoint as activities.
  • Pagination is real everywhere it's offered/home/feed's page/size, /search's start/size, /activities/{id}/reviews's page, /transport/car-rentals/search's page/page_size, and /deals/more's page/limit all confirmed by comparing results and observing genuinely different items (zero id overlap across pages, in every case tested).
  • Every other list-shaped endpoint was audited for pagination and confirmed to return everything in one call, not paginated: /activities/{id}/similar (its length param already returns up to total in one shot — length=20 against a total=12 activity returned all 12), /esim/destinations, /activities/{id}/esim-packages, /dining/home, /trips/inspiration (fixed total=5), /transport/car-rentals/countries (every country, ~230, in one response), /transport/car-rentals/locations, and /transport/car-rentals/search-locations. /home/nearby's own limit param is a one-shot size cap, not a page — its actual "see more" is the separate /home/nearby/more endpoint documented above. /home/where-to-next's "See more" opens a plain klook.com webpage, not an API call at all.
  • Device identity is self-generated per Actor run, matching how the real app behaves on first install — it's not validated against any device registry (confirmed: a freshly generated id works immediately with no prior registration).
  • /search/suggest and /transport/car-rentals/search-locations were both confirmed live by typing a partial query into the app's real search box / pickup-location field and capturing the resulting request — /search/suggest returns "Osaka" with the matched substring highlighted for query osa; /transport/car-rentals/search-locations resolves Osaka straight to Kansai International Airport, same as what the app itself resolves to.
  • /trips/inspiration was confirmed live by opening the app's Trips tab, a screen not otherwise reachable through Home or Search — the response includes a "Recently browsed" tag as one server-assigned tag among several (alongside e.g. "Maritime Heritage", "Island Gateway"), not a client-side label.
  • /destinations/{id} was confirmed live by tapping a city from /search/init's Trending destinations rail — it opens a dedicated city-guide screen, not /search with a city filter. Confirmed with two different cities: dest_id=28 and dest_id=29 round-trip to Tokyo and Osaka respectively, the same ids used as city_id everywhere else in this API.
  • /destinations/{id}/trending-sights is a separate, richer endpoint from /destinations/{id}, confirmed live by tapping into a city's "Trending sights" card — a different backend subsystem (cardinfocenterservicesrv) from every other destination/home endpoint here, returning a full description per point of interest rather than just a title/image.
  • /recently-viewed is self-populating, confirmed live with a device identity that had never made a single request before: calling GET /activities/{id} once was enough to make that exact activity show up in /recently-viewed on the very next call. There is no separate "mark as viewed" endpoint — viewing an activity's detail through this API is itself what registers it.
  • Leaf categories under GET /categories/tree's "All" screen were spot-checked and confirmed to browse through the app's own internal search, carrying the same category ids already exposed by GET /search's branchCategory/mainCategory params — no separate landing page per category, so no new endpoint was needed here.
  • currency is a real, caller-controlled parameter on every price-returning endpoint, not just a display label. It was hardcoded to GBP in every request this Actor makes; confirmed live it's read by Klook's backend and genuinely changes the returned price (not just a symbol swap) — GET /activities/93901 returns selling_price: "35.99"/currency: "GBP" with the default, "49.09"/"USD" with currency=USD, and "822994"/"IDR" with currency=IDR. It isn't part of the request signature (only path+query/body is signed), so passing any ISO code is always safe. Exposed on: /search, /activities/{id}, /activities/{id}/similar, /activities/{id}/esim-packages, /transport/car-rentals/search, /deals, /deals/more, /recently-viewed. Every other endpoint (/dining/home, /destinations/{id}, /transport/airport-transfers/search, etc.) was checked and confirmed to carry no price field at all, so currency was deliberately left off rather than added as dead weight.
  • All outbound requests route through Apify's residential proxy pool, not the Actor container's own IP. Confirmed live: a request byte-for-byte identical to one that succeeds from a non-datacenter network got back a bare, empty-body HTTP 403 when made directly from the deployed Standby container — an IP-reputation block at Klook's edge, not a signing or logic issue. One proxy session per device identity keeps a stable IP for the run, same as a real device would have.

Pricing

Pay-per-event, billed on each successful call. See the Actor's Pricing tab for current rates.

Known limitations

  • No booking/pricing-calendar endpoint for activities. Klook protects activity schedule/availability and package-pricing endpoints with an additional bot-detection layer (DataDome) that returns a JS challenge page instead of JSON, even with a fully valid request signature — confirmed by testing repeatedly with correct signatures, across several related internal endpoints. /activities/{id} still returns the activity's base price and discount info from its own detail payload.
  • No dedicated "recently viewed" endpoint found. Correction: found in a later pass. An earlier version of this README reported no such endpoint after investigating multiple screens without triggering the right request. It was found by actually viewing an item in the app first, then tapping "See more" — see GET /recently-viewed above. /trips/inspiration's "Recently browsed" tag (one tag among several on a generic recommendation carousel) is unrelated and still not a reliable per-user history signal on its own.
  • Several Account-tab and cart features require a logged-in session and were not built, confirmed live by testing each unauthenticated: Wishlist, My bookings, price-drop alerts (return a generic server error with no session), the referral-program details endpoint, and the shopping cart. The notification bell redirects straight to the login screen. This Actor only wraps guest-accessible endpoints — no login flow is implemented or planned, by design.
  • Help Centre, currency, and language settings aren't wrapped. Currency/language are bundled client-side data with no API call behind them (confirmed live: switching currency fires no signed request). Help Centre's FAQ content loads with no signed request in the capture either, suggesting a separate (likely third-party) support backend outside this Actor's cracked signing scheme — same category as the WebView-based Events & shows gap above.
  • No dedicated restaurant search/listing endpoint — root-caused to DataDome, not the request signature. /dining/home (city info) is confirmed working with no session state at all. The app's own restaurant-listing call is protected by the same bot-detection layer as the blocked activity endpoints above — the real app's requests to it carry a session token and cookie sourced from a periodic device-check call this Actor doesn't replicate, and replaying the exact captured token/cookie pair standalone still gets rejected (these tokens are evidently bound to the originating device/network, not just present-or-absent). Solving it would mean implementing that bot-detection layer's own device-fingerprint challenge-response protocol — a separate reverse-engineering effort from the request-signing scheme this Actor already cracked.
  • No dedicated hotel detail/booking endpoint — confirmed to be a wholly separate network stack, not just a different signature. /search's Hotels results (main_category=54) are real, but the dedicated hotel search-and-book flow (star ratings, distance-from-station, per-night rates, room selection) never calls any function this Actor's cracked signing scheme covers — confirmed live by monitoring every native signing function this Actor's cracked scheme covers and watching zero requests fire while using that flow. A deeper pass confirmed the hotel flow runs on a wholly separate HTTP engine from every other verified vertical, and doesn't merely sign differently — it bypasses this Actor's own traffic-capture setup entirely, consistent with a separately configured network stack talking directly to its own backend. Hotel images come from a well-known third-party hotel-booking CDN, so this is very likely a separate white-labeled partner integration with its own authentication and its own network stack — cracking it would be a second reverse-engineering effort on the scale of the original, not a variation on the signing scheme already solved here.
  • "Events & shows" (concerts, festivals, exhibitions) isn't covered. Tapping it in the app opens an embedded WebView loading the public klook.com website rather than calling a signed app API — confirmed live, no API request fires at all. That's a fundamentally different integration (scraping a public webpage vs. wrapping the app's own API), so it's out of scope here.
  • Transport sub-verticals shown in the app's own UI as separate search forms (trains, buses, rail passes, metro passes, car charters, ferries, cruises) turned out, on inspection, to just be pre-filtered activity listings — already fully covered by /search with the right main_category/branch_category, not separate systems needing their own endpoints. Only car rentals and airport transfers turned out to be genuinely separate booking flows, and both are covered above.
  • The Trips tab's chatbot trip-planner (beyond the inspiration carousel) requires login — the only guest-reachable part is /trips/inspiration; typing a preference/question into a planner UI wasn't reachable without logging in. A related feature-flag endpoint (an in-destination AI chat entry point) was found and confirmed live, but returned show_entrance: false for this session — a real endpoint currently gated off, not wrapped since it returns no usable content in that state.
  • Home feed cards were spot-checked for non-activity content and found to be activities only — no separate article/blog-detail endpoint exists; every card on every rail already covered by /home/feed is a bookable activity, confirmed live while scrolling multiple rails.
  • This is an unofficial, reverse-engineered integration, not affiliated with or endorsed by Klook Travel Technology Limited. Behavior may change if Klook changes its API or signing scheme.

Found a bug or have a feature request? Use the Issues tab on this Actor's page.