# Short-Term Rental Multi-Channel iCal Double-Booking (`kingii98/short-term-rental-multi-channel-ical-double-booking`) Actor

Compare the iCal exports of one rental unit. Report double bookings and unmirrored blocks.

- **URL**: https://apify.com/kingii98/short-term-rental-multi-channel-ical-double-booking.md
- **Developed by:** [kingii98](https://apify.com/kingii98) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.00 / 1,000 run starteds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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

## Rental iCal Double-Booking Finder

This Actor compares the iCal exports of one rental unit. It reports two kinds of conflict:

- `overlap`: two feeds both hold a reservation for the same dates. An event whose summary has "not available", "blocked", or "closed" is a block, not a reservation.
- `unmirrored_block`: one feed blocks dates that start soon, and another feed of the same listing has no block for them.

The Actor uses HTTP GET only. It uses no login, browser, proxy, or paid API. Use only export URLs that you are authorized to use. The platform terms for automated use of export URLs were not checked. This is an open risk.

### Input

| Field | Default | Limit |
| --- | --- | --- |
| `listings` | one demo listing | 100 listings. Each has `listing_id` and 2 to 10 feeds (`label`, `url`). |
| `secret_listings` | empty | Optional secret JSON text. Same shape as `listings`. |
| `window_days` | 180 | 1 to 540 |
| `min_overlap_nights` | 1 | 1 to 540 |
| `urgent_days` | 14 | 0 to 540 |

The default listing uses two public holiday feeds. A run with empty input uses these defaults and succeeds. The feed URLs contain access tokens. The Actor never writes a URL to the dataset or to the log. The input schema cannot mark the nested `url` field as secret. So put real export URLs in `secret_listings`. This field is a JSON string with the same shape as `listings`. The platform hides its value. If it is set, the Actor ignores `listings`.

### Output

The dataset has two row types.

Conflict row: `type` = `conflict`, `listing_id`, `conflict_type`, `feeds` (labels), `event_uids`, `start_date`, `end_date`, `nights`, `days_until_start`.

Summary row (one for each listing): `type` = `summary`, `listing_id`, `feeds_fetched`, `feeds_failed` (label, HTTP status, error), `conflict_count`, `listing_clear`. `listing_clear` is true only if no feed failed and no conflict was found. A listing with invalid input gets a summary row with an `error` field.

A failed feed, an unreachable target, or zero findings never fails the run. The status message states the result.

### Pay-per-event pricing

| Event | Charged | Count |
| --- | --- | --- |
| `run-started` | Once at the start of each run. | 1 |
| `listing-checked` | For each listing with valid input, also if a feed failed. | 1 for each listing |
| `conflict-found` | For each overlap or unmirrored block reported. | 1 for each conflict row |

The Actor stops when the maximum total charge is reached.

### Limits and safety

- Feed URLs must use http or https. The Actor rejects localhost, private, and reserved addresses, also after DNS resolution and after each redirect.
- Maximum 3 redirects, 15 s timeout for each network operation, 30 s total for each feed, 1 MB for each feed, 10 parallel fetches.
- The parser does not expand recurring events (`RRULE`). It ignores cancelled events.
- An event end date is the checkout day. Back-to-back stays are not an overlap.

### Demo run cost

The default input uses the same holiday feed twice. Each holiday in the window gives one `overlap` row. Each row costs USD 0.02.

### Development

```
uv run pytest
uv run ruff check .
```

# Actor input Schema

## `listings` (type: `array`):

Up to 100 listings. Each listing has a listing\_id and 2 to 10 feeds. Each feed has a label and an iCal url. Use only export URLs that you are authorized to use.

## `secret_listings` (type: `string`):

Optional. A JSON array with the same shape as Listings. Use this field for real export URLs, because the platform hides its value. If set, the Actor ignores Listings.

## `window_days` (type: `integer`):

Days forward from today to compare.

## `min_overlap_nights` (type: `integer`):

Report an overlap only if it has at least this number of nights.

## `urgent_days` (type: `integer`):

Report an unmirrored block only if it starts within this number of days.

## Actor input object example

```json
{
  "listings": [
    {
      "listing_id": "demo-unit",
      "feeds": [
        {
          "label": "public-holidays-a",
          "url": "https://www.thunderbird.net/media/caldata/autogen/UnitedStatesHolidays.ics"
        },
        {
          "label": "public-holidays-b",
          "url": "https://www.thunderbird.net/media/caldata/autogen/UnitedStatesHolidays.ics"
        }
      ]
    }
  ],
  "window_days": 180,
  "min_overlap_nights": 1,
  "urgent_days": 14
}
```

# Actor output Schema

## `conflicts` (type: `string`):

No description

## `summaries` (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 = {
    "listings": [
        {
            "listing_id": "demo-unit",
            "feeds": [
                {
                    "label": "public-holidays-a",
                    "url": "https://www.thunderbird.net/media/caldata/autogen/UnitedStatesHolidays.ics"
                },
                {
                    "label": "public-holidays-b",
                    "url": "https://www.thunderbird.net/media/caldata/autogen/UnitedStatesHolidays.ics"
                }
            ]
        }
    ],
    "window_days": 180,
    "min_overlap_nights": 1,
    "urgent_days": 14
};

// Run the Actor and wait for it to finish
const run = await client.actor("kingii98/short-term-rental-multi-channel-ical-double-booking").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 = {
    "listings": [{
            "listing_id": "demo-unit",
            "feeds": [
                {
                    "label": "public-holidays-a",
                    "url": "https://www.thunderbird.net/media/caldata/autogen/UnitedStatesHolidays.ics",
                },
                {
                    "label": "public-holidays-b",
                    "url": "https://www.thunderbird.net/media/caldata/autogen/UnitedStatesHolidays.ics",
                },
            ],
        }],
    "window_days": 180,
    "min_overlap_nights": 1,
    "urgent_days": 14,
}

# Run the Actor and wait for it to finish
run = client.actor("kingii98/short-term-rental-multi-channel-ical-double-booking").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 '{
  "listings": [
    {
      "listing_id": "demo-unit",
      "feeds": [
        {
          "label": "public-holidays-a",
          "url": "https://www.thunderbird.net/media/caldata/autogen/UnitedStatesHolidays.ics"
        },
        {
          "label": "public-holidays-b",
          "url": "https://www.thunderbird.net/media/caldata/autogen/UnitedStatesHolidays.ics"
        }
      ]
    }
  ],
  "window_days": 180,
  "min_overlap_nights": 1,
  "urgent_days": 14
}' |
apify call kingii98/short-term-rental-multi-channel-ical-double-booking --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,kingii98/short-term-rental-multi-channel-ical-double-booking"
        }
    }
}
```

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/WMYqB2OJjO8DqgpUs/builds/uTzPBnbwJ04BgIphC/openapi.json
