Website Change Monitor: Page Change Detection & Diffs
Pricing
from $2.10 / 1,000 changed pages
Website Change Monitor: Page Change Detection & Diffs
Watch web pages for changes, a website change tracker: give it page URLs (or a dataset of them) and each scheduled run returns only the pages that changed, with a line-by-line diff, the sections affected, and added and removed text. Unchanged pages cost less. Selector, ignore and keyword filters.
Pricing
from $2.10 / 1,000 changed pages
Rating
0.0
(0)
Developer
Michael Costa
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
15 hours ago
Last modified
Categories
Share
What does Website Change Monitor do?
Website Change Monitor watches web pages for changes. Give it the pages you care about; each run compares every page with what it saw last time and returns only the pages that changed, with a line-by-line diff, the sections the change is under, and the text that was added and removed. In short: change detection and a website change tracker for the pages you choose.
Run it on a schedule and send the changes to Slack, email or a webhook: a competitor's pricing page, a supplier's terms, a product's documentation, a tender or job page, a policy you have to track. It reads pages like a normal visitor, without a browser, and it is not a screenshot or visual-diff tool: it compares the text.
Input fields · API · Use it from Claude, ChatGPT or Cursor (MCP)
Jump to: Alerts · Fields · Price · How to use · Input · Output · AI agents (MCP) · Limits · FAQ
Try it in one click: the input comes pre-filled with Wikipedia's Main Page, which changes every day. The first run records it (1 page, $0.003, plus $0.00005 for the run start); run it again tomorrow to see what changed. Then replace it with the pages you actually want to watch.
Get page changes in Slack, email or a webhook
- Put your pages in Pages to watch and click Start. This first run returns every page as
new: it's the baseline the next runs compare with. - Click Save as a new task (top right of the actor page). A task keeps its own memory, so it always compares with its own last run.
- In Apify Console, open Schedules, click Create new, set how often (for example every day at 07:00, or every hour), then Add your task.
- On the task, open the Integrations tab and pick where the changes go:
- Slack: click Configure, sign in, pick the channel and the "run succeeded" event. A useful message:
{{resource.statusMessage}}and a link to the changes,<https://console.apify.com/storage/datasets/{{resource.defaultDatasetId}}|page changes>. Each row'ssummaryreads like "1 line added, 1 removed in “Plans”". - Gmail: click Connect with Google, set the subject and body, and attach the dataset (for example as CSV).
- HTTP webhook: event
ACTOR.RUN.SUCCEEDED, your URL. Apify POSTs{"eventType": ..., "resource": {...}};GET https://api.apify.com/v2/datasets/<resource.defaultDatasetId>/items?format=json(with your API token) returns the changed pages.
- Slack: click Configure, sign in, pick the channel and the "run succeeded" event. A useful message:
Apify's integrations fire after every successful run, including quiet ones: a quiet run's dataset is empty, and its status message says how many pages were unchanged.
A page that fails is never reported as a change. When a page is down, blocked by robots.txt or a bot check, or
needs JavaScript, it isn't compared: it's listed as failed in the log and RUN_STATS, isn't charged, and the next run
compares with the last text it really had. A page that answers 404 or 410 after being seen before is reported once as
removed, and as changed if it comes back.
What data does Website Change Monitor return?
One row per page that is new, changed or removed since the last run:
| Field | Example | Notes |
|---|---|---|
url | https://example.com/pricing | The page as you listed it |
status | changed | new (first check), changed, or removed (404/410) |
summary | 1 line added, 1 removed in “Plans” | One line for an alert |
changedSections | ["Plans"] | The headings the changes are under |
added / removed | ["Starter costs $12 a month."] | The changed lines (first 100) |
linesAdded / linesRemoved | 1 / 1 | Complete counts; null for a new page |
diff | -Starter costs $10… | Unified diff, 2 lines of context; null for a new page |
matchedKeywords | ["price"] | With Only report changes that mention; null without |
text | ## Plans\nStarter costs… | The watched text now (first 20,000 characters) |
title, finalUrl | The page's title, and its address after redirects | |
checkedAt, previousCheckedAt, previousChangeAt | 2026-10-02T04:44:00Z | When it was checked, last checked, and last changed |
The full list is under Output.
How much does it cost to monitor a website for changes?
You pay per page checked, and a page that didn't change costs half as much as one that did:
- $3.00 per 1,000 changed pages: a row in your results, for a page that is new (its first check), changed or removed;
- $1.50 per 1,000 unchanged pages: a page checked, found unchanged and not returned;
- plus $0.00005 each time a run starts.
The changed-page price is lower on paid Apify plans: $2.70 (Starter), $2.40 (Scale), $2.10 (Business) per 1,000.
Pages that fail (down, blocked, not found on the first check, JavaScript-only, a selector that matches nothing) are never charged.
- The pre-filled example: the first run is 1 new page, $0.003; a later run is 1 changed page, $0.003.
- A month, for example: 50 competitor pages checked daily, 3 of them changing on a typical day: 30 × (47 × $0.0015 + 3 × $0.003) + the start fees ≈ $2.39. The same 50 pages every hour, with the same 3 changes a day: ≈ $54.17.
- Caps: Max pages per run in the input, and Maximum cost per run in the run options. The run stops cleanly at whichever comes first; pages not checked are checked by the next run.
How to monitor a web page for changes
- Open Website Change Monitor and click Try for free (or Start if you're signed in).
- Put the pages in Pages to watch, one per line.
- Optional: pick the Part of the page to watch, a CSS selector, text to ignore, or words to watch for.
- Click Start. The first run records every page; open the Output tab to see them.
- Save it as a task and schedule it (Alerts); each later run returns only what changed.
Example: a busy page, and how to quiet it
Two pages, checked twice, 2 minutes apart (real output from local runs on 2026-10-02):
{"urls": ["https://en.wikipedia.org/wiki/Main_Page", "https://news.ycombinator.com/"],"monitorName": "readme-example"}
The first run returned both pages as new (2 × $0.003 = $0.006). The second said "2 of 2 pages checked (compared
with the run of 2026-10-02 04:54 UTC): 1 changed, 0 new, 0 removed, 1 unchanged" and returned one row, for $0.0045
($0.003 for the changed page, $0.0015 for the unchanged one). Shortened (usernames replaced by …, 2 of 8 lines
shown):
{"id": "0f63a2a5a5620b745938e6a2","url": "https://news.ycombinator.com/","title": "Hacker News","status": "changed","summary": "8 lines added, 8 removed in “Hacker News”","changedSections": ["Hacker News"],"linesAdded": 8,"linesRemoved": 8,"added": ["| | 42 points by … 1 hour ago \\| hide \\| 14 comments | |","| | 44 points by … 2 hours ago \\| hide \\| 14 comments | |", "..."],"removed": ["| | 41 points by … 1 hour ago \\| hide \\| 14 comments | |","| | 43 points by … 2 hours ago \\| hide \\| 13 comments | |", "..."],"diff": "--- before\n+++ after\n@@ -4,9 +4,9 @@\n ...\n-| | 41 points by … 1 hour ago ...\n+| | 42 points by … 1 hour ago ...","wordCount": 891,"checkedAt": "2026-10-02T04:55:53Z","previousCheckedAt": "2026-10-02T04:54:08Z","previousChangeAt": null}
Every changed line was a vote or comment count, which is noise for most people watching a page like this. Adding
Ignore text matching \d+ points?, \d+ comments? and \d+ (minute|hour|day)s? ago turns those into …;
the next run compares afresh (not reported), and after that only stories that appear, disappear or move are
changes. Wikipedia's Main Page didn't change in those 2 minutes; it changes daily.
Input
| Field | What it does |
|---|---|
Pages to watch (urls) | The pages, one per line; https:// is added when missing. Up to 2,000 per run. |
Or: page URLs from a dataset (datasetId) | Another actor's results, e.g. every page Sitemap URL Extractor found on a site. urls is then ignored. |
Part of the page (contentScope) | main (default): the main content, without menus, headers, footers and sidebars. page: all visible text. |
Only this part of the page (cssSelector) | A CSS selector such as #pricing or .product-price; only matching elements are compared. |
Ignore text matching (ignorePatterns) | Regular expressions for text that changes without meaning anything (view counts, "updated 5 minutes ago"); each match becomes … before comparing. |
Only report changes that mention (watchFor) | Words or phrases; a change is returned only when an added or removed line contains one. |
Count changed link addresses as changes (includeLinks) | Off by default: link text is compared, not its address (tracking parameters change on every visit). |
Monitor name (monitorName) | Which memory to compare with. Tasks get their own automatically; set it when you run several monitors without tasks. |
Max pages per run (maxResults) | Caps the pages checked in one run. |
{"urls": ["https://example.com/pricing", "https://example.com/terms"],"contentScope": "main","ignorePatterns": ["\\d+ (views|comments)"],"watchFor": ["price", "$"]}
Output
Every row has the same fields; a field that doesn't apply is null (for example diff on a new page, text on a
removed one).
| Field | Type | Meaning |
|---|---|---|
id | string | Stable id of the page (from its URL), the same on every run |
url | string | The page as you listed it |
finalUrl | string or null | Its address after redirects |
title | string or null | The page's title |
status | string | new, changed or removed |
summary | string | What changed and where, in one line |
changedSections | array | The headings the changes are under; (top of page) before the first heading |
linesAdded, linesRemoved | integer or null | Complete counts of changed lines |
added, removed | array | The changed lines: the first 100, each up to 1,000 characters |
diff | string or null | Unified diff with 2 lines of context, up to 30,000 characters |
diffTruncated | boolean | The diff was cut (the counts stay complete) |
matchedKeywords | array or null | The watched words found in the changed lines |
text | string or null | The watched text now, as Markdown lines, up to 20,000 characters |
textTruncated | boolean | text was cut at 20,000 characters |
textCutAtLimit | boolean | The page has over 200,000 characters of text; only the first 200,000 are compared |
wordCount | integer or null | Words in the watched text |
contentHash, previousContentHash | string or null | Hashes of the watched text now and last time |
checkedAt | string | When this run checked the page (ISO 8601, UTC) |
previousCheckedAt, previousChangeAt | string or null | When this monitor last checked it, and last saw it change |
sourceTitle, sourcePlaceId, sourceIndex | With a dataset: the source item's title, placeId and position |
The run's RUN_STATS record lists every page with its status (new, changed, removed, unchanged) or the reason
it failed, and the totals (pagesChanged, pagesUnchanged, ...).
Run it on a schedule, or from your own code
- Save your input as a task and add it to a schedule; the steps are under Alerts.
- Collect results: download the dataset as JSON, CSV or Excel; fetch the latest run's changes from the
API
(
GET https://api.apify.com/v2/actor-tasks/<task id>/runs/last/dataset/items?status=SUCCEEDED&format=json, with your API token); let a webhook tell your system when a run succeeds; or connect it to Make, Zapier or n8n through Apify's integrations.
Can I use Website Change Monitor from an AI agent (MCP)?
Yes, through Apify's MCP server, from Claude, ChatGPT, Cursor or any other MCP client. Add this to your client's MCP configuration; your client signs you in to Apify:
{"mcpServers": {"apify": {"url": "https://mcp.apify.com?tools=humble-echidna/website-change-monitor"}}}
To use an Apify API token instead of signing in, add
"headers": {"Authorization": "Bearer <APIFY_TOKEN>"} next to url.
The agent passes the pages and a monitor name of its own, e.g.
{"urls": ["https://example.com/pricing"], "monitorName": "agent-pricing"}, and reads summary and added /
removed for each changed page. An empty result means nothing changed since its last call with that name.
Who it's for
Product, marketing and sales teams watching competitors' pricing, plans and feature pages; procurement and legal teams tracking suppliers' terms, policies and price lists; anyone waiting for a page to change (a tender, a job, a "coming soon" or out-of-stock page). The recurring job: the same list of pages, checked every day or every hour, with an alert only when something actually changed.
Why this one?
- Unchanged pages cost half. A page that didn't change is charged $1.50 per 1,000, a changed one $3.00, so checking often costs less than a flat per-check price. No compute, proxy or storage charges on top, apart from the $0.00005 run-start fee.
- Changes you can act on. Each change says which section it's in and which lines were added and removed, plus a unified diff, not just "the page changed".
- Less noise. Main content only by default (a new menu item or footer line isn't a change), link addresses ignored by default, your own ignore patterns, and an optional keyword filter.
- Failures aren't changes. A page that's down, blocked or replaced by a bot check isn't compared or charged; the next run compares with its last real text.
- Polite and safe. It identifies itself honestly (User-Agent
HumbleEchidnaApify), follows each site's robots.txt and Crawl-delay, makes at most 2 requests per site at once, and only requests public web addresses on the standard ports (80 and 443). - Reliable. One failing page never affects the others. The log and
RUN_STATSsay which page had a problem and why.
Limits
- No browser: pages that build their text with JavaScript are reported as failed (not charged). Most public pages (pricing, docs, terms, news, listings) are served as HTML and work.
- Text only: a changed image, color or layout isn't a change unless its text (or, with
includeLinks, a link address) changes. - Up to 2,000 pages per run and 200,000 characters of text per page; split bigger sites into several tasks.
- No logins, no proxies: pages behind a login, and sites that refuse automated visitors or answer with a bot check, are reported and not charged.
FAQ
How does Website Change Monitor know what changed since the last run?
It keeps each monitor's memory in a key-value store named website-change-monitor-memory in your own Apify account:
per page, a hash of its watched text, and the text itself when it changed. Each run compares with that and moves the
memory on only for the pages you got a result for, so a run stopped by your maximum cost reports the change next time.
Why is a page I didn't change reported as changed?
Usually a counter, date or rotating block in the page's main content. Add an Ignore text matching pattern for it
(for example \d+ views or updated .* ago), watch only the part you care about with a CSS selector, or use
Only report changes that mention. Changing these settings compares the affected pages afresh on the next run
(not reported, charged as unchanged), so you don't get a burst of false changes.
Can I watch a whole website?
Yes: run Sitemap URL Extractor on the site and pick its dataset in
Or: page URLs from a dataset, up to 2,000 pages per run. A page that newly appears in the list is reported as
new.
Why did a page come back as "blocked by robots.txt" or "needs JavaScript"?
The site's robots.txt disallows automated visitors on that page, or the page has no text until a browser runs its
scripts. Both are listed in the log and RUN_STATS, and neither is charged.
Does it run on Apify's schedule, or does it watch continuously?
It checks when it runs. Schedule it as often as you need (hourly is common); each run is quick for a few dozen pages.
Is it legal to monitor a website for changes?
It reads public pages anyone can open, without logging in, identifies itself honestly and follows each site's robots.txt. What you do with the text is up to you and the site's terms; don't use it to republish someone else's content.
Related actors
| Actor | Use it when |
|---|---|
| Sitemap URL Extractor | You want to watch every page of a site: it lists the URLs to feed in. |
| Website & Page to Markdown for RAG and LLMs | You want the full pages as clean Markdown, for example to refresh a knowledge base. |
| Product Price & Stock Monitor | The pages are product pages and you want price and stock as fields. |
Feedback and support
Found a bug, or a page that's reported as changed when it isn't? Open an issue on the Issues tab with the input you used.
Versions
Current version: 0.1. See the Changelog tab for what changed in each version.
