# Website Change Monitor — Diff, Screenshot & AI Summary (`almoutasem_nabil/page-change-monitor`) Actor

Watch pricing pages, product availability, terms, docs or any URL. Get only what changed since the last run — added/removed text, change %, screenshot — plus an optional AI summary of what the change means.

- **URL**: https://apify.com/almoutasem\_nabil/page-change-monitor.md
- **Developed by:** [Al Moutasem Nabil](https://apify.com/almoutasem_nabil) (community)
- **Categories:**
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 1,000 page checkeds

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?

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

## Website Change Monitor — Track Any Page for Changes, with Diff, Screenshot & AI Summary

Watch pricing pages, product availability, terms, docs or any URL. Get only what changed since the
last run — added/removed text, change %, screenshot — plus an optional AI summary of what the change
means. Schedule it and route alerts to Slack or email.

### Who it's for

- **Product marketers** watching competitors' pricing and plan pages.
- **E-commerce and procurement teams** watching stock status, prices and tender portals.
- **Legal and compliance** watching terms, privacy policies and regulatory pages.
- **Developers** watching API docs, changelogs and status pages.
- **Analysts and journalists** watching government announcements and published figures.

### What makes it different

- **Only what changed.** Each run compares against the last one and returns the changed pages, not
  a fresh copy of everything.
- **A real diff.** Added and removed lines, plus a `changePercent`, so you can tell a new pricing
  tier from a reworded footer at a glance.
- **Noise control that actually works.** Nav, footers and cookie banners are stripped before
  comparing; `ignorePatterns` neutralises timestamps and counters; `minChangePercent` sets a floor;
  and a CSS `selector` narrows the watch to a single price or status line.
- **Keyword alerts.** "Sold out" appearing or "In stock" disappearing is reported however small the
  edit, regardless of the change threshold.
- **AI meaning, not just text.** An optional summary says what the change means, categorises it
  (price, availability, legal, content, outage) and scores importance 1–5, so you can route only the
  4s and 5s to a human. Arabic summaries are written in Modern Standard Arabic.
- **Screenshots** of changed pages, stored with the run.

### Input

```json
{
    "pages": [
        { "url": "https://www.apify.com/pricing", "name": "Apify pricing", "selector": ".pricing-table" },
        { "url": "https://nodejs.org/en/blog", "name": "Node.js releases" },
        { "url": "https://shop.example.com/item/42", "name": "Stock", "ignoreSelectors": [".reviews"] }
    ],
    "trackingId": "competitor-watch",
    "mode": "changes",
    "ignorePatterns": ["Last updated:.*", "\\d{1,2}:\\d{2}\\s*(AM|PM)?"],
    "minChangePercent": 0.5,
    "watchKeywords": ["Sold out", "Discontinued"],
    "aiSummary": true,
    "screenshotOnChange": true
}
```

The **first run is a silent baseline** — it records the pages and reports nothing, so your first
alert is a real change. Set `emitBaselineAsChanged` if you want that first run reported.

### Output

One item per page. Every field is documented in `.actor/dataset_schema.json`; the Console shows
**Changes** and **All pages** views. A changed page, from a real test run:

```json
{
    "url": "https://www.apify.com/pricing",
    "name": "Apify pricing",
    "status": "changed",
    "httpStatus": 200,
    "checkedAt": "2026-09-07T06:00:00.000Z",
    "lastChangedAt": "2026-09-07T06:00:00.000Z",
    "changePercent": 11.901,
    "diff": {
        "added": ["Pay per event - you pay a fixed price for the events the Actor's developer defines."],
        "removed": ["A line that will disappear", "Another removed line"]
    },
    "keywordHits": null,
    "contentHash": "3f2a1c9e8b7d6f5a4c3b2a1908f7e6d5c4b3a291",
    "wordCount": 1482,
    "pageTitle": "Pricing | Apify",
    "screenshotUrl": "https://api.apify.com/v2/key-value-stores/.../records/screenshot-...png",
    "aiSummary": {
        "summary": "The Pro plan price rose from $49 to $59 per month and annual billing was added.",
        "category": "price_change",
        "importance": 5
    },
    "error": null,
    "truncated": false
}
```

Pages that could not be fetched come back with `status: "error"` and are **never charged**.

### Schedule it and get alerts

1. Fill in the input and **Save as task**.
2. Add a **Schedule** to the task — daily at 07:00 is typical; hourly for stock or status pages.
3. Open the task's **Integrations** tab and add **Slack**, **Email**, **Webhook**, **Make** or
   **Zapier** on *Run succeeded*. In `changes` mode the dataset is empty when nothing changed, so
   you only hear from it when something actually moved.
4. For Slack, map `name`, `url`, `changePercent` and `aiSummary.summary` into the message — that is
   usually all a human needs to decide whether to look.

#### n8n and Make

Use the **Apify** node with *Run Actor and get dataset*, then branch on
`aiSummary.importance >= 4` to page someone, and log the rest. Filtering on `category` lets you send
`price_change` to sales and `legal_policy` to legal.

### Use as an API / MCP tool

With **Standby** enabled the Actor stays warm and checks one page per request:

```
GET https://<your-actor>.apify.actor/?url=https://example.com/pricing&trackingId=my-watch&aiSummary=1
```

Parameters mirror the input (`url`, `selector`, `trackingId`, `renderJs`, `aiSummary`,
`summaryLanguage`, `screenshotOnChange`, `minChangePercent`, `watchKeywords`). `GET /health` returns
`{"ok":true}`.

### Pricing

Pay-per-event. You pay for pages checked and changes found.

| Event             | Price  | When                                                                     |
| ----------------- | ------ | ------------------------------------------------------------------------ |
| Page checked      | $0.002 | Per page fetched and compared. A page that could not be fetched is free. |
| Change detected   | $0.01  | Per page reported as changed or new.                                     |
| AI change summary | $0.02  | Per AI summary produced.                                                 |
| Screenshot        | $0.005 | Per screenshot captured.                                                 |

- **50 pages checked daily**: 50 × $0.002 × 30 = **$3.00 a month**, plus $0.01 per change.
- **A typical month with ~30 changes**: about **$3.30**. With AI summaries on those changes,
  **under $1 extra**.

Default memory 1024 MB. Use **2048 MB** when `renderJs` or `screenshotOnChange` is on — Apify's
start fee scales per GB, so leave them off unless a page needs them.

### Limitations

- **Login-walled pages are not supported.** The Actor never logs in and sends no cookies.
- **Very large pages are truncated** at 300 KB of text; `truncated: true` says so.
- **JS-heavy pages** need a browser. Pages whose HTML comes back as an empty shell are retried in
  one automatically; set `renderJs` per page to force it.
- **Rotating content** (adverts, "related articles", A/B tests) will report changes until you
  exclude it with `ignoreSelectors` or `ignorePatterns`. A `selector` is the most reliable fix.
- **Changing your own settings** — `selector`, `ignorePatterns`, `textOnly` — changes what is
  compared, so the next run legitimately reports a change.

### Related actors

- **[Hiring Signals Monitor](../hiring-signals-monitor)** — track companies' career pages and get
  new, removed and changed roles.
- **[Ad Library Intelligence](../ad-library-intelligence)** — track competitors' live ads across
  Google, LinkedIn and TikTok.
- **[Middle East Jobs Scraper](../mena-jobs-aggregator)** — five MENA job boards in one
  deduplicated feed.

### Legal note

The Actor reads publicly accessible pages — the same content any visitor sees — and never logs in or
sends cookies. It stores a normalized text snapshot of each page purely so the next run has
something to compare against; nothing is republished. It extracts no personal data: no emails, no
phone numbers, no names are pulled into fields. Requests are paced and concurrency is capped. Use it
in line with each site's terms and the laws that apply to you.

# Actor input Schema

## `pages` (type: `array`):

Each entry is a URL, or an object { url, name, selector, ignoreSelectors, renderJs }. Use 'selector' to watch one element (a price, a status line) — it is the single best way to avoid false alarms.

## `trackingId` (type: `string`):

Name of this watch. The previous run's snapshot is stored under this key in the 'page-monitor-state' key-value store, so use one ID per set of pages and keep it stable.

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

'changes' returns only pages that changed (plus errors) — the normal way to run this on a schedule. 'snapshot' returns every page's current state each run.

## `textOnly` (type: `boolean`):

Compare the visible text. Turn off to compare markup too, which catches attribute and structural changes but reports far more noise.

## `renderJs` (type: `boolean`):

Load every page in a headless browser. Slower and needs 2048 MB. Leave off: pages whose HTML comes back as an empty shell are automatically retried in a browser anyway.

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

Regular expressions for text to ignore — timestamps, view counters, CSRF tokens, 'Last updated' lines. This is the main tool for stopping a page from reporting a change on every single run.

## `minChangePercent` (type: `number`):

Ignore changes smaller than this share of the page text. Raise it for noisy pages; a watched keyword always reports regardless of this setting.

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

Report whenever one of these appears or disappears, however small the change — 'Sold out', 'Out of stock', 'Discontinued', a competitor's plan name.

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

Capture a full-page PNG when a page changes and return its URL. Needs a browser, so budget 2048 MB.

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

For each changed page, add a plain-language summary of what changed, a category (price, availability, legal, content, outage) and an importance score from 1 to 5.

## `summaryLanguage` (type: `string`):

ISO code for the AI summary language. 'ar' produces Modern Standard Arabic.

## `emitBaselineAsChanged` (type: `boolean`):

On the first run for a tracking ID, report every page as new. Off = the first run quietly records the baseline so your first alert is a real change.

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

How many pages are checked in parallel.

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

Proxies for the requests. Datacenter is enough for most pages; switch to residential for sites that block cloud IPs.

## Actor input object example

```json
{
  "pages": [
    {
      "url": "https://www.apify.com/pricing",
      "name": "Apify pricing",
      "selector": null
    },
    {
      "url": "https://nodejs.org/en/blog",
      "name": "Node.js releases"
    },
    {
      "url": "https://www.gov.uk/government/announcements",
      "name": "GOV.UK announcements"
    }
  ],
  "trackingId": "my-watch",
  "mode": "changes",
  "textOnly": true,
  "renderJs": false,
  "ignorePatterns": [
    "Last updated:.*",
    "\\d{1,2}:\\d{2}\\s*(AM|PM)?"
  ],
  "minChangePercent": 0.5,
  "screenshotOnChange": false,
  "aiSummary": false,
  "summaryLanguage": "en",
  "emitBaselineAsChanged": false,
  "maxConcurrency": 10,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

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

One item per page checked. In 'changes' mode only changed, new and errored pages are returned; in 'snapshot' mode every page is.

## `screenshots` (type: `string`):

Full-page PNGs captured for changed pages, when screenshots are on. One record per changed page.

## `state` (type: `string`):

Text snapshot and hash of every watched page under each tracking ID, so the next run can diff against it. One record per tracking ID.

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

Pages checked, changed, unchanged and errored, request count and the charged-event tally.

# 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 = {
    "pages": [
        {
            "url": "https://www.apify.com/pricing",
            "name": "Apify pricing",
            "selector": null
        },
        {
            "url": "https://nodejs.org/en/blog",
            "name": "Node.js releases"
        },
        {
            "url": "https://www.gov.uk/government/announcements",
            "name": "GOV.UK announcements"
        }
    ],
    "trackingId": "my-watch",
    "ignorePatterns": [
        "Last updated:.*",
        "\\d{1,2}:\\d{2}\\s*(AM|PM)?"
    ],
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("almoutasem_nabil/page-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 = {
    "pages": [
        {
            "url": "https://www.apify.com/pricing",
            "name": "Apify pricing",
            "selector": None,
        },
        {
            "url": "https://nodejs.org/en/blog",
            "name": "Node.js releases",
        },
        {
            "url": "https://www.gov.uk/government/announcements",
            "name": "GOV.UK announcements",
        },
    ],
    "trackingId": "my-watch",
    "ignorePatterns": [
        "Last updated:.*",
        "\\d{1,2}:\\d{2}\\s*(AM|PM)?",
    ],
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("almoutasem_nabil/page-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 '{
  "pages": [
    {
      "url": "https://www.apify.com/pricing",
      "name": "Apify pricing",
      "selector": null
    },
    {
      "url": "https://nodejs.org/en/blog",
      "name": "Node.js releases"
    },
    {
      "url": "https://www.gov.uk/government/announcements",
      "name": "GOV.UK announcements"
    }
  ],
  "trackingId": "my-watch",
  "ignorePatterns": [
    "Last updated:.*",
    "\\\\d{1,2}:\\\\d{2}\\\\s*(AM|PM)?"
  ],
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call almoutasem_nabil/page-change-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,almoutasem_nabil/page-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/xoPqyg4duOSCmEaLn/builds/83URgFWWUuUvyktKK/openapi.json
