# Website Change Monitor & Alerts (`monema/apify-change-monitor`) Actor

Monitor a web page or CSS selector for meaningful changes and optionally send webhook alerts.

- **URL**: https://apify.com/monema/apify-change-monitor.md
- **Developed by:** [Maarten Vreeburg](https://apify.com/monema) (community)
- **Categories:** Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per usage

This Actor is paid per platform usage. The Actor is free to use, and you only pay for the Apify platform usage, which gets cheaper the higher subscription plan you have.

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

## What's an Apify Actor?

Actors are a software tools running on the Apify platform, for all kinds of web data extraction and automation use cases.
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.

In JavaScript/TypeScript projects, use official [JavaScript/TypeScript client](https://docs.apify.com/api/client/js.md):

```bash
npm install apify-client
```

In Python projects, use official [Python client library](https://docs.apify.com/api/client/python.md):

```bash
pip install apify-client
```

In shell scripts, use [Apify CLI](https://docs.apify.com/cli/docs.md):

````bash
# MacOS / Linux
curl -fsSL https://apify.com/install-cli.sh | bash
# Windows
irm https://apify.com/install-cli.ps1 | iex
```bash

In AI frameworks, you might use the [Apify MCP server](https://docs.apify.com/platform/integrations/mcp.md).

If your project is in a different language, use 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

## Website Change Monitor & Alerts

![Website Change Monitor](assets/thumbnail.jpg)

![Hero banner](assets/hero.jpg)

A production-ready Apify Actor for tracking meaningful changes on websites without babysitting pages or drowning in noise.

### Why this product exists

The best buyers for this Actor are people who need *alerts they can trust*:

- founders watching competitors
- operators tracking pricing or policy pages
- traders monitoring listings, inventory, or market-adjacent pages
- recruiters and analysts watching job boards or directories
- compliance teams watching terms, help docs, and policy updates

### What it does

- monitors a full page or a specific CSS selector
- strips noisy sections before comparing snapshots
- keeps state in a persistent key-value store between runs
- detects both raw and meaningful changes
- supports webhook notifications on meaningful changes
- works locally and on Apify Cloud

### Core features

| Feature | What it solves |
|---|---|
| URL monitoring | Track any public page |
| CSS selector support | Focus on the part that matters |
| Ignore selectors | Remove cookie banners, promos, ads, and other noise |
| Change threshold | Avoid false positives from tiny edits |
| Persistent state | Compare against the last run, not just the current run |
| Webhook alerts | Forward important changes to your own system |

### Input example

```json
{
  "url": "https://example.com",
  "selector": "main",
  "ignore_selectors": ".cookie-banner,.ads,.promo",
  "min_change_ratio": 0.05,
  "webhook_url": "https://your-endpoint.example/webhook"
}
````

### Input reference

| Field | Required | Description |
|---|---|---|
| `url` | yes | Page to monitor |
| `selector` | no | CSS selector to monitor a specific region |
| `ignore_selectors` | no | Comma-separated CSS selectors removed before hashing |
| `min_change_ratio` | no | Minimum normalized difference before a change counts as meaningful |
| `webhook_url` | no | Endpoint that receives the JSON result on meaningful change |
| `state_key` | no | Custom storage grouping key for shared or split snapshots |
| `user_agent` | no | Custom HTTP user agent |
| `timeout_seconds` | no | Request timeout in seconds |

### Output

Each run writes one dataset item with:

| Field | Meaning |
|---|---|
| `status` | `initial_snapshot`, `unchanged`, `minor_change`, or `changed` |
| `raw_changed` | Whether the content hash changed |
| `significant_change` | Whether the change crossed the threshold |
| `change_ratio` | Normalized difference score |
| `current_hash` / `previous_hash` | Stable snapshot hashes |
| `current_excerpt` / `previous_excerpt` | Short previews for quick review |
| `webhook_sent` | Whether a webhook was successfully sent |
| `webhook_error` | Error text if webhook delivery failed |

### How the detection works

1. Fetch the page with a normal browser-like user agent.
2. Remove script/style and any selectors you mark as noisy.
3. Extract either the selected region or the full body text.
4. Compare against the previous stored snapshot.
5. Mark the result as:
   - `initial_snapshot`
   - `unchanged`
   - `minor_change`
   - `changed`
6. Optionally send the full JSON payload to your webhook when the change is meaningful.

### State model

This Actor stores snapshots in a **named key-value store**, not the run-scoped default store.
That means the previous snapshot survives across Apify Cloud runs and production use.

If you need separate monitors for the same URL, set different `state_key` values.
If you want multiple monitors to share state intentionally, reuse the same `state_key`.

### Good buyer stories

This Actor sells best when positioned for real operational outcomes:

- “Track competitor pricing changes”
- “Watch policy pages and get notified instantly”
- “Monitor product pages for restocks and content changes”
- “Track directory or job board listings without manual checks”
- “Send webhooks to Slack, Discord, Make, Zapier, or your backend”

### Local development

```bash
apify run
```

### Deploy

```bash
apify login --token <your-token>
apify push
```

### Notes

- The Actor is intentionally lightweight and fast.
- It is designed for clear, explainable alerts instead of noisy scraping.
- Webhook delivery is optional, so it works fine as a pure monitoring tool too.

# Actor input Schema

## `url` (type: `string`):

The URL of the website or page you want to monitor for changes.

## `selector` (type: `string`):

Optional CSS selector to monitor only a specific part of the page. If omitted, the full page text is monitored.

## `ignore_selectors` (type: `string`):

Comma-separated CSS selectors to remove before hashing (for example: '.cookie-banner,.ads,.promo').

## `min_change_ratio` (type: `number`):

Only treat a change as meaningful if the normalized text difference is at least this ratio. Set to 0 for any change.

## `webhook_url` (type: `string`):

Optional endpoint that receives the full JSON result whenever a meaningful change is detected.

## `state_key` (type: `string`):

Optional custom state key source when you want multiple runs to share or separate stored snapshots.

## `user_agent` (type: `string`):

Optional HTTP user agent string.

## `timeout_seconds` (type: `number`):

Request timeout in seconds.

## Actor input object example

```json
{
  "url": "https://www.example.com/",
  "ignore_selectors": ".cookie-banner,.advertising,.ads",
  "min_change_ratio": 0.05,
  "timeout_seconds": 30
}
```

# Actor output Schema

## `results` (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 = {
    "url": "https://www.example.com/",
    "ignore_selectors": ".cookie-banner,.advertising,.ads"
};

// Run the Actor and wait for it to finish
const run = await client.actor("monema/apify-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 = {
    "url": "https://www.example.com/",
    "ignore_selectors": ".cookie-banner,.advertising,.ads",
}

# Run the Actor and wait for it to finish
run = client.actor("monema/apify-change-monitor").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "url": "https://www.example.com/",
  "ignore_selectors": ".cookie-banner,.advertising,.ads"
}' |
apify call monema/apify-change-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=monema/apify-change-monitor",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "Website Change Monitor & Alerts",
        "description": "Monitor a web page or CSS selector for meaningful changes and optionally send webhook alerts.",
        "version": "0.3",
        "x-build-id": "4d1e0tq6VIXhTxFD5"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/monema~apify-change-monitor/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-monema-apify-change-monitor",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        },
        "/acts/monema~apify-change-monitor/runs": {
            "post": {
                "operationId": "runs-sync-monema-apify-change-monitor",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor and returns information about the initiated run in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/runsResponseSchema"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/acts/monema~apify-change-monitor/run-sync": {
            "post": {
                "operationId": "run-sync-monema-apify-change-monitor",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        }
    },
    "components": {
        "schemas": {
            "inputSchema": {
                "type": "object",
                "required": [
                    "url"
                ],
                "properties": {
                    "url": {
                        "title": "URL of the page",
                        "type": "string",
                        "description": "The URL of the website or page you want to monitor for changes."
                    },
                    "selector": {
                        "title": "CSS selector (optional)",
                        "type": "string",
                        "description": "Optional CSS selector to monitor only a specific part of the page. If omitted, the full page text is monitored."
                    },
                    "ignore_selectors": {
                        "title": "Ignore selectors (optional)",
                        "type": "string",
                        "description": "Comma-separated CSS selectors to remove before hashing (for example: '.cookie-banner,.ads,.promo')."
                    },
                    "min_change_ratio": {
                        "title": "Minimum change ratio",
                        "minimum": 0,
                        "maximum": 1,
                        "type": "number",
                        "description": "Only treat a change as meaningful if the normalized text difference is at least this ratio. Set to 0 for any change.",
                        "default": 0.05
                    },
                    "webhook_url": {
                        "title": "Webhook URL (optional)",
                        "type": "string",
                        "description": "Optional endpoint that receives the full JSON result whenever a meaningful change is detected."
                    },
                    "state_key": {
                        "title": "Custom state key (optional)",
                        "type": "string",
                        "description": "Optional custom state key source when you want multiple runs to share or separate stored snapshots."
                    },
                    "user_agent": {
                        "title": "User agent (optional)",
                        "type": "string",
                        "description": "Optional HTTP user agent string."
                    },
                    "timeout_seconds": {
                        "title": "Timeout in seconds (optional)",
                        "minimum": 1,
                        "maximum": 300,
                        "type": "number",
                        "description": "Request timeout in seconds.",
                        "default": 30
                    }
                }
            },
            "runsResponseSchema": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "object",
                        "properties": {
                            "id": {
                                "type": "string"
                            },
                            "actId": {
                                "type": "string"
                            },
                            "userId": {
                                "type": "string"
                            },
                            "startedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "finishedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "status": {
                                "type": "string",
                                "example": "READY"
                            },
                            "meta": {
                                "type": "object",
                                "properties": {
                                    "origin": {
                                        "type": "string",
                                        "example": "API"
                                    },
                                    "userAgent": {
                                        "type": "string"
                                    }
                                }
                            },
                            "stats": {
                                "type": "object",
                                "properties": {
                                    "inputBodyLen": {
                                        "type": "integer",
                                        "example": 2000
                                    },
                                    "rebootCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "restartCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "resurrectCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "computeUnits": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "options": {
                                "type": "object",
                                "properties": {
                                    "build": {
                                        "type": "string",
                                        "example": "latest"
                                    },
                                    "timeoutSecs": {
                                        "type": "integer",
                                        "example": 300
                                    },
                                    "memoryMbytes": {
                                        "type": "integer",
                                        "example": 1024
                                    },
                                    "diskMbytes": {
                                        "type": "integer",
                                        "example": 2048
                                    }
                                }
                            },
                            "buildId": {
                                "type": "string"
                            },
                            "defaultKeyValueStoreId": {
                                "type": "string"
                            },
                            "defaultDatasetId": {
                                "type": "string"
                            },
                            "defaultRequestQueueId": {
                                "type": "string"
                            },
                            "buildNumber": {
                                "type": "string",
                                "example": "1.0.0"
                            },
                            "containerUrl": {
                                "type": "string"
                            },
                            "usage": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "integer",
                                        "example": 1
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "usageTotalUsd": {
                                "type": "number",
                                "example": 0.00005
                            },
                            "usageUsd": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "number",
                                        "example": 0.00005
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
