# KKday Product Availability & Prices (`boncross/kkday-availability`) Actor

Scrape KKday availability and prices for any product. Just add a product URL and get every package, bookable date, time slot and ticket type with its own price, plus closed slots, sold-out flags and the sale window. Track price changes between runs. $0.50 per 1,000 slots.

- **URL**: https://apify.com/boncross/kkday-availability.md
- **Developed by:** [BonCross API](https://apify.com/boncross) (community)
- **Categories:** Travel
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.35 / 1,000 slot-listeds

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 Product Availability & Prices

### What is KKday Product Availability & Prices?

What does this tour cost on the 14th, and can you still book it? A KKday product page answers with a single "from" price. KKday Product Availability & Prices returns the booking calendar underneath: each package, date, time slot and ticket type as its own row, with the price for that slot and whether it's still on sale. Paste a product link and it finds the packages for you.

KKday scrapers usually stop at listings and reviews. This is the one to use when the question is "what does it cost on the 14th, and is it still on sale?". For finding products, the product record or the reviews, see the three sibling actors at the bottom of this page.

What you're getting:

- **The price of a slot, not of a product.** One Bangkok floating market tour returned 164 slots across two packages on 7 October 2026, all 164 with a price.
- **Open and closed, slot by slot.** In that run 120 slots were bookable and 44 weren't: the Monday-to-Thursday dates of a package that runs Friday to Sunday.
- **Prices on flat-priced products too.** Across 13 varied products, 342 of 348 slots came back priced. The other 6 were closed slots.
- **A price memory.** Name a store and each later run tells you what a slot cost last time and how far it moved. Unchanged slots can be left out, and they're free.
- **$0.50 per 1,000 slots** on the Free plan and $0.35 on Business. That's the whole price, with proxy usage and platform usage included.

**BonCross Verified** means we tested this actor against the live site before release and kept the record:

- 23 of 23 live checks on its inputs and filters passed on 8 October 2026
- 13 products run on 7 October 2026, tickets, tours, a show and an eSIM: 348 slots, 342 of them priced, 2 error rows
- Charges matched the rows: 30 packages, 348 slots

### What data is in each row?

| | |
|---|---|
| 📅 Date | ⏰ Time slot |
| 💵 Price for that slot | Ticket type name and id |
| 📦 Package name and id | Item id |
| ✅ Bookable flag | Sold-out or closed flag |
| Sale window start | Sale window end |
| Crossed-out price | Discount flag |
| 💱 Currency | 📈 Price change since your last run |
| Product name and id | 🔗 Product link |
| Places left, where KKday publishes a count | Time of the reading |

### How to scrape KKday availability

You don't need to write any code. It's a form, and the result opens as a table.

1. [Sign up for Apify](https://console.apify.com/sign-up). It's free.
2. Open [KKday Product Availability & Prices](https://apify.com/boncross/kkday-availability).
3. Paste one or more KKday product links into "Products".
4. Pick a currency. If you only care about the next few weeks, set "Days ahead".
5. Press Start, then download the slots as Excel, CSV or JSON.

### ⬇️ Input

All it needs is a product link. You can also give a product slug, or an exact package and item pair written as `package:item` when you already know which package you're watching.

Left alone, a run reads every package (up to 10 per product) across the product's whole sale window, in US dollars. For a busy timed ticket that can be thousands of rows, so set a date range or "Days ahead" unless you really want all of it.

There are limits at three levels. "Max packages per product" (default 10) caps how many packages are followed. "Maximum slots per package" and "Max dates per package" cap what each package returns: three packages with a slot limit of 5 gives you up to 15 rows. "Maximum results for the whole run" is the ceiling across all of it, and nothing past it is fetched or charged for.

Long lists of links go in "Start URLs", which can load them from a file, a Google Sheet or a remote list. Both inputs are combined.

#### Every package of one tour, whole sale window

```json
{
  "products": ["https://www.kkday.com/en/product/152911-floating-market-day-tour-from-bangkok-thailand"],
  "currency": "USD"
}
```

#### Only what you can still book in the next 45 days

```json
{
  "products": [
    "https://www.kkday.com/en/product/8332-taipei-101-observatory-ticket",
    "https://www.kkday.com/en/product/2358-nanta-show-tickets-in-seoul"
  ],
  "daysAhead": 45,
  "onlyAvailable": true,
  "currency": "USD"
}
```

#### One known package, watched daily for price moves

```json
{
  "products": ["1946312:1692089"],
  "priceStoreName": "kkday-prices",
  "onlyNewOrChanged": true,
  "currency": "THB"
}
```

| Field | Notes |
|---|---|
| `products` | Product links, slugs, or `package:item` pairs. |
| `startUrls` | The same links, loaded in bulk. |
| `maxPackagesPerProduct` | Packages followed per product, 1 to 50. Default 10. |
| `maxSlotsPerPackage` | Rows per package. Default 0, no limit. |
| `maxItems` | Rows for the whole run. Default 0, no limit. |
| `dateFrom`, `dateTo` | `YYYY-MM-DD`. Empty takes the whole sale window. |
| `daysAhead` | Keep the next N days, up to 400. Used only when `dateFrom` is empty. Default 0. |
| `maxDates` | The first N dates of each package. Default 0, no limit. |
| `onlyAvailable` | Drops closed and sold-out slots. Dropped rows are free. Off by default. |
| `currency` | An ISO code KKday quotes. Default `USD`. |
| `priceStoreName` | The store that remembers the last price per slot. |
| `onlyNewOrChanged` | Returns only slots that are new or whose price moved. Needs a price store. Off by default. |
| `delayMs`, `maxConcurrency` | Pacing, 1200 and 4 by default. See below. |

#### For developers

- The actor reads each package's sale window first and stays inside it. Dates outside the window don't exist on KKday, so they produce no rows.
- `maxDates` counts per package. A product with three packages and `maxDates: 2` returned 5 distinct dates in one measurement, not 2.
- With `maxSlotsPerPackage` or `maxDates` set, the dates past the cap aren't requested at all.
- Row `id` is built from the item id, date, time slot and ticket type id. KKday has no slot id of its own.
- `maxConcurrency` (default 4, from 1 to 8) is how many requests run at once. `delayMs` (default 1200, up to 10,000) is the pause before each one. These are the fastest settings that ran clean. We'd only lower the first or raise the second if a run reports errors.
- If Apify moves a run to another server part-way, it carries on where it stopped. Rows already delivered aren't written or charged again.
- Rows are saved as they arrive. A run that hits its time limit keeps what it collected.

### ⬆️ Output

You get one row per date, time slot and ticket type. This one is the first row of the Bangkok run in the first example, exactly as it arrived on 7 October 2026:

```json
{
  "input": "https://www.kkday.com/en/product/152911-floating-market-day-tour-from-bangkok-thailand",
  "id": "1692089-2026-10-09-fullday-896e81f21c46ab2fb33268ad0128aee6",
  "url": "https://www.kkday.com/en/product/152911-floating-market-day-tour-from-bangkok-thailand",
  "productId": "152911",
  "productName": "Floating Market Day Tour from Bangkok: Damnoen Saduak, Maeklong Market & Amphawa Fireflies Cruise | Thailand",
  "packageId": "1946312",
  "packageName": "Maeklong Market and Damnoen Saduak Market Day Tour",
  "itemId": "1692089",
  "date": "2026-10-09",
  "timeSlot": "fullday",
  "skuId": "896e81f21c46ab2fb33268ad0128aee6",
  "skuName": "Not Limited",
  "price": 25.96,
  "priceListed": null,
  "isAvailable": true,
  "isSoldOut": false,
  "isDiscounted": false,
  "stock": null,
  "currency": "USD",
  "saleWindowFrom": "2026-10-09",
  "saleWindowTo": "2026-12-31",
  "previousPrice": null,
  "previousScrapedAt": null,
  "priceChange": null,
  "priceChangePct": null,
  "scrapedAt": "2026-10-07T20:28:29.914Z"
}
```

A few columns worth a note:

| Column | Meaning |
|---|---|
| `timeSlot` | A clock time such as `10:30` on timed tickets (Taipei 101 comes back in half-hour slots), `fullday` on a day product, and empty on a date with no session. |
| `skuName` | The ticket type, from the product's own option labels. Some are as plain as "Not Limited". |
| `isAvailable`, `isSoldOut` | Whether you can book this ticket type on this date and time. |
| `priceListed`, `isDiscounted` | KKday's crossed-out price, when it shows one. |
| `stock` | Places left for that date and time. Filled only where KKday publishes a count. |
| `saleWindowFrom`, `saleWindowTo` | The package's own sale window. Packages of one product can differ. |
| `previousPrice`, `priceChange`, `priceChangePct` | Filled from your price store on the second run onwards. |

### How much does it cost to track KKday availability?

| Event | Free plan | Starter | Scale | Business |
|---|---|---|---|---|
| `actor-start`, per run | $0.002 | $0.002 | $0.002 | $0.002 |
| `product-resolved`, per package | $0.002 | $0.002 | $0.002 | $0.002 |
| `product-checked`, per package KKday answered as not existing (the row says so), instead of `product-resolved` | $0.0005 | $0.0005 | $0.0005 | $0.0005 |
| `slot-listed`, per row returned | $0.0005 | $0.00045 | $0.0004 | $0.00035 |

Enterprise plans pay the Business price.

**$0.50 per 1,000 slots** on the Free plan, $0.35 on Business. There's nothing on top: proxy usage and platform usage are included. Rows a filter removes and unchanged rows in a price watch are free.

- The Bangkok tour above, 2 packages and 164 slots: $0.002 + 2 × $0.002 + 164 × $0.0005 = **$0.088** ($0.063 on Business).
- A product with 6 packages and 1,822 slots: $0.002 + 6 × $0.002 + 1,822 × $0.0005 = **$0.925** ($0.652 on Business).
- A daily watch on one package that reports 40 changed slots: $0.002 + $0.002 + 40 × $0.0005 = **$0.024** ($0.018 on Business).

On Apify's Free plan the $5 of monthly credits covers about 56 runs the size of the Bangkok one, or about 9,800 slots on larger products.

### How many slots can you get, and how fast?

These are whole runs on Apify, start-up included, measured on 2 and 3 October 2026.

| Run | Rows | Time | Price on the Free plan |
|---|---|---|---|
| One product, 2 packages, first 5 slots each | 10 | 11.0 s | $0.011 |
| Three products, 7 packages, next 60 days | 3,488 | 16.0 to 19.6 s | $1.760 |
| Ten products, 24 packages, next 30 days | 1,937 | 31 s | $1.019 |

KKday is read at a steady pace, about three seconds a product for a month of dates. A one-hour run fits roughly 1,000 products. For a longer list, raise the run's timeout or split the list.

How many rows a product returns depends on its sale window and how finely it's sliced. In one September run of six packages, three were on sale from 15 September to 12 November and the other three from 15 November to 20 December.

### Run a daily price watch

This is the setup we'd recommend. Give "Price memory store" a name, switch on "Only new or changed", and put the run on an Apify Schedule. Each morning you get only the slots whose price moved or that weren't there before, with the old price beside the new one. A webhook can post those rows to Slack or append them to a Google Sheet.

From code, `POST https://api.apify.com/v2/acts/boncross~kkday-availability/run-sync-get-dataset-items` with the input JSON as the body returns the slots in one call. Through the Apify MCP server (`mcp.apify.com`) an AI agent sees the actor as the tool `boncross/kkday-availability`, so it can answer "is the Taipei 101 ticket bookable on Saturday afternoon, and for how much?" from live rows.

### FAQ

#### Do I need proxies or a KKday account?

Neither. There's nothing to set up, and proxy usage is already in the per-slot price.

#### Do I have to know the package id?

Paste the product link and you get all of its packages, each with its name and id. Once you know which one you care about, you can pass `package:item` and pay for that package alone.

#### Does it tell me how many places are left?

Sometimes. `stock` is filled where KKday publishes a count for the date and time, and most products don't publish one: none of the 12 products we checked on 7 October 2026 did, while a timed ticket and a day tour measured earlier had it on every sale day. What you always get is whether the slot is open or closed. The count belongs to the date and time, so a ticket type can be closed while places remain for others.

#### Why do packages of the same product cover different dates?

Each package has its own sale window, and KKday sells nothing outside it. Both ends of the window are on each row, so you can see the difference in the data.

#### Why is a slot closed on a day the tour seems to run?

Check the package name first. The "Fri-Sun Only" package in the Bangkok example comes back as not bookable from Monday to Thursday, which is right. Closed rows are the answer to "can I book this?", and "Only bookable slots" drops them at no charge if you don't want them.

#### How do I get prices in my own currency?

Type its ISO code in "Currency". Prices are quoted in it and the code is on each row. The default is USD.

#### What happens if a link is wrong or a product has gone?

You get a row that says what happened and what to check. A package and item pair KKday says doesn't exist is billed the $0.0005 `product-checked` fee. A product that couldn't be reached costs nothing. A package with nothing on sale in your dates gets one row saying so: it pays its `product-resolved` fee and the row is free.

#### What can't it do?

- It can't report dates outside a package's sale window. KKday doesn't sell them yet.
- Places left are there only for the minority of products where KKday publishes a count. Private tours and days not on sale have none.
- "Max dates per package" keeps the first N dates of each package, so a product with several packages can return more than N distinct dates in total.
- Ticket type names are KKday's own option labels and aren't tidied.
- It doesn't describe the product. Use KKday Product Details Scraper for the description, packages and rating.

#### Is it legal to scrape KKday?

The actor reads only what KKday shows any visitor who isn't logged in, and prices and dates aren't personal data. Scraping public data is generally lawful, but you're responsible for how you use it and for respecting KKday's terms and the law where you are.

### More from BonCross

- [Klook Activity Details Scraper](https://apify.com/boncross/klook-activity-details): Full Klook activity records.
- [Klook Activity Availability & Prices](https://apify.com/boncross/klook-availability): Klook availability and prices.
- [Klook Activity Reviews Scraper](https://apify.com/boncross/klook-reviews): Every review Klook shows for an activity.
- [Trip.com Attraction Reviews Scraper](https://apify.com/boncross/tripcom-reviews): Trip.com reviews for any attraction.
- [Trip.com Hotel Search Scraper](https://apify.com/boncross/tripcom-hotel-search): Every hotel Trip.com lists for a destination and your dates.
- [Accor Hotel Details Scraper](https://apify.com/boncross/accor-hotel-details): Full Accor hotel records.

### Your feedback

If a price doesn't match what KKday shows for that date, or a package is missing, tell us on the Issues tab in Apify Console with the product link. We read every issue and reply there.

# Changelog

This Actor's version history is a separate document: https://apify.com/boncross/kkday-availability/changelog.md

# Actor input Schema

## `products` (type: `array`):

KKday product URLs such as https://www.kkday.com/en/product/152911-... , product slugs, or an exact package and item pair written as pkgOid:itemOid.

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

KKday page addresses, with the option to load them in bulk from a file, a Google Sheet or a remote list. Use this when you have more links than you want to paste. Combined with anything in the list above.

## `maxPackagesPerProduct` (type: `integer`):

A product page can list many packages. Cap how many are followed.

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

A run-wide ceiling across every target. The per-target limit above applies to each one separately, so ten targets at 50 each returns 500; set this to cap the total. Zero means no run-wide limit. Rows that report an input the actor could not resolve are not counted here and are never charged for.

## `maxSlotsPerPackage` (type: `integer`):

Stop after this many rows per package. 0 means no limit. Named per PACKAGE because that is what it counts: a product with three packages and a limit of 5 returns up to 15 rows, so use the run-wide limit above to bound a run.

## `dateFrom` (type: `string`):

Earliest date to keep, as YYYY-MM-DD. Leave empty to take the whole sale window.

## `dateTo` (type: `string`):

Latest date to keep, as YYYY-MM-DD. Leave empty to take the whole sale window.

## `daysAhead` (type: `integer`):

Keep only dates within this many days from today. Only used when From date is empty.

## `maxDates` (type: `integer`):

Keep only the first N dates of each package. 0 means no limit. Measured: a product with three packages and N=2 returns 5 distinct dates, not 2.

## `onlyAvailable` (type: `boolean`):

Drop closed and sold-out rows. Filtered rows are free.

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

ISO currency code. Prices come back quoted in it. Default USD.

## `priceStoreName` (type: `string`):

Name a key-value store and each run remembers the last price per slot, so rows carry the change since the previous run.

## `onlyNewOrChanged` (type: `boolean`):

Return only slots whose price moved since the last run. Needs a price memory store.

## `delayMs` (type: `integer`):

Pause between calls.

## `maxConcurrency` (type: `integer`):

How many requests run at once, each one paced by the delay above. The default is the fastest level measured clean; lower it only if a run reports refusals or errors.

## Actor input object example

```json
{
  "products": [
    "https://www.kkday.com/en/product/152911-floating-market-day-tour-from-bangkok-thailand"
  ],
  "startUrls": [],
  "maxPackagesPerProduct": 10,
  "maxItems": 0,
  "maxSlotsPerPackage": 0,
  "dateFrom": "",
  "dateTo": "",
  "daysAhead": 0,
  "maxDates": 0,
  "onlyAvailable": false,
  "currency": "USD",
  "priceStoreName": "",
  "onlyNewOrChanged": false,
  "delayMs": 1200,
  "maxConcurrency": 4
}
```

# Actor output Schema

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

KKday availability: every row the run returned.

## `stats` (type: `string`):

Counts for the run: what was resolved, fetched, filtered and returned.

# 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 = {
    "products": [
        "https://www.kkday.com/en/product/152911-floating-market-day-tour-from-bangkok-thailand"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("boncross/kkday-availability").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 = { "products": ["https://www.kkday.com/en/product/152911-floating-market-day-tour-from-bangkok-thailand"] }

# Run the Actor and wait for it to finish
run = client.actor("boncross/kkday-availability").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 '{
  "products": [
    "https://www.kkday.com/en/product/152911-floating-market-day-tour-from-bangkok-thailand"
  ]
}' |
apify call boncross/kkday-availability --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,boncross/kkday-availability"
        }
    }
}
```

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/8xI99oRz6Wh8ujGUZ/builds/n4Znxq4bCDQ33IbRY/openapi.json
