Website Change Monitor: Page & Selector Alerts avatar

Website Change Monitor: Page & Selector Alerts

Pricing

from $2.00 / 1,000 page checks

Go to Apify Store
Website Change Monitor: Page & Selector Alerts

Website Change Monitor: Page & Selector Alerts

Checks web pages or CSS selectors on a schedule and reports each page as changed, unchanged, baseline, selector missing or fetch error, with a line diff. Sends one alert per run to Slack, Discord or a webhook.

Pricing

from $2.00 / 1,000 page checks

Rating

0.0

(0)

Developer

Holloway & Co.

Holloway & Co.

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

4 hours ago

Last modified

Categories

Share

Website Change Monitor: Page & Selector Change Alerts (Slack, Discord, Webhook)

Website Change Monitor checks a list of web pages on a schedule and tells you what changed since the last run. It can watch a whole page or only the part that matches a CSS selector, such as a price, a table, a job list or a terms-of-service section. Every run stores one result per page with a line-by-line diff, and sends one combined message to Slack, Discord or any webhook.

What it does

  • Fetches each page and compares it with the copy saved by the previous run.
  • Compares the visible text (default), the HTML, or only the elements that match your CSS selector.
  • Removes text that changes on every visit before comparing: timestamps, dates, "5 minutes ago", CSRF and nonce tokens, cache-busting query parameters and hashed file names. You can edit this list.
  • Reports each page as changed, unchanged, baseline (first check), selector_missing or fetch_error, with the HTTP status, the added and removed lines and the share of the content that changed.
  • Optionally watches a sitemap.xml, a sitemap index, or an RSS or Atom feed and reports URLs that were added since the last run.
  • Sends one message per run to Slack, Discord and/or a JSON webhook. You choose whether to be told about changes, about every run, or only about problems.
  • Can render pages in headless Chrome when the content is created by JavaScript.

Why it does not break when a page layout changes

A monitor that watches a CSS selector usually breaks when the site is redesigned: the selector no longer matches, the run fails, and you stop getting results without being told why.

This Actor treats that as a result, not a crash:

  • If the selector matches nothing, the page gets the status selector_missing with the message No element matches the selector "#price". You get an alert, and the other pages are still checked.
  • The last good copy of the page is kept. When the selector matches again, the page is compared with that copy.
  • Timeouts, DNS errors, refused connections, HTTP 404, HTTP 500 and similar problems give the status fetch_error with the HTTP status code and the error message. Timeouts, network errors and HTTP 429 or 5xx responses are tried once more before they are reported.
  • The run finishes as Succeeded with a status message such as Checked 12 of 12 pages: 1 changed, 10 unchanged, 1 selector missing. Only input that cannot work, for example a URL without https:// or an invalid regular expression, fails the run, and the error message lists every problem.

Use cases

  • Price and stock changes on product pages (watch the price element only).
  • Terms of service, privacy policy and pricing pages of vendors you depend on.
  • Competitor pages: plans, features, landing pages, job openings.
  • Government, regulatory and grant pages that publish notices without a feed.
  • Documentation and changelog pages of tools you use.
  • New pages on a site, through its sitemap or RSS feed.
  • Your own site: catch content that disappears after a deploy (a missing selector is reported).

How to run it on a schedule

Each run compares with the previous run of the same monitor, so the Actor is meant to run on a schedule.

  1. Open the Actor in Apify Console, enter your pages and notification URLs, and click Start once. The first run stores a baseline.
  2. Click Save as a new task so the input is kept as a task.
  3. Go to Schedules, click Create new, and set how often to run, for example every hour (0 * * * *) or every morning (0 8 * * *). Choose your time zone.
  4. Click Add, choose Actor task, and pick the task you saved.
  5. Click Enable. New schedules are created disabled.

Runs started by the schedule use the same monitor ID as your first run, so they compare against it. If you edit the page list later and want to keep the history, set Monitor ID to a fixed name such as shop-prices before you edit.

Input example

{
"pages": [
{ "url": "https://www.example-shop.com/products/blue-mug", "selector": ".product-info", "label": "Blue mug" },
{ "url": "https://www.example-vendor.com/legal/terms", "label": "Vendor terms" },
{ "url": "https://www.example-agency.gov/notices", "selector": "main", "label": "Agency notices" }
],
"mode": "text",
"minChangePercent": 0,
"watchSitemap": "https://www.example-vendor.com/sitemap.xml",
"notifyOn": "changes",
"slackWebhookUrl": "https://hooks.slack.com/services/XXX/YYY/ZZZ",
"monitorId": "vendor-watch"
}

The main fields:

FieldWhat it doesDefault
pagesURL, optional CSS selector, optional label for each pagerequired
modetext (visible text), html (HTML without scripts and styles) or selector (text of the selector; every page needs one)text
ignorePatternsRegular expressions for text that should not count as a changetimestamps, dates, tokens, cache-busting parameters
minChangePercentReport a change only when at least this share of the content changed0
maxDiffLinesMost added and most removed lines kept per result50
watchSitemapSitemap, sitemap index, RSS or Atom URL to watch for new URLsempty
notifyOnchanges, always or errorschanges
webhookUrl, slackWebhookUrl, discordWebhookUrlWhere to send the run's message (stored encrypted)empty
monitorIdName of the stored historyderived from the page list
renderJsLoad pages in headless Chromefalse
requestTimeoutSecsTime limit per page30
proxyConfigurationOptional proxyno proxy

Output example

One dataset item per page per run:

{
"type": "page",
"url": "https://www.example-shop.com/products/blue-mug",
"finalUrl": "https://www.example-shop.com/products/blue-mug",
"label": "Blue mug",
"checkedAt": "2026-10-13T08:00:04.512Z",
"status": "changed",
"httpStatus": 200,
"previousCheckedAt": "2026-10-13T07:00:03.981Z",
"previousStatus": "unchanged",
"changePercent": 43.75,
"diff": {
"added": ["Price: $12.50"],
"removed": ["Price: $10.00"],
"addedCount": 1,
"removedCount": 1
},
"selector": ".product-info",
"mode": "text",
"contentLength": 31,
"message": "1 line added and 1 line removed (43.75% of the content).",
"error": null
}

A page whose selector stopped matching:

{
"type": "page",
"url": "https://www.example-vendor.com/pricing",
"status": "selector_missing",
"httpStatus": 200,
"selector": "#plans",
"message": "The selector was not found, so the page layout probably changed. The last good snapshot is kept and used again once the selector matches.",
"error": "No element matches the selector \"#plans\"."
}

With sitemap watching on, each run adds one item with "type": "sitemap". The first run has the status baseline. Later runs have the status new_urls with newUrls, newUrlCount, removedUrlCount and totalUrls.

The run summary (counts per status, pages not checked, notification results) is saved as OUTPUT in the run's key-value store. The JSON webhook receives the same summary plus every result.

Pricing

You pay $0.002 per page check, which is $2 for 1,000 checks. A page check is one page fetched and compared, including a page where the selector was not found. Pages that could not be fetched (fetch_error) are not charged, and a sitemap check counts as one page check.

Example: checking 20 pages once a day is about 600 page checks a month, or $1.20.

If you set a maximum cost for a run, the Actor stops starting new page checks when the next one would go over it. The run still finishes as Succeeded, the status message says that it stopped early, and the pages that were not checked are listed in the run summary.

FAQ

Why do I get selector_missing instead of a failed run? Because a selector that stops matching is information you need: the page changed its layout. The run finishes, the other pages are checked, and the message tells you which selector to fix.

The first run did not alert me. Is that right? Yes. The first check of a page stores a baseline. With notifyOn set to changes you get a message only when something changed or failed. Set notifyOn to always to get a message after every run.

How do I stop alerts caused by dates or counters on the page? Add a regular expression to ignorePatterns, or use a selector that covers only the part you care about. The default patterns already remove most timestamps and dates. If you do want to know when a date changes, remove the date patterns.

What does changePercent mean? The share of the content, by characters, that sits on added or removed lines. Changing one line out of a hundred similar lines is about 1%. Use minChangePercent to ignore small edits; small edits add up, because the stored copy is only replaced when a change is reported.

Where is the history stored, and how do I reset it? In a named key-value store in your Apify account called website-change-monitor-<monitor ID>. To start from a fresh baseline, change monitorId or delete that store.

Can it check pages that need a login? No. It checks public pages only.

The page builds its content with JavaScript. What do I do? Turn on renderJs. The page is then loaded in headless Chrome before it is compared. This is slower, and the default memory goes from 256 MB to 2 GB.

Can I watch a JSON API or a file? Yes. JSON responses are formatted and compared line by line. Other files, such as PDFs, are compared by their content hash: you learn that the file changed, without a line diff.

Does it send one message per page? No. Each run sends one message per channel with all pages that need attention, so a run that checks 50 pages does not flood your channel.

Limitations

  • Public pages only: no logins, cookies, forms or clicks.
  • Without renderJs, "visible text" is read from the HTML. Elements hidden by the hidden attribute or by an inline display: none style are skipped; elements hidden by CSS classes are still included.
  • Only the first 250,000 characters of a page (after cleanup) are stored and diffed. A change after that point is still detected, but without a line diff.
  • Responses larger than 10 MB are reported as fetch_error.
  • With the default 256 MB of memory, whole pages up to 10 MB can be watched. A CSS selector needs more memory on very large pages (roughly 3 MB of HTML or more); such a page is reported as fetch_error with a message that says so, and is not charged. Give the run more memory to check selectors on those pages.
  • Sitemap watching reads up to 50 sitemap files and 50,000 URLs.
  • Pages with bot protection may return a block page instead of the content. That shows up as a change. Try a proxy or renderJs.
  • Two runs with the same monitor ID at the same time can overwrite each other's saved copies. Give overlapping tasks different monitor IDs.
  • How often pages are checked depends on your schedule. This is not a real-time monitor.