KKday Scraper - Tours, Activities & Prices avatar

KKday Scraper - Tours, Activities & Prices

Pricing

from $1.70 / 1,000 activity records

Go to Apify Store
KKday Scraper - Tours, Activities & Prices

KKday Scraper - Tours, Activities & Prices

Scrape KKday travel activities by keyword, city or URL. Every row carries prices, discount, ratings, booking counts, destinations and images. Full details (description, address, coordinates, price tiers) are one toggle away, with recurring change tracking built in.

Pricing

from $1.70 / 1,000 activity records

Rating

0.0

(0)

Developer

Abot API

Abot API

Maintained by Community

Actor stats

1

Bookmarked

2

Total users

1

Monthly active users

13 days ago

Last modified

Categories

Features

Limited permissions

Share

KKday Scraper: Activity Prices, Ratings, Details & Change Tracking

KKday Scraper turns KKday's travel activities catalog into structured data. Search by keyword or paste KKday links to collect tours, attractions and experiences with their price range, original price, discount, rating, review count, booking-demand count, destinations and images. Fetch activity details (on by default) adds the full description, meeting address, coordinates and complete price-tier list for each activity. Schedule recurring runs with Incremental monitoring to get only new and changed activities, then export to JSON, CSV or Excel, or pull the results straight into your app through the API.

Why This Scraper?

  • Two ways to find activities. Search by keyword, or paste KKday links: individual activity pages or keyword search-result pages, which keep paging forward from wherever you pasted them.
  • Rich listing data out of the box. Price range, original price, discount percent, rating, review count, booking-demand count, destinations, tags and a cover image come straight from the search results, with no extra toggle needed.
  • Full activity detail on demand. Fetch activity details, on by default, adds the long description, meeting address, coordinates and the complete price-tier list, billed only for records it actually reaches and saves.
  • Availability calendar on demand. Turn on Include availability calendar to add, for every package of an activity, each date's lowest price and whether the date can be booked, for a window you choose (up to 180 days). Read-only and not billed as a separate event.
  • Language and currency control. Search mode reads result pages in the site language and currency you choose; pasted links are read exactly as pasted.
  • Built for schedules. Incremental monitoring tracks one search or URL set across recurring runs and returns only new and changed activities, so unchanged ones are not billed again; Resume skips activities already saved in a previous run or dataset.
  • A refused run fails loudly instead of returning an empty dataset, so "nothing matched this search" is never confused with "the connection was refused."

Use Cases

  • Travel content and comparison sites: build destination pages with current activity prices, discounts and ratings.
  • Deal and price monitoring teams: schedule recurring runs with Incremental monitoring to catch new discounts or price changes without re-billing unchanged activities.
  • Travel agencies and OTAs: enrich a curated list of activity URLs with full descriptions, coordinates and price tiers for repackaging.
  • Market and competitor researchers: track booking-demand counts, ratings and supplier names across a destination's activities over time.
  • Trip-planning tools and chatbots: feed activity search results, coordinates and price tiers into your own app or assistant.

Data You Get

Sample shape: values are illustrative placeholders, not from a live record.

FieldExample
id"20000001"
name"Sample Harbor Sunset Cruise"
urlKKday link to the activity (always the English page)
minPrice / maxPrice24.5 / 89.0
officialPrice29.0
currency"USD"
discountPercent15.5
ratingStar / ratingCount4.7 / 1204
bookedCount"8K+"
destinations["Sample Harbor"]
displayTags["Instant Confirmation"]
supplierName"Sample Supplier"
introductionshort teaser line from the search result
imageUrlcover image link (imageUrls carries the full gallery when Fetch activity details is on)
address / latitude / longitude"1 Sample Pier, Sample City, ST" / 25.0 / 121.5 (when Fetch activity details is on)
descriptionfull activity description (when Fetch activity details is on)
priceTiers[{ "price": 24.5, "currency": "USD", "name": "Adult" }] (when Fetch activity details is on; each tier carries its own currency)
availability[{ "packageId": "1001", "packageName": "Adult ticket", "date": "2026-12-01", "available": true, "price": 24.5, "basePrice": 24.5, "saleWindowFrom": "2026-11-01", "saleWindowTo": "2027-10-31" }] (only when Include availability calendar is on; a date with no price is not bookable, available is false and price is null; with Time slots on each entry also carries itemId, timeSlots, soldOut, discounted and skus, one per ticket type and slot: { "skuId", "skuName": "Adult", "timeSlot": "10:00", "price", "basePrice", "remainingQty" }; remainingQty is KKday's published remaining count for that slot, null when none is shown; basePrice is the crossed-out price)
availabilityStatus"complete" (also "unavailable", "skipped_breaker"; only when Include availability calendar is on)
detailStatus"complete" (also "listing_only", "unavailable", "skipped_breaker")
changeType"NEW" (every row is NEW with Incremental monitoring off; with it on, also "UPDATED", "UNCHANGED", "EXPIRED")
previousMinPrice / minPriceChange / minPriceChangePercent / previousSeenAt24.5 / -2.0 / -7.5 / ISO timestamp (Incremental monitoring only, on activities the baseline already knew; empty on new activities and never a reason for a row to count as changed)
previousPrice / priceChange / priceChangePcton each availability entry and each skus cell: the price this exact package and date (and ticket type and slot) had on the previous Incremental run, and the move since (Incremental monitoring with Include availability calendar only; absent on a first run and on dates the previous run did not price; never a reason for a row to count as changed)
changedFields["minPrice", "ratingCount"] (empty with Incremental monitoring off)
firstSeenAt / lastSeenAt / scrapedAtISO timestamps (all equal this run's read time with Incremental monitoring off)

Activities read from a pasted single-activity URL do not carry minPrice, maxPrice, officialPrice, discountPercent, ratingStar, ratingCount, bookedCount, destinations, displayTags, supplierName or introduction: KKday only exposes those on search-result pages, not on the activity page itself. On those rows currency simply repeats your Currency input, so read the price currency from each priceTiers entry instead. Enriched descriptions and addresses come from the English activity page whatever Language you pick.

How to Use

  1. Pick a mode: search (find activities by keyword) or url (read pasted KKday links).
  2. For search mode, set the query, language and currency. For URL mode, paste activity pages or keyword search-result pages (a link written with q= is read as a keyword search).
  3. Keep Fetch activity details on (the default) for the full description, address, coordinates and price tiers, or turn it off for listing fields only, and set Max items and Max search pages to control run size and cost.
  4. Click Start, then download the dataset as JSON, CSV or Excel, or read it through the API.

Search by keyword:

{
"mode": "search",
"query": "Kyoto",
"language": "en",
"currency": "USD",
"maxItems": 20
}

Paste an activity link, with full details:

{
"mode": "url",
"urls": ["https://www.kkday.com/en/product/20000001-sample-activity"],
"fetchDetails": true,
"maxItems": 1
}

Continue a pasted search page, capped to 3 pages:

{
"mode": "url",
"urls": ["https://www.kkday.com/en/product/productlist?q=Osaka"],
"maxItems": 30,
"maxPages": 3
}

Search in another language and currency:

{
"mode": "search",
"query": "Taipei",
"language": "zh-tw",
"currency": "TWD",
"maxItems": 10
}

Run it from your code

Python:

from apify_client import ApifyClient
client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("abotapi/kkday-com-scraper").call(run_input={"mode": "search", "query": "Kyoto", "maxItems": 20})
for activity in client.dataset(run["defaultDatasetId"]).iterate_items():
print(activity["name"], activity["minPrice"], activity["currency"])

JavaScript:

import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });
const run = await client.actor('abotapi/kkday-com-scraper').call({ mode: 'search', query: 'Kyoto', maxItems: 20 });
const { items } = await client.dataset(run.defaultDatasetId).listItems();

Or connect it to Make, Zapier, n8n, Google Sheets or webhooks from the Integrations tab.

Max items and Max search pages

Max items stops a run once that many activities have been returned, not scanned: a suppressed row (unchanged, with Emit unchanged off) never counts against it, so an activity ranked after a run of unchanged ones on the same result page is still reached and returned. Max search pages caps how many result pages one search, or one pasted search-result URL, will read; leave it at 0 for no page limit. A run that stops on either cap (including one whose returned rows exactly reach Max items), or a pasted URL that could not be read, never counts as a fully scanned run, so Emit expired never fires from it. On a fully scanned run, EXPIRED rows are added after the scan and are not limited by Max items.

Resume and recurring updates

  • Resume from run or dataset (resumeFromRunId) skips activity ids already saved by a previous run: paste its run id or dataset id.
  • Incremental monitoring (incrementalMode) tracks one search or URL set across recurring runs under a state key (set your own with stateKey, or let the actor derive one from mode, query or URLs, language and currency). Each activity is classified NEW, UPDATED (with changedFields naming what moved), or, only when you opt in, UNCHANGED (emitUnchanged, suppressed and unbilled otherwise) or EXPIRED (emitExpired, only once a run has fully read the tracked scope). With Incremental monitoring off, output is exactly as before.
  • Resume from run cannot be combined with Incremental monitoring once a baseline already exists for the same state key.
  • With Fetch activity details on, an activity whose detail page could not be read on one run comes back as UPDATED with detailStatus and the detail fields in changedFields, and is returned (and billed as a record) like any other update.

Send results into your apps (MCP connectors)

Current status: connector delivery is not active in this build. The connector fields below are accepted so saved inputs stay valid, but selecting a connector currently sends nothing; every result is in the Apify dataset only. The rest of this section describes the intended behavior.

The connector option is meant to pipe the scraped results into the apps you already use, via Model Context Protocol (MCP) connectors. This is an extra delivery step after the scrape: the Apify dataset is never changed.

What gets written to the connector: a condensed, human-readable summary of each record, not the full JSON. Each activity becomes one entry with a title and its key fields flattened to plain text. The complete record always stays in the Apify dataset.

  1. Authorize a connector once under Apify, Settings, Integrations (Notion, Linear, Airtable, or Apify).
  2. Select it in the "Pipe results into your apps" input field. If the picker is empty, you haven't authorized a connector yet.
  3. For Notion, also set notionParentPageUrl to the page where activity pages should be created.

The connection is mediated by Apify's MCP proxy, so this actor never sees your third-party credentials. Leave the field empty to skip.

Input Parameters

ParameterTypeDefaultDescription
modestringsearchsearch (find activities by keyword) or url (read pasted KKday links).
querystringTaipeiKeyword for activity search, such as a city, attraction or experience. Ignored in URL mode.
languagestringenSite language path for search mode, for example en or zh-tw. English pages are verified; other codes reuse the same layout. Pasted URLs keep their own language.
currencystringUSDThree-letter display currency. Search mode requests pages in it; pasted URLs are read as pasted, so put the currency in a pasted search link yourself.
sortBystringprecSearch order: prec (recommended), omdesc (most popular), sdesc (top rated), pasc (price low to high). Applies to search mode; a pasted results link keeps its own sort.
urlsarraysample search URL (.../en/product/productlist?q=Taipei)Activity pages (/product/<id>-<slug> or the id-only /product/<id>) or keyword search pages (/product/productlist). Other pages (city or destination pages, categories, home) are skipped with a warning. Search pages keep paging forward from the pasted page. The Console form also pre-fills example activity links; a run started through the API without urls reads only the sample search page.
fetchDetailsbooleantrueAdd description, meeting address, coordinates and price tiers to each activity. Billed only for records actually enriched and saved (detail-enrichment). Pasted single-activity links are always read from the activity page and carry this charge either way.
maxItemsinteger20Maximum number of records returned, from 1 to 500. The only cap that stops a run early by default.
maxPagesinteger0Maximum search pages to read per search. 0 means no page limit.
includeAvailabilitybooleanfalseAdd the per-package, per-date availability calendar to each activity. Read-only; it adds no billed event and does not make a row count as changed in Incremental monitoring. With Incremental monitoring on, the actor remembers each calendar price between runs so every date shows its own price move.
availabilityDateFromstringtodayFirst calendar date, YYYY-MM-DD. Past dates start today.
availabilityDaysinteger30Calendar length in days, 1 to 180.
availabilityOnlyAvailablebooleanfalseKeep only bookable dates.
availabilityTimeSlotsbooleanfalseAlso read, per package and date, the time slots, the sold-out and discounted flags and each ticket type's name, price and remaining count (timeSlots, soldOut, discounted, skus). One extra read per package, so runs are slower.
resumeFromRunIdstring(none)Skip activity ids already present in a previous run or dataset.
incrementalModebooleanfalseReturn only new or changed activities for the same state key.
stateKeystring(none)Name for shared recurring-run state. Leave empty to derive it from mode, query or URLs, language and currency.
emitUnchangedbooleanfalseReturn (and bill) activities unchanged since the previous run.
ignoreFieldsForChangesarray[]Field names (or dotted nested paths) that never count as a change; rows differing only in them stay UNCHANGED. Changing the list does not reset the baseline.
emitExpiredbooleanfalseReturn (and bill) activities present before but no longer found, marked EXPIRED. Only produced once a run has fully scanned the tracked scope.
mcpConnectorsarray(none)Optional: send a summary of each record to apps you authorized under Integrations. Not active in the current build.
notionParentPageUrlstring(none)Notion connector only: page under which activity pages are created. Not active in the current build.
maxNotifyListingsinteger50Cap on records written to each connector per run. Does not affect the dataset. Not active in the current build.
proxyConfigurationobjectApify Proxy (Residential, SG)Connection settings for KKday requests. Residential exits are tried first; when the site refuses them the actor switches to its backup connection automatically.

Output Example

Sample shape: values are illustrative placeholders, not from a live record.

{
"id": "20000001",
"name": "Sample Harbor Sunset Cruise",
"url": "https://www.kkday.com/en/product/20000001-sample-harbor-sunset-cruise",
"minPrice": 24.5,
"maxPrice": 89.0,
"officialPrice": 29.0,
"currency": "USD",
"discountPercent": 15.5,
"ratingStar": 4.7,
"ratingCount": 1204,
"bookedCount": "8K+",
"destinations": ["Sample Harbor"],
"displayTags": ["Instant Confirmation"],
"imageUrl": "https://example.com/img/sample.jpg",
"imageUrls": ["https://example.com/img/sample.jpg"],
"supplierName": "Sample Supplier",
"introduction": "A relaxed evening cruise around the fictional Sample Harbor.",
"address": "1 Sample Pier, Sample City, ST",
"latitude": 25.0,
"longitude": 121.5,
"description": "A fictional sunset cruise with narration and drinks included, used here only as a placeholder.",
"priceTiers": [{ "price": 24.5, "currency": "USD", "name": "Adult" }],
"detailStatus": "complete",
"changeType": "NEW",
"changedFields": [],
"firstSeenAt": "2026-01-01T00:00:00+00:00",
"lastSeenAt": "2026-01-01T00:00:00+00:00",
"scrapedAt": "2026-01-01T00:00:00+00:00"
}

Plan Requirement

KKday sits behind a verification challenge that plain datacenter connections do not pass, so the default proxy setting uses residential exits (Singapore by default); switch the country under Connection to match your market. If the selected connection is refused, the actor automatically steps down to a backup connection lane after the first refusal and locks onto whichever one starts serving content for the rest of the run.

A paid Apify plan is required to use the built-in connections: the FREE plan has no Apify proxy access, so on FREE you must supply your own proxy URLs. If you supply your own proxy URLs, the actor uses only those and never switches lanes. No KKday account is needed; only public catalog and activity pages are read.

FAQ

How much does it cost?

You pay per record returned, plus a one-time detail charge per record only when its activity page was actually read and the record saved: with Fetch activity details on, or for any pasted single-activity link (those are always read from the activity page). The Pricing tab shows the current rates. Use Max items to cap the cost of any run.

This actor collects only publicly available catalog and activity data, without logging into any account. You are responsible for how you use it: follow KKday's terms and the laws that apply to you, and get legal advice if you plan commercial redistribution. Prices and availability change constantly and are generally facts, but activity photos and descriptions may be subject to third-party rights.

Can I get only new or changed activities on a schedule?

Yes. Schedule the actor from the Schedules tab and turn on Incremental monitoring. Each run then returns only new and updated activities for the same state key, and unchanged ones are not billed unless you opt in with Emit unchanged.

Does Incremental monitoring flag an activity that disappeared and came back?

No. Activities are classified NEW, UPDATED or UNCHANGED, plus EXPIRED when Emit expired is on and a run fully scanned the tracked scope. An activity that vanishes and is later seen again is classified UPDATED or UNCHANGED the next time it is scanned, not as a separate reappeared state.

Why does a pasted activity URL return fewer fields than a search result?

KKday only publishes price range, discount, rating, review count, booking-demand count, destinations, tags and supplier name on its search-result pages, not on the activity page itself. Pasting a single activity link (or turning on Fetch activity details) still gets you the name, description, address, coordinates and price tiers, but those listing-only fields stay empty. To get both, search or paste a search-result URL with Fetch activity details on.

Why did my run fail instead of returning an empty dataset?

If the actor could not load a single page, the run stops with a clear message so "no activities matched" is never confused with "nothing could be read." Three messages exist, chosen from what the connection actually did:

  • "The proxy connection was refused for this account, so no page could be read. Your Apify plan does not include proxy access; a paid plan is required: https://apify.com/pricing. If you entered your own proxy URLs, check that their credentials are valid." appears when every connection attempt was refused by the proxy itself.
  • "No results: residential proxies are unavailable on this run (Apify free plans do not include them), and the backup connection could not load the requested KKday pages. Upgrade at https://apify.com/pricing or supply your own proxy URLs, then try again." appears when the proxy refused the default connection (a free plan), the backup connection was reachable, and KKday still served no usable page.
  • "Could not load any results page (the requests failed), so nothing was collected. This is a connection problem, not an empty search. Please try again later." appears for every other run that loaded no page, including a paid run where KKday challenges every connection (try again later).

When the default connection is refused by the proxy, a warning is also written once at the start of the run and the actor continues on its backup connection. If only some requests were rejected, or the run time nearly ran out, you still get what was found.

Can I use it with AI agents or MCP?

Yes. Call it through the Apify API, any Apify integration or an MCP client such as Apify's MCP server, and read the results from the dataset. The connector field for pushing results straight into Notion, Linear or Airtable is not active in the current build.

🔗 More scrapers you might like

Pair this actor with these related scrapers from the same team:

✈️ Viator Scraper
Collect Viator.com tour and activity results from destinations, categories, search pages...
🏨 Klook Scraper
Scrape Klook activities, tours, attractions and travel experiences from search pages or...
✈️ Pollstar Scraper
Scrape Pollstar upcoming concerts and tour dates: play date, venue with full address and...
🧩 Fever Scraper
Scrape Fever events by city, category, keyword or URL. Extract from-prices, venues, dates...
🧩 ASOS Scraper
Scrape ASOS by keyword, category or URL. Every row carries the selling price and the...
🛒 Tokopedia Scraper
Extract product data from Tokopedia, Indonesia’s largest marketplace. Search by keyword...

👉 Browse all abotapi scrapers

💬 Support

  • 🐞 Found a bug or a missing field? Open a ticket on the Issues tab. We usually reply within hours.
  • 🛠️ Need another site, extra fields or a private build? Email contact@abotapi.com.
  • ⭐ Enjoying it? A quick review on the actor page helps other users find it.