# ICS Calendar Change Monitor (`quietdatatools/ics-calendar-change-monitor`) Actor

Monitor public ICS calendars for new events, time and venue changes, recurrence edits and explicit cancellations. Persistent history, exact previous/current values, and cautious missing-event detection. $0.01 per successful check.

- **URL**: https://apify.com/quietdatatools/ics-calendar-change-monitor.md
- **Developed by:** [QuietDataTools](https://apify.com/quietdatatools) (community)
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$0.01 / successful calendar check

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

## ICS Calendar Change Monitor

Keep an event directory or automation workflow synchronized with a public ICS calendar. Reuse the same calendar URL and monitor name to get exact previous/current values when event times, titles, venues, links, statuses or recurring-series rules change.

Unlike a calendar parser or feed merger, this Actor keeps a baseline between runs and produces change records. It works on **VEVENT records**, identified by UID plus RECURRENCE-ID. Recurring masters and exception records remain separate; it does **not expand recurring events into occurrences**.

### Start

Run the default public UK bank-holiday calendar to inspect the output, then replace it with a public calendar you are authorized to monitor. Save and schedule the same input. The first successful check establishes a baseline; subsequent runs compare against the last successful check. Change monitorName to start independent history.

```json
{"calendarUrl":"https://www.gov.uk/bank-holidays/england-and-wales.ics","monitorName":"default","includeUnchanged":false,"allowEmpty":false}
```

### Change records

BASELINE means present on the first check, not newly created. NEW means newly observed. UPDATED has changedFields and exact previous/current objects. TIME_CHANGED, RECURRENCE_CHANGED, EXPLICIT_CANCELLATION and CANCELLATION_CLEARED are useful signals. An explicit cancellation requires source STATUS:CANCELLED; a missing record is **never called cancelled**.

MISSING_ONCE means absent on one complete check. NO_LONGER_OBSERVED requires two complete consecutive omissions and is emitted once. Feeds can age past events out or omit records for other reasons; absence does not prove cancellation. UNCHANGED is counted in OUTPUT and optionally returned as rows.

The OUTPUT key-value record always contains counts and timezoneDefinitionsChanged, even when the dataset has no changes. Use eventId for downstream deduplication; delivery is at least once.

Dates retain the source's value type and TZID. All-day end dates are exclusive. Floating times are not represented as UTC. This Actor reports source schedule definitions, rather than computing instants or validating calendar correctness. It ignores DTSTAMP, SEQUENCE, descriptions, attendees and organizers; timezone-definition edits appear in the summary. It does not send invitations, edit calendars or synchronize Google/Outlook accounts.

### Limits and history

One public HTTPS calendar per check, maximum 2 MB and 2,000 VEVENT records. Up to three redirects, each checked for public Internet addresses; no credentials, custom ports or private network destinations. Source requests time out after 20 seconds. Parse errors, duplicate UID identities, truncation, oversized responses, HTTP failures and ambiguous METHOD:CANCEL messages fail the check and preserve history. Empty feeds fail by default; enable allowEmpty only for feeds where a complete empty calendar is legitimate. A valid but incomplete feed cannot always be detected.

Named history is isolated by the running user, Actor, URL and monitor name. Deleting its named storage resets the baseline. Use only one concurrent run per monitor. A best-effort five-minute lease detects overlapping checks, but is not an atomic storage transaction; real simultaneous contention is not guaranteed safe. Interrupted output/billing/history commits can repeat delivered changes and charges on retry.

### Pricing

**$0.01 per successful calendar check**, including baseline and unchanged checks. One custom calendar-check event; no automatic startup or per-row charge. Platform usage is included. Set maximum run charge above $0.01; suggested default $0.05. Failed source/parser/output checks do not trigger this custom event. Billing occurs after output and before history advances. The Apify Pricing tab is authoritative.

Public calendars only. Do not supply private calendar subscription tokens, secret URLs or attendee information. This is an independent tool, unaffiliated with the source publisher or calendar providers. Open an Issue with a public reproducible feed and run ID; keep secrets out of public Issues.

# Actor input Schema

## `calendarUrl` (type: `string`):

Public complete calendar snapshot; no private tokens or credentials.

## `monitorName` (type: `string`):

Reuse this name and URL to preserve history.

## `includeUnchanged` (type: `boolean`):

Enabled by default to show results on every successful check. Disable for changes-only datasets.

## `allowEmpty` (type: `boolean`):

Off preserves history on empty responses.

## Actor input object example

```json
{
  "calendarUrl": "https://www.gov.uk/bank-holidays/england-and-wales.ics",
  "monitorName": "default",
  "includeUnchanged": true,
  "allowEmpty": false
}
```

# Actor output Schema

## `changes` (type: `string`):

Baseline, new, updated, missing and optional unchanged VEVENT records with previous/current source values.

## `summary` (type: `string`):

Success or unknown status, counts and previous-check timestamp. Absence is not cancellation.

# 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 = {
    "includeUnchanged": true
};

// Run the Actor and wait for it to finish
const run = await client.actor("quietdatatools/ics-calendar-change-monitor").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 = { "includeUnchanged": True }

# Run the Actor and wait for it to finish
run = client.actor("quietdatatools/ics-calendar-change-monitor").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 '{
  "includeUnchanged": true
}' |
apify call quietdatatools/ics-calendar-change-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,quietdatatools/ics-calendar-change-monitor"
        }
    }
}
```

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/8VMAjQTonp9CbmlHF/builds/pkJHccbXFsHPZeheZ/openapi.json
