# Sports Fixture Schedule Change Monitor (`zinin/sports-fixture-schedule-change-monitor`) Actor

Compare two supplied sports snapshots with exact old/new evidence and honest missing-event status.

- **URL**: https://apify.com/zinin/sports-fixture-schedule-change-monitor.md
- **Developed by:** [Tim Zinin](https://apify.com/zinin) (community)
- **Categories:** Sports, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $85.00 / 1,000 schedule comparison report delivereds

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?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Sports Fixture Schedule Change Monitor

Compare two sports schedule snapshots you supply. The report shows added events, confirmed field changes and events missing from the newer snapshot, with exact previous/current evidence. It does not fetch a feed, parse ICS, persist a baseline or run ongoing monitoring. Disappearance is not treated as cancellation.

This Actor is available on Apify and analyzes records you supply. Repeat the comparison when you receive an updated schedule, supplying both the prior and current snapshots.

On Apify's Free plan, a useful report costs **$0.10**, plus **$0.005 per start event**. At the default 512 MB memory, one useful report costs **$0.105**. Other plan tiers have different rates. Fixed demos and invalid-input diagnostics request no result event; a platform start charge can still apply. The examples below are synthetic and do not establish live source accuracy.

When Apify rejects input against the input schema, the Actor does not run. The diagnostics and partial reports below describe checks performed after the Actor starts.

### Input

For the fixed synthetic demo, supply only `{ "demo": true }`. It requests no result event; a platform start charge may still apply. Demo mode rejects supplied snapshots.

For your own snapshots, omit `demo` or set it to `false`. This mode requests one report event on the platform when the report is useful. The following copyable example uses **fictional synthetic records**; setting `demo: false` selects the real analysis route even when the records are fictional:

```json
{
  "demo": false,
  "previous": [
    {
      "namespace": "synthetic-example",
      "eventId": "demo-match",
      "participants": ["demo-home", "demo-away"],
      "startsAt": "2026-09-09T18:00:00Z",
      "venue": "Demo Stadium",
      "status": "scheduled"
    }
  ],
  "current": [
    {
      "namespace": "synthetic-example",
      "eventId": "demo-match",
      "participants": ["demo-home", "demo-away"],
      "startsAt": "2026-09-09T19:00:00Z",
      "venue": "Demo Stadium",
      "status": "scheduled"
    }
  ]
}
```

The only top-level fields are `previous`, `current` and optional boolean `demo`. Real mode requires both arrays. Each accepts 0–1000 rows, with at most 2 MiB of combined serialized UTF-8 snapshot JSON; both arrays empty is invalid. Each row allows only these fields:

| Field | Required | Accepted value |
| --- | --- | --- |
| `namespace` | Yes | Nonempty provider namespace, at most 100 characters. |
| `eventId` | Yes | Nonempty event ID, at most 100 characters; unique within its namespace. |
| `participants` | Yes | Exactly two distinct nonempty IDs, each at most 100 characters. Order is significant. |
| `startsAt` | Yes | Explicit-zone timestamp as described below, or `null` when unknown. |
| `venue` | Yes | Nonempty string of at most 200 characters, or `null` when unknown. |
| `status` | Yes | `scheduled`, `live`, `completed`, `postponed`, `cancelled` or `unknown`. |
| `tournamentId` | No | Nonempty string of at most 100 characters; not `null`. |

String lengths use JavaScript UTF-16 units. Control characters and whitespace-only identifiers/venues are rejected. IDs remain exact: no trimming, case folding, fuzzy matching or cross-provider merging. Participant order may represent home/away. An omitted tournament ID normalizes to null for comparison while source evidence retains the omission.

Timestamps use `YYYY-MM-DDTHH:mm:ss[.SSS]Z` or a signed `±HH:MM` offset, with 0–3 fractional digits. A timezone is mandatory. Dates must be real Gregorian calendar dates, hours 00–23 and minutes/seconds 00–59; leap seconds are unsupported. Offsets cannot exceed ±14:00. Both the supplied date and its normalized UTC instant must remain within years 0001–9999. Equal instants compare equal despite different original offsets.

### Report and incomplete coverage

A useful report has `schemaVersion: "1.0"`, `reportType: "sports_schedule_changes"`, `status: "ok"` or `"partial"`, and `useful: true`.

- `previous` and `current` counts each satisfy `received = accepted + duplicates + invalid`. Accepted counts are unique eligible identities, scoped by exact `(namespace, eventId)`.
- `changes` is ordered by namespace/event ID. Types are `added`, `updated` and `missing_from_current`. Changes contain normalized `old`/`new` records and exact `evidence.previous/current.records` with original zero-based `rowIndices`. Updated fields carry old/new values; null-time transitions use `time_confirmed` or `time_unknown`.
- `unchangedCount` counts eligible events with no changes. A valid unchanged comparison is useful. `ineligibleComparisonCount` counts readable identities that cannot safely be classified.
- At most 100 issues are returned, with `issuesTruncated` indicating omitted issues. Counting continues after that limit.

Normalized exact duplicates count once and retain all source indices. A conflicting or invalid row quarantines its entire identity group. An invalid/conflicted counterpart prevents a misleading addition or disappearance. If an invalid row has no readable identity, unmatched identities on the opposite side also become ineligible; matched valid identities remain comparable. These cases produce partial coverage when useful comparisons remain. All-invalid or entirely ineligible input receives an unbilled diagnostic instead of a paid empty report, subject to the platform pricing checks below.

Missing events carry **status unknown**, previous evidence and no current evidence. Only an explicitly supplied cancelled status can produce a cancellation status change; the report does not independently verify it. An empty previous snapshot produces `added/first_observation`; an empty current snapshot produces missing events only where coverage permits. The report does not infer why a schedule changed.

Input JSON is unchanged and returned evidence is detached. Unsupported JavaScript structures are rejected without invoking their hooks. Additional structural limits are depth 64, 100000 traversal steps, at most 1000 elements per nested array and at most 1001 own keys per container; ordinary valid rows within the limits above remain supported.

### Actor integration and delivery

This published Actor uses Node 22 and Apify SDK 3.7.2. You can also run the same package locally for development. Install locked dependencies with `npm ci`, then use `npm start` with local Apify input storage. Local execution requests no result event. The package makes no source-provider calls and uses no credentials, proxies, child Actors or cross-run storage.

For the fixed synthetic example, supply only:

```json
{ "demo": true }
```

Demo mode rejects extra snapshots or matches; supplied customer data is never processed as a free demo. Demo reports carry `isDemo: true` and `dataProvenance: "fixed_synthetic_demo"`. For real mode supply the JSON input shown above, with optional `demo: false`; the report is marked `isDemo: false`, `dataProvenance: "user_supplied"`. These labels describe the input origin, not verified source rights or accuracy.

A useful real input produces exactly one default Dataset report, including useful partial results; a valid Schedule comparison with no changes is useful. On the platform, that report requests one `result-found` event at the buyer's readable configured event price. Inspect the Actor's configured pricing before running and optionally set **Max total charge** sufficient to cover the report plus any platform start charge. A verified unset limit permits the one-report delivery; an explicit zero prevents a paid report. Unreadable or inconsistent run-limit settings fail safely before paid delivery. The runtime verifies pricing, exact remaining allowance and named counters before analysis and again before the paid write. Every platform Dataset write, including demos and diagnostics, requires consistent confirmed pay-per-event pricing and an absent or numeric-zero default Dataset item price. Free, pay-per-result, rental, unknown or inconsistent pricing models are unsupported and fail without a Dataset write. An empty event-price table alone does not prove that writing is free. Local development continues without result billing.

When the supported PPE/free-item pricing and transport checks pass, malformed or all-invalid input receives an unbilled diagnostic, retaining bounded counts, issue codes and row indices when available. It omits source IDs, field values and raw exception text. A known insufficient budget stops normally. Broken pricing, budget state, SDK operations or unexpected computation errors fail the run. “Unbilled” means no result event is requested; a platform start charge may already apply.

Download the complete nested report from the Dataset link in the run output. The Dataset is the delivery authority; there is no mandatory OUTPUT or cache write after paid delivery. The SDK writes the row before requesting its linked charge, so a paid row records `billing.settlement: "unconfirmed_at_write"`. A successful run verifies the SDK receipt and an increase of exactly one in the named `result-found` counter. A failed push or unreadable receipt leaves delivery and settlement unknown: data may exist and a charge may have occurred. The Actor does not retry that write. For the pinned SDK, it sets the actual API HTTP client to zero retries and verifies that the default Dataset uses that same client before each write; an unreadable or different transport blocks delivery. Inspect that run's Dataset and charged event counts before another analysis.

Each new run is a new analysis and can incur a new result event. There is no cross-run idempotency promise. Automatic resurrection is unsupported: if the same run already records a result event, the runtime refuses another report and directs you to its existing Dataset. This check cannot resolve a previous Dataset write with a zero or unavailable result counter; do not automatically resurrect or rerun a failed delivery.

Local tests use synthetic fixtures and guarded SDK transports. The pure algorithms and SDK runtime have independent local acceptance; cloud release acceptance remains outstanding. Future release gates include an approved price decision, a bounded real cloud run with Dataset/event readback, real-source provenance, icon upload/readback and a complete Store listing. Local tests do not establish market demand, profitability or cloud acceptance.

# Actor input Schema

## `demo` (type: `boolean`):

True uses only a fixed synthetic example. Supply no matches or snapshots in demo mode. A platform start charge may still apply.

## `previous` (type: `array`):

Buyer-supplied records; this Actor does not fetch any source. Omit this field in demo mode. See README for the strict record contract.

## `current` (type: `array`):

Buyer-supplied records; this Actor does not fetch any source. Omit this field in demo mode. See README for the strict record contract.

## Actor input object example

```json
{
  "demo": true
}
```

# Actor output Schema

## `report` (type: `string`):

One report or diagnostic. A paid row is written before charge acknowledgement; inspect the run event counts if the run fails.

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

// Run the Actor and wait for it to finish
const run = await client.actor("zinin/sports-fixture-schedule-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 = { "demo": True }

# Run the Actor and wait for it to finish
run = client.actor("zinin/sports-fixture-schedule-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 '{
  "demo": true
}' |
apify call zinin/sports-fixture-schedule-change-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,zinin/sports-fixture-schedule-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/ogu4lrhxdWl6sciT7/builds/tmo9pYEO0Q8ydo1ZJ/openapi.json
