# KKday Scraper - Tours, Activities & Prices (`abotapi/kkday-com-scraper`) Actor

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.

- **URL**: https://apify.com/abotapi/kkday-com-scraper.md
- **Developed by:** [Abot API](https://apify.com/abotapi) (community)
- **Features:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 1 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.70 / 1,000 activity records

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## 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.

| Field | Example |
| --- | --- |
| `id` | `"20000001"` |
| `name` | `"Sample Harbor Sunset Cruise"` |
| `url` | KKday link to the activity (always the English page) |
| `minPrice` / `maxPrice` | `24.5` / `89.0` |
| `officialPrice` | `29.0` |
| `currency` | `"USD"` |
| `discountPercent` | `15.5` |
| `ratingStar` / `ratingCount` | `4.7` / `1204` |
| `bookedCount` | `"8K+"` |
| `destinations` | `["Sample Harbor"]` |
| `displayTags` | `["Instant Confirmation"]` |
| `supplierName` | `"Sample Supplier"` |
| `introduction` | short teaser line from the search result |
| `imageUrl` | cover 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) |
| `description` | full 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` / `previousSeenAt` | `24.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` / `priceChangePct` | on 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` / `scrapedAt` | ISO 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:**

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

**Paste an activity link, with full details:**

```json
{
  "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:**

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

**Search in another language and currency:**

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

#### Run it from your code

Python:

```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:

```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

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `mode` | string | `search` | `search` (find activities by keyword) or `url` (read pasted KKday links). |
| `query` | string | `Taipei` | Keyword for activity search, such as a city, attraction or experience. Ignored in URL mode. |
| `language` | string | `en` | Site 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. |
| `currency` | string | `USD` | Three-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. |
| `sortBy` | string | `prec` | Search 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. |
| `urls` | array | sample 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. |
| `fetchDetails` | boolean | `true` | Add 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. |
| `maxItems` | integer | `20` | Maximum number of records returned, from 1 to 500. The only cap that stops a run early by default. |
| `maxPages` | integer | `0` | Maximum search pages to read per search. `0` means no page limit. |
| `includeAvailability` | boolean | `false` | Add 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. |
| `availabilityDateFrom` | string | today | First calendar date, `YYYY-MM-DD`. Past dates start today. |
| `availabilityDays` | integer | `30` | Calendar length in days, 1 to 180. |
| `availabilityOnlyAvailable` | boolean | `false` | Keep only bookable dates. |
| `availabilityTimeSlots` | boolean | `false` | Also 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. |
| `resumeFromRunId` | string | (none) | Skip activity ids already present in a previous run or dataset. |
| `incrementalMode` | boolean | `false` | Return only new or changed activities for the same state key. |
| `stateKey` | string | (none) | Name for shared recurring-run state. Leave empty to derive it from mode, query or URLs, language and currency. |
| `emitUnchanged` | boolean | `false` | Return (and bill) activities unchanged since the previous run. |
| `ignoreFieldsForChanges` | array | `[]` | 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. |
| `emitExpired` | boolean | `false` | Return (and bill) activities present before but no longer found, marked `EXPIRED`. Only produced once a run has fully scanned the tracked scope. |
| `mcpConnectors` | array | (none) | Optional: send a summary of each record to apps you authorized under Integrations. Not active in the current build. |
| `notionParentPageUrl` | string | (none) | Notion connector only: page under which activity pages are created. Not active in the current build. |
| `maxNotifyListings` | integer | `50` | Cap on records written to each connector per run. Does not affect the dataset. Not active in the current build. |
| `proxyConfiguration` | object | Apify 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.

```json
{
  "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.

#### Is it legal to scrape KKday?

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:

<table>
<tr><td>✈️ <a href="https://apify.com/abotapi/viator-com-scraper"><b>Viator Scraper</b></a><br>Collect Viator.com tour and activity results from destinations, categories, search pages...</td><td>🏨 <a href="https://apify.com/abotapi/klook-activities-scraper"><b>Klook Scraper</b></a><br>Scrape Klook activities, tours, attractions and travel experiences from search pages or...</td></tr>
<tr><td>✈️ <a href="https://apify.com/abotapi/pollstar-concert-tour-scraper"><b>Pollstar Scraper</b></a><br>Scrape Pollstar upcoming concerts and tour dates: play date, venue with full address and...</td><td>🧩 <a href="https://apify.com/abotapi/fever-com-scraper"><b>Fever Scraper</b></a><br>Scrape Fever events by city, category, keyword or URL. Extract from-prices, venues, dates...</td></tr>
<tr><td>🧩 <a href="https://apify.com/abotapi/asos-com-scraper"><b>ASOS Scraper</b></a><br>Scrape ASOS by keyword, category or URL. Every row carries the selling price and the...</td><td>🛒 <a href="https://apify.com/abotapi/tokopedia-scraper"><b>Tokopedia Scraper</b></a><br>Extract product data from Tokopedia, Indonesia’s largest marketplace. Search by keyword...</td></tr>
</table>

👉 [Browse all abotapi scrapers](https://apify.com/abotapi)

### 💬 Support

- 🐞 **Found a bug or a missing field?** Open a ticket on the [Issues tab](https://apify.com/abotapi/kkday-com-scraper/issues/open). 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.

# Actor input Schema

## `mode` (type: `string`):

Search KKday by keyword or read pasted KKday URLs.

## `query` (type: `string`):

Keyword for activity search, such as a city, attraction or experience. Ignored in URL mode.

## `sortBy` (type: `string`):

Order of search results (search mode and pasted results links that carry no sort of their own). Recommended is the site's default ranking.

## `urls` (type: `array`):

Activity pages (/product/<id>-<slug>, or the id-only form /product/<id>, for example https://www.kkday.com/en/product/133300-shibuya-sky-observatory-e-ticket-tokyo) or keyword search pages (/product/productlist?q=...). Search pages keep paging forward from the pasted page. Other KKday pages (city or destination pages, categories, home) are skipped with a warning.

## `language` (type: `string`):

Site language path, for example en or zh-tw. English pages are verified; other codes reuse the same layout.

## `currency` (type: `string`):

Three-letter display currency applied in both search and URL modes, for example USD.

## `fetchDetails` (type: `boolean`):

Add description, meeting address, coordinates and price tiers to each activity. An extra charge applies only to records actually enriched and saved (detail-enrichment).

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

Maximum number of records returned. This is the only cap that stops a run early by default.

## `maxPages` (type: `integer`):

Maximum search pages to read per search. Zero means no page limit.

## `includeAvailability` (type: `boolean`):

Adds an `availability` list to each activity: one entry per package and date with the lowest price and whether the date can be booked.

## `availabilityDateFrom` (type: `string`):

First date to read, as YYYY-MM-DD. Leave empty to start today. Past dates are moved to today.

## `availabilityDays` (type: `integer`):

How many days to read from the start date (1-180).

## `availabilityOnlyAvailable` (type: `boolean`):

Keep only the dates that can be booked and drop sold-out or closed dates.

## `availabilityTimeSlots` (type: `boolean`):

Also read, per package and date, the bookable time slots, the sold-out and discounted flags and the price of each ticket type (SKU). One extra read per package, so runs are slower.

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

Apify Proxy settings used for KKday requests. Residential exits are tried first; when the site refuses them the actor switches to its backup connection automatically. Custom proxy URLs are used as-is.

## `resumeFromRunId` (type: `string`):

Skip activity IDs already present in a previous run or dataset. See Incremental monitoring for recurring runs.

## `incrementalMode` (type: `boolean`):

Return only new or changed activities for the same state key. Cannot be combined with Resume from run when a baseline already exists.

## `stateKey` (type: `string`):

Name for shared recurring-run state. Leave empty to derive it from mode, query or URLs, language and currency.

## `emitUnchanged` (type: `boolean`):

Return records that have not changed during incremental monitoring. Unchanged rows are billed as dataset items.

## `emitExpired` (type: `boolean`):

Off by default. Turn on to return records that were present in a previous run but are no longer found, marked EXPIRED. Only produced once a run has fully scanned the tracked scope: not when Max items capped the run, and not when a pasted URL could not be read. This returns, and bills, extra synthetic rows, so leave it off unless you need expiry tracking.

## `ignoreFieldsForChanges` (type: `array`):

Field names (or dotted nested paths such as priceTiers.price) that never count as a change in incremental monitoring. Rows that differ only in these fields stay UNCHANGED and are not billed. Changing this list does not reset the baseline.

## `mcpConnectors` (type: `array`):

Optionally send results into the apps you already use, via Model Context Protocol (MCP) connectors. Authorize one under Apify, Settings, API & Integrations, then select it here. Notion gets a page per record; other connectors get a best-effort write or digest. Each connector receives a condensed summary per record, not the full record; the complete record always stays in the dataset. Leave empty to skip; this never changes the dataset output. Supported: Notion (https://mcp.notion.com/mcp), Linear (https://mcp.linear.app/sse), Airtable (https://mcp.airtable.com/mcp), Apify (https://mcp.apify.com).

## `notionParentPageUrl` (type: `string`):

URL or id of the Notion page under which record pages are created. Required to enable the Notion export; ignored by other connectors.

## `maxNotifyListings` (type: `integer`):

Cap on records written to each connector per run. Does not affect the dataset.

## Actor input object example

```json
{
  "mode": "search",
  "query": "Taipei",
  "sortBy": "prec",
  "urls": [
    "https://www.kkday.com/en/product/productlist?q=Taipei",
    "https://www.kkday.com/en/product/133300-shibuya-sky-observatory-e-ticket-tokyo",
    "https://www.kkday.com/en/product/133300"
  ],
  "language": "en",
  "currency": "USD",
  "fetchDetails": true,
  "maxItems": 20,
  "maxPages": 0,
  "includeAvailability": false,
  "availabilityDays": 30,
  "availabilityOnlyAvailable": false,
  "availabilityTimeSlots": false,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "SG"
  },
  "incrementalMode": false,
  "emitUnchanged": false,
  "emitExpired": false,
  "ignoreFieldsForChanges": [],
  "maxNotifyListings": 50
}
```

# Actor output Schema

## `overview` (type: `string`):

No description

## `availability` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

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

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "mode": "search",
    "query": "Taipei",
    "sortBy": "prec",
    "urls": [
        "https://www.kkday.com/en/product/productlist?q=Taipei",
        "https://www.kkday.com/en/product/133300-shibuya-sky-observatory-e-ticket-tokyo",
        "https://www.kkday.com/en/product/133300"
    ],
    "language": "en",
    "currency": "USD",
    "fetchDetails": true,
    "maxItems": 20,
    "maxPages": 0,
    "includeAvailability": false,
    "availabilityDays": 30,
    "availabilityOnlyAvailable": false,
    "availabilityTimeSlots": false,
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ],
        "apifyProxyCountry": "SG"
    },
    "incrementalMode": false,
    "emitUnchanged": false,
    "emitExpired": false,
    "ignoreFieldsForChanges": [],
    "maxNotifyListings": 50
};

// Run the Actor and wait for it to finish
const run = await client.actor("abotapi/kkday-com-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 = {
    "mode": "search",
    "query": "Taipei",
    "sortBy": "prec",
    "urls": [
        "https://www.kkday.com/en/product/productlist?q=Taipei",
        "https://www.kkday.com/en/product/133300-shibuya-sky-observatory-e-ticket-tokyo",
        "https://www.kkday.com/en/product/133300",
    ],
    "language": "en",
    "currency": "USD",
    "fetchDetails": True,
    "maxItems": 20,
    "maxPages": 0,
    "includeAvailability": False,
    "availabilityDays": 30,
    "availabilityOnlyAvailable": False,
    "availabilityTimeSlots": False,
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
        "apifyProxyCountry": "SG",
    },
    "incrementalMode": False,
    "emitUnchanged": False,
    "emitExpired": False,
    "ignoreFieldsForChanges": [],
    "maxNotifyListings": 50,
}

# Run the Actor and wait for it to finish
run = client.actor("abotapi/kkday-com-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 '{
  "mode": "search",
  "query": "Taipei",
  "sortBy": "prec",
  "urls": [
    "https://www.kkday.com/en/product/productlist?q=Taipei",
    "https://www.kkday.com/en/product/133300-shibuya-sky-observatory-e-ticket-tokyo",
    "https://www.kkday.com/en/product/133300"
  ],
  "language": "en",
  "currency": "USD",
  "fetchDetails": true,
  "maxItems": 20,
  "maxPages": 0,
  "includeAvailability": false,
  "availabilityDays": 30,
  "availabilityOnlyAvailable": false,
  "availabilityTimeSlots": false,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "SG"
  },
  "incrementalMode": false,
  "emitUnchanged": false,
  "emitExpired": false,
  "ignoreFieldsForChanges": [],
  "maxNotifyListings": 50
}' |
apify call abotapi/kkday-com-scraper --silent --output-dataset

```

## MCP server setup

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