# Website Change Monitor - Page Change Detection & Alerts (`tidytools/website-change-monitor`) Actor

Website change tracker and content monitor: text and price changes, CSS selector per URL, keyword triggers, noise filters, screenshots, email, Slack, Discord, webhook alerts, AI summary. $2/1k checks.

- **URL**: https://apify.com/tidytools/website-change-monitor.md
- **Developed by:** [Yukai Lin](https://apify.com/tidytools) (community)
- **Categories:** Automation, Developer tools, SEO tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per event

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

### What does Website Change Monitor do?

It watches web pages and tells you **what changed since the last check**: which text was added and which was removed. Run it on a schedule (hourly, daily, weekly) and get the changes in the dataset, by **e-mail**, in **Slack or Discord**, or pushed to a **webhook** (Zapier, Make, n8n, your own app). It accepts **Content Checker inputs** (`url`, `contentSelector`, `sendNotificationTo` and more), takes **before and after screenshots**, and can watch thousands of pages in one run.

- 🔁 **Remembers the previous version** of every page between runs, so each run is a real comparison (or compare with a **locked baseline** you approved)
- 🎯 **Watch only what matters**: a CSS selector for the whole run or **per page** (`#pricing`, `.product-price`, `main`), with an optional fallback to the whole page when the element disappears
- 🔇 **Less noise**: ignore dates, times, "5 minutes ago" and view counters with one switch, add your own ignore patterns, and set a minimum size for a change to count
- 🔑 **Keyword triggers**: alert only when a changed line mentions "price", "out of stock" or any words you choose
- 🔢 **Number changes**: price and percentage changes are extracted (`$19 → $29`)
- 🤖 **Optional AI summary**: one or two sentences on what changed, plus a significance score from 1 (trivial) to 5 (critical)
- 📬 **Alerts**: e-mail (via Apify's send-mail Actor), Slack and Discord messages, or JSON webhooks with an optional HMAC-SHA256 signature
- 📸 **Before and after screenshots** (optional): a screenshot of the page, or of just the watched element, on every alerted change, linked in results and alerts. Cookie and consent banners are always removed before the screenshot (verified on IKEA, The Guardian and apify.com), so the images show the page, not the banner
- 🚨 **Alerts when a page keeps failing**: get told when a page could not be read (or its element vanished) several runs in a row, so a monitor never fails silently
- 🗑️ **Removed pages are reported**: a watched page that starts answering 404 or 410 is reported as `removed` and alerted once (on by default, not charged), and as `restored` when it comes back
- ⚙️ **No false alerts after a settings change**: changing the CSS selector, ignore rules or mode saves a new baseline (`reason: "settings-changed"`) instead of reporting the whole page as changed
- 🌐 **Works on protected and JavaScript sites**: pages are read over plain HTTP first, with automatic fallback to another network and a real browser
- 💸 **$2 per 1,000 page checks**, no extra fee per detected change; failed checks are free

### Content Checker alternative

If you use a Content Checker-style Actor today, your input works as it is: `url`, `contentSelector`, `screenshotSelector`, `sendNotificationTo` and `informOnError` are accepted.

#### Migrate in one minute

Paste your Content Checker input into this Actor's JSON input, keep your schedule, and that's it:

```json
{
    "url": "https://www.apify.com/change-log",
    "contentSelector": "article",
    "screenshotSelector": "article",
    "sendNotificationTo": "you@your-apify-account-email.com",
    "informOnError": "true"
}
```

`screenshotSelector` turns on change screenshots of that element ($0.003 each), and `informOnError: "true"` alerts you from the first failed check. To watch more pages in the same run, move them into `urls` (one entry per page, each with its own selector and name):

```json
{
    "urls": [
        { "url": "https://www.apify.com/change-log", "userData": { "selector": "article", "name": "Apify changelog" } },
        { "url": "https://example.com/pricing", "userData": { "selector": "#plans", "name": "Competitor pricing" } }
    ],
    "monitorName": "my-watchlist",
    "screenshotOnChange": true,
    "alertOnError": true,
    "notifyEmail": "you@your-apify-account-email.com"
}
```

| Content Checker field | This Actor | Notes |
|---|---|---|
| `url` | `url` or `urls` | `urls` takes many pages in one run (up to 5,000) |
| `contentSelector` | `contentSelector` or `cssSelector` | Also per page: `{"url": "...", "userData": {"selector": "#price", "name": "Pricing"}}` |
| `sendNotificationTo` | `sendNotificationTo` or `notifyEmail` | Sent with apify/send-mail, which delivers to your Apify account's e-mail address |
| `screenshotSelector` | `screenshotSelector`, or `screenshotOnChange` (uses the watched selector) | Screenshot after the change plus the previous one ("before"), in results and alerts |
| `informOnError` | `informOnError` or `alertOnError` + `errorThreshold` | Alert after N failed runs in a row (Content Checker's `informOnError` = after the first), plus a "works again" note |
| (none) | `webhookUrl`, `webhookSecret` | Slack, Discord or JSON, optionally signed |
| (none) | `watchKeywords`, `minChangedLines`, `minChangeRatio`, `ignoreCommonNoise` | Fewer false alarms |
| (none) | `aiSummary` | What changed and how much it matters |

What stays the same for you: a schedule in Apify Console runs the check, and each run compares with the last one. What you gain: many pages per run, per-page selectors, automatic browser fallback, noise filters, and failed checks are not charged.

### Use cases

- **Competitor monitoring**: pricing pages, product catalogs, feature lists, job openings (use `watchKeywords` like "price" and read `numericChanges`)
- **Compliance & legal**: terms of service, privacy policies, regulatory pages (use a **locked baseline** to compare with the approved version)
- **Release tracking**: changelogs, download pages, documentation
- **Content watch**: news pages, government notices, stock or availability pages

### How much does it cost?

| Event | Price |
|---|---|
| Page check | **$2.00 / 1,000 checks** |
| AI change summary (optional) | **$0.005** per changed page summarized |
| Change screenshot (optional) | **$0.003** per screenshot |

**No start fee** and no fee per detected change: the base check price is fixed, and only optional features are charged per use.

| Monitor | Checks per month | Cost per month |
|---|---|---|
| 1 page, every hour | 720 | **$1.44** |
| 1 page, every hour, with screenshots and 10 alerted changes | 720 + 11 screenshots | **$1.47** |
| 20 pages, once a day | 600 | **$1.20** |
| 100 pages, once a day | 3,000 | **$6.00** |

With screenshots on, one screenshot is taken when the baseline is saved (or the first time a page has none) and one per alerted change; unchanged checks take no screenshot. Pages that could not be read, and pages whose CSS selector matched nothing, are not charged. E-mail alerts run apify/send-mail on your account (a few seconds of platform usage). Higher Apify plans get volume discounts (see the *Pricing* tab).

Content Checker itself has no Actor fee in the Apify Store; you pay Apify platform usage for its browser runs, which depends on your plan (checked September 2026). For comparison, some change monitors in the Apify Store charge $0.008 to $0.02 per detected change on top of each check, and AI change summaries elsewhere cost $0.02 to $0.06 each (checked September 2026).

### Control your cost

- **Charged**: each page that was read and compared ($0.002), plus screenshots ($0.003) and AI summaries ($0.005) only if you turn them on.
- **Free**: pages that could not be read, removed pages (404 / 410), pages whose CSS selector matched nothing, and invalid input lines. Every row has `charged: true` or `false`, and the `error` text of free rows ends with "(not charged)".
- At the start, the log and status message show the plan: number of pages × price (and the most that screenshots or AI summaries could add) = the most the run can cost, compared with your **maximum charge per run** (set in the run options).
- When that maximum is reached, the run stops checking new pages. The status message says how many were not checked, `SUMMARY.notProcessed` lists them (count and up to 100 URLs), and the webhook and e-mail are still sent with `incomplete: true` and `uncheckedPages`, so an integration does not read "no alert" as "no changes".

### How to use it

1. Paste the **pages to watch**, one URL per line (`example.com/pricing` works too). A line that is not a URL gets its own error row (`errorType: "invalid_input"`, not charged) and the other pages are still checked. For per-page settings, use **Pages with per-page settings** and add user data to a URL: `{"selector": "#pricing", "name": "Competitor pricing"}`.
2. Give the monitor a **name** (runs with the same name compare against each other).
3. Optional: set a **CSS selector**, **ignore patterns**, **keywords**, and alerts (**e-mail**, **webhook URL**).
4. Run it once to save the baseline, then **create a schedule** in Apify Console (Schedules → Create, pick this Actor and your input, choose e.g. "every day at 9:00") to run it automatically.
5. Optional: turn on **Screenshot changed pages** and **Alert when a page keeps failing** (Alerts and noise control). **Alert when a page is removed** is on by default.
6. Want to check your alert setup first? Turn on **Test alerts without sending** (Alerts and noise control): the webhook is not called and the e-mail uses send-mail's mock mode; the result is in the `SUMMARY` record.

#### Input example

```json
{
    "urls": [
        { "url": "https://www.python.org/downloads/", "userData": { "selector": ".download-list-widget", "name": "Python releases" } },
        { "url": "https://example.com/pricing", "userData": { "selector": "#plans", "name": "Competitor pricing" } }
    ],
    "monitorName": "competitors",
    "ignoreCommonNoise": true,
    "watchKeywords": ["price", "release"],
    "aiSummary": true,
    "outputOnlyChanges": true,
    "webhookUrl": "https://hooks.slack.com/services/...",
    "notifyEmail": "you@your-apify-account-email.com"
}
```

#### Output

The first run saves a baseline (`"status": "first-check"`). Later runs report `unchanged` or `changed`. A real changed result (September 2026, httpbin.org/uuid returns a new ID on every request):

```json
{
    "url": "https://httpbin.org/uuid",
    "status": "changed",
    "checkedAt": "2026-09-29T02:38:51.571Z",
    "addedCount": 1,
    "removedCount": 1,
    "changeRatio": 0.333,
    "added": ["\"uuid\": \"03c057ca-3989-4dd3-9bbe-076de0a0eb27\""],
    "removed": ["\"uuid\": \"3f613ece-f6a4-4943-bfa1-d6a33a3ce95a\""],
    "significant": true,
    "aiSummary": "The UUID value changed from 3f613ece-f6a4-4943-bfa1-d6a33a3ce95a to 03c057ca-3989-4dd3-9bbe-076de0a0eb27",
    "significance": 1,
    "mode": "browser",
    "via": "browser"
}
```

Other fields you may see:

- `significant: false` with a `minorReason` (for example "no watched keyword in the changed lines"): reported, but no alert
- `numericChanges`: `[{ "before": "$19", "after": "$29", "context": "Pro plan $29/month" }]`
- `noiseOnly: true` on an `unchanged` page: only dates, times or counters differed (with *Ignore dates, times and counters*)
- `reorderedOnly: true` on an `unchanged` page: the same lines only moved around, so there is nothing to show and no alert
- Very long lines (table layouts such as Hacker News or price tables often convert to one huge line) are compared in short pieces, so `added` / `removed` show the part that changed, not a 10,000-character line
- `status: "selector-not-found"`: the watched element is missing (real example: `{"status": "selector-not-found", "name": "Missing element", "selector": "#nope-xyz", "message": "The CSS selector \"#nope-xyz\" matched nothing on the page. ..."}`); with *Fall back to the whole page*, the whole page is compared instead and `selectorFallback: true` is set
- Locked baseline: `baselineAt`, `driftedSince` and `newSinceLastRun` (alerts fire only when the page differs from what the previous run saw)
- `status: "failed"` with `error`, `errorType` and `consecutiveFailures` (not charged; the baseline is kept)
- On `first-check`: `watchedPreview` (the first lines of the watched text) and `watchedLines`, so you can see whether your CSS selector picked the right part of the page. After a settings change: `reason: "settings-changed"`
- `status: "removed"` with `httpStatus` (404 or 410), `removedAt` and `significant: true` the first time (not charged; the baseline is kept). When the page is back, the result carries `restored: true`
- Every row has `charged: true` or `false`; successful rows also carry `finalUrl` and `httpStatus`. A line that is not a URL: `{ "input": "acme", "status": "invalid-input", "errorType": "invalid_input", "charged": false }`
- With *Screenshot changed pages*: `screenshotUrl` (JPEG after the change, or the baseline shot on the first check) and `previousScreenshotUrl` (the "before" image: the last screenshot saved for that page; with a locked baseline, the baseline's screenshot). `screenshotError` if the screenshot failed (not charged)

A real screenshot result (September 2026, httpbin.org/uuid with screenshots on, second run):

```json
{
    "url": "https://httpbin.org/uuid",
    "status": "changed",
    "addedCount": 1,
    "removedCount": 1,
    "added": ["\"uuid\": \"762816a2-8e92-4d9a-aa9a-05b68c05601e\""],
    "removed": ["\"uuid\": \"e83ca153-a8dc-498e-b49c-80ddb4cfe18e\""],
    "significant": true,
    "screenshotUrl": "https://api.apify.com/v2/key-value-stores/NGtk6Pi14T1p0800F/records/shot-f5056fea96bf70021469b9ac93aae968-20260929034729491.jpg?signature=…",
    "previousScreenshotUrl": "https://api.apify.com/v2/key-value-stores/NGtk6Pi14T1p0800F/records/shot-f5056fea96bf70021469b9ac93aae968-20260929034644443.jpg?signature=…"
}
```

With *Alert when a page keeps failing* (`alertOnError`, `errorThreshold`), pages that failed that many runs in a row are listed in the alert under **Problems**, once per failure streak, and pages that work again are listed under **Recovered**. Real `SUMMARY` excerpt (httpbin.org/status/503, threshold 2, second run):

```json
{
    "problems": [{ "event": "problem", "url": "https://httpbin.org/status/503", "count": 2, "status": "failed", "error": "target returned HTTP 503" }],
    "notifications": [{ "channel": "webhook", "ok": true, "kind": "json", "status": 200 }]
}
```

The JSON webhook receives `{ "monitor": "...", "checkedAt": "...", "changes": [ { "url", "name", "title", "addedCount", "removedCount", "added", "removed", "matchedKeywords", "numericChanges", "aiSummary", "significance", "screenshotUrl", "previousScreenshotUrl" } ], "problems": [ ... ] }` (`problems` only with error alerts), plus `incomplete` and, when the run stopped at your maximum charge limit, `uncheckedPages`. A removed page appears in `changes` with `pageRemoved: true` and `httpStatus`; a page that is back has `restored: true`. With a signing secret, verify the `X-Signature-256: sha256=<hex>` header (HMAC-SHA256 of the raw body). The `SUMMARY` record has `status` (`SUCCESS`, `PARTIAL_RESULTS`, `FAILED`, `NO_RESULTS` or `LIMIT_REACHED`), counts and the result of each alert.

### Use with AI agents (MCP)

Connect Apify's MCP server (https://mcp.apify.com?tools=tidytools/website-change-monitor) to Claude, Cursor or any MCP client, then ask e.g. "Check whether the pricing pages of these three competitors changed since last week and summarize what changed."

```json
{ "urlList": ["https://example.com/pricing"], "monitorName": "pricing", "aiSummary": true }
```

Failed items are not charged and carry an `errorType`.

### Use it from code and integrations

Run it from your own code with the Apify API. This call waits for the run and returns the results as JSON (replace `YOUR_TOKEN` with your Apify API token):

```bash
curl -X POST "https://api.apify.com/v2/acts/tidytools~website-change-monitor/run-sync-get-dataset-items?token=YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"urlList":["https://www.python.org/downloads/","https://news.ycombinator.com"]}'
```

The synchronous endpoint waits up to 5 minutes. For bigger runs, start the run with `POST https://api.apify.com/v2/acts/tidytools~website-change-monitor/runs` and read the dataset when it finishes, or use the `apify-client` package for JavaScript or Python.

**Schedules and integrations:** run it daily or weekly with Apify Schedules, get a webhook when a run finishes, or send the results to Zapier, Make, n8n, Google Sheets, Slack and other apps with Apify integrations. Results can be exported as JSON, CSV, Excel or XML.

### Tips

- Use **Only output changed pages** for scheduled runs, so the dataset contains just the news.
- Rotating banners, ads or "related articles" blocks: list their CSS selectors in **Ignore these parts of the page**; they are removed before comparing.
- If a page reports changes every time, turn on **Ignore dates, times and counters**, add **ignore patterns** for the parts that always differ (rotating banners), set **Minimum changed lines**, or narrow the **CSS selector**.
- Use different **monitor names** for independent watch lists.
- **Advanced settings**: *Plain HTTP requests from* and *Proxy* help with sites that block data-center requests (proxy usage is billed to your Apify account).

### Limitations

- Compares page **text**, not images or layout. Screenshots are taken in a separate browser visit right after the check, so a page that changes on every request (like a random ID) can differ slightly from the compared text.
- Login and newsletter pop-ups are hidden too, and full-page screenshots pin sticky headers so they appear once, as soon as the capture backend supports it (the item then lists what was removed in `screenshotCleanup`).
- Screenshot links are signed links to your monitor's key-value store; the 5 newest screenshots per page are kept (plus the locked baseline's).
- Lines that only moved position are not reported as changes.
- E-mail alerts use apify/send-mail, which only delivers to the e-mail address of your Apify account. For other recipients, use a webhook (Zapier, Make) or Slack.
- Only public pages; no logins.

### FAQ

**How often can it check a page?** As often as your Apify schedule runs it: hourly, daily or weekly. One page checked every hour is 720 checks, about $1.44 per month.

**Can it monitor price changes?** Yes. Watch only the price element with a CSS selector (e.g. `.product-price`), and price and percentage changes are extracted (`$19 → $29`). Keyword triggers can limit alerts to lines that mention words you choose.

**How do I get alerts?** By e-mail to your Apify account address, in Slack or Discord, or with a JSON webhook to Zapier, Make, n8n or your own app.

**Will it alert me about dates and counters that change on every visit?** Not if you turn on **Ignore dates, times and counters**: dates, times, "5 minutes ago" and view counters are ignored, and you can add your own ignore patterns and a minimum change size.

### Support

Open an issue in the **Issues** tab with the URL and your input. Issues are checked regularly.

# Actor input Schema

## `urlList` (type: `array`):

Main input (fill this, or `urls`). Pages to check for changes, one URL per line (lines with several URLs separated by commas or spaces are split; lines that are not URLs get an error row and are not charged). Run this Actor on a schedule (e.g. daily) to be told what changed since the last run.

## `urls` (type: `array`):

Same as the list above, plus optional per-page settings: add user data such as {"selector": "#pricing", "name": "Competitor pricing"} to a URL (Advanced mode of this list, or in JSON input). You can also link a text file with one URL per line.

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

Runs with the same name compare against each other. Use different names for independent monitors (e.g. "competitor-prices").

## `cssSelector` (type: `string`):

Optional, e.g. "main", "#pricing" or ".product-price". Leave empty to watch the whole page. A per-page "selector" in user data overrides it. Changing it saves a new baseline instead of reporting a change.

## `ignorePatterns` (type: `array`):

Optional regular expressions for noisy lines, e.g. "^Updated", "\d+ views", "Copyright". Matching lines are not compared.

## `outputOnlyChanges` (type: `boolean`):

Skip unchanged pages in the results (they are still checked and charged).

## `mode` (type: `string`):

"Auto" reads pages over plain HTTP and switches to a real browser for sites that block it or need JavaScript. Values: auto = Auto (recommended); fast = Fast (HTTP only); browser = Browser (JavaScript rendering).

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

How many pages are checked at the same time.

## `notifyEmail` (type: `string`):

Optional. The e-mail address of your Apify account (apify/send-mail delivers only to the account owner). When pages changed, a summary e-mail is sent by running apify/send-mail on your account (a few seconds of platform usage).

## `webhookUrl` (type: `string`):

Optional. When at least one page changed (or was removed, or the run stopped at your charge limit), a summary is POSTed here. Slack and Discord webhook URLs get a formatted message; other URLs (Zapier, Make, n8n, your app) get JSON.

## `webhookSecret` (type: `string`):

Optional. Signs the webhook body with HMAC-SHA256 in the X-Signature-256 header (sha256=<hex>) so your endpoint can verify it.

## `alertOnRemoved` (type: `boolean`):

When a watched page answers 404 or 410, report it as "removed" and notify once (e-mail / webhook), then "restored" when it is back. Not charged. The baseline is kept.

## `alertOnError` (type: `boolean`):

Also notify (e-mail / webhook) when a page could not be read, or its CSS selector matched nothing, several runs in a row, and again when it works again. Stops monitors from failing silently.

## `errorThreshold` (type: `integer`):

Used with "Alert when a page keeps failing". 1 = alert on the first failure. Each failure streak is alerted once.

## `screenshotOnChange` (type: `boolean`):

Save a screenshot (JPEG) when the baseline is saved and on every alerted change. Only the watched element is captured when a CSS selector is set. Results and alerts link the new screenshot (screenshotUrl) and the previous one (previousScreenshotUrl, the "before" image). Charged per screenshot; stored in the monitor's key-value store (the 5 newest per page are kept).

## `dryRunNotifications` (type: `boolean`):

Prepare alerts but do not deliver them: the webhook is not called and the e-mail uses send-mail's mock mode. The result is recorded in the SUMMARY record.

## `watchKeywords` (type: `array`):

Optional, e.g. "price", "out of stock", "discontinued". A change counts as significant only if an added or removed line contains one of them (case-insensitive). Matches are listed in matchedKeywords.

## `minChangedLines` (type: `integer`):

Changes with fewer added + removed lines are reported as minor (significant: false) and do not trigger alerts. 0 = off.

## `minChangeRatio` (type: `string`):

Optional number between 0 and 1, e.g. 0.05 = at least 5% of the page changed. Smaller changes are minor and do not trigger alerts. Empty = off.

## `ignoreCommonNoise` (type: `boolean`):

Treat lines that differ only in dates, clock times, "5 minutes ago" or view/like counters as unchanged.

## `ignoreSelectors` (type: `array`):

Optional, e.g. ".carousel", "#ads", ".related-posts". These elements are removed before comparing, so rotating banners or recommendations do not count as changes.

## `wholePageFallback` (type: `boolean`):

When the CSS selector matches nothing, compare the whole page instead. Off: the page is reported as "selector-not-found" (not charged, baseline kept).

## `aiSummary` (type: `boolean`):

For significant changes, add aiSummary (what changed, in one or two sentences) and significance (1 trivial to 5 critical), also included in alerts. Charged per summary.

## `baselineMode` (type: `string`):

Locked: every run compares with the approved version saved on the first run (compliance use), and alerts fire only when the page differs from what the previous run saw. Values: rolling = The previous check (default); locked = A locked baseline (the first check, until you reset it).

## `resetBaseline` (type: `boolean`):

Save the current version as the new baseline in this run (reports first-check).

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

Same as adding one page to "Pages to watch". Accepted so Content Checker inputs work unchanged.

## `contentSelector` (type: `string`):

Same as "Only watch this part of the page".

## `sendNotificationTo` (type: `string`):

Same as "E-mail changes to".

## `screenshotSelector` (type: `string`):

Same as a screenshot of this element. Setting it turns on "Screenshot changed pages" (charged per screenshot), as in Content Checker.

## `informOnError` (type: `string`):

"true" = same as "Alert when a page keeps failing" with an alert after the first failure (unless you set the number of failures).

## `httpVia` (type: `string`):

Some sites block requests from data centers. Auto retries from a second network before falling back to a real browser. If both are refused, Auto also tries our second server (Oracle, different IP) before a real browser.

## `proxyConfiguration` (type: `object`):

Only used for requests sent from Apify's network. Apify proxy usage is billed to your Apify account.

## Actor input object example

```json
{
  "urlList": [
    "https://www.python.org/downloads/",
    "https://news.ycombinator.com"
  ],
  "monitorName": "default",
  "outputOnlyChanges": false,
  "mode": "auto",
  "maxConcurrency": 5,
  "alertOnRemoved": true,
  "alertOnError": false,
  "errorThreshold": 3,
  "screenshotOnChange": false,
  "dryRunNotifications": false,
  "minChangedLines": 0,
  "ignoreCommonNoise": false,
  "wholePageFallback": false,
  "aiSummary": false,
  "baselineMode": "rolling",
  "resetBaseline": false,
  "httpVia": "auto"
}
```

# Actor output Schema

## `results` (type: `string`):

No description

## `full` (type: `string`):

No description

## `summary` (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 = {
    "urlList": [
        "https://www.python.org/downloads/",
        "https://news.ycombinator.com"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("tidytools/website-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 = { "urlList": [
        "https://www.python.org/downloads/",
        "https://news.ycombinator.com",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("tidytools/website-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 '{
  "urlList": [
    "https://www.python.org/downloads/",
    "https://news.ycombinator.com"
  ]
}' |
apify call tidytools/website-change-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,tidytools/website-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/rhX83ewDzbmT6Fq94/builds/KZJ1BJbkggMipm9Pw/openapi.json
