Page Watch — Website Change Monitor & Content Tracker avatar

Page Watch — Website Change Monitor & Content Tracker

Pricing

from $1.00 / 1,000 page checkeds

Go to Apify Store
Page Watch — Website Change Monitor & Content Tracker

Page Watch — Website Change Monitor & Content Tracker

Monitor any URL for content changes with hash-based change detection. Pay per check + per detected change. Ideal for watching competitor pages, docs, pricing, or any public web page.

Pricing

from $1.00 / 1,000 page checkeds

Rating

0.0

(0)

Developer

Mariano Ferreras

Mariano Ferreras

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

10 hours ago

Last modified

Share

Page Watch — Website Change Monitor

Pay-per-event pricing. Fetches URLs, hashes their content, and compares against the previous run's hash stored in Apify's KeyValueStore. Per-URL CSS selectors scope the check to specific page sections. A noise threshold (minChangeRatio) suppresses small changes from rotating widgets or ad slots. Several competitors offer similar features — this is a commodity market; pick based on price and reliability. $0.001/check + $0.01/change.

The most important limitation: ultimate page-watch is trivial to DIY — a cron job + curl | sha256sum. You're paying for the hosted scheduler, KVS persistence across runs, and the CSS-selector noise-threshold UX.


What it does

This actor fetches one or more URLs, extracts the page text (optionally scoped to specific sections via CSS selectors), computes a content hash, and compares it against the previous check's hash stored in the actor's persistent KeyValueStore. An optional noise threshold (minChangeRatio, using Dice coefficient over word multisets) ignores small content churn so you only get charged for changes that matter. Each result includes the hashes and a diff excerpt around the first divergence. Several competitors offer similar features (e.g. foo121/website-change-monitor has CSS selectors + noise-filtered diffing, ryanclinton/website-change-monitor has 31 users).

Run it once to establish a baseline, then schedule it (e.g. every hour, daily) to monitor web pages continuously. Because it uses Apify's built-in KeyValueStore, the hash state persists across scheduled runs — no external storage needed. If you're doing competitor page tracking at scale, batch multiple URLs into a single run to work around Apify's schedule limit (100 per account).


Use cases

  • Competitor page tracking — watch competitor landing pages, product pages, or docs for changes. Get notified when they update pricing, features, or copy.
  • Price tracking — watch pricing pages on e-commerce or SaaS sites. Detects when prices go up or down.
  • Documentation change alerts — monitor API docs, terms of service, or compliance pages. Know the moment something changes.
  • Regulatory / legal monitoring — watch government pages, procurement portals, or regulatory sites for new announcements.
  • Agent-powered watchlists — wire this into an AI agent workflow: have your agent set up monitors, check diff excerpts, and summarize what changed.

Input example

Watch only a price div on one page, the full body on another, and ignore under-2% churn everywhere:

{
"urls": [
{ "url": "https://example.com/pricing", "selector": ".pricing-table" },
"https://example.com/docs"
],
"minChangeRatio": 0.02,
"maxItems": 50,
"useResidentialProxy": false
}
FieldTypeDefaultDescription
urlsarrayrequiredOne or more URLs to check. Each entry can be a plain URL string, { "url": "..." }, or { "url": "...", "selector": "..." } to scope just that URL to a CSS selector
defaultSelectorstringnoneCSS selector applied to any URL that doesn't set its own selector. Example: .pricing-table
minChangeRationumber0Noise threshold in [0, 1]. 0 (default) reports every content hash difference, same as before. 0.02 ignores under-2% content churn — the hash still notices the diff, but it won't be reported as changed or charged unless the changed fraction meets the threshold
selectorsarray[]Legacy, global multi-selector mode — applied only to URLs with no per-URL selector and no defaultSelector. Kept for backward compatibility
maxItemsinteger50Max URLs to check in this run. 0 = unlimited
useResidentialProxybooleanfalseUpgrade to residential proxies if the target blocks datacenter IPs

Per-URL selectors — behavior

  • Selector resolution order per URL: its own selectordefaultSelector → the legacy global selectors array → full page body.
  • A bad selector never produces a silent phantom "change". If a per-URL/default selector is invalid CSS or matches no content on the page, the actor logs a warning, sets selectorWarning on that URL's result row, and falls back to hashing the full page instead of a stable empty string.
  • Switching a URL's selector (adding, removing, or changing it) changes what's being tracked, so the very next check may legitimately report one change as the new baseline is established — this is expected, not a bug.

Output example

[
{
"url": "https://example.com/pricing",
"checkedAt": "2026-07-23T23:00:00.000Z",
"changed": true,
"hash": "a1b2c3d4e5f6g7h8",
"previousHash": "9i8j7k6l5m4n3o2p",
"title": "Example — Pricing",
"contentLength": 4523,
"selector": ".pricing-table",
"selectorWarning": null,
"diffSnippetPrevious": "...our starter plan is $10 per month with...",
"diffSnippetCurrent": "...our starter plan is $15 per month with...",
"changeRatio": 0.18,
"belowChangeThreshold": false
}
]
FieldTypeNotes
urlstringThe URL that was checked
checkedAtstringISO 8601 timestamp of the check
changedbooleantrue only when the content hash differs from the previous check AND (if minChangeRatio is set) the changed fraction meets the threshold
hashstringSHA-256 hex digest (first 16 chars) of the current extracted content
previousHashstring|nullHash from the previous check. null on the first run
titlestring|nullPage <title> content
contentLengthintegerCharacter count of the extracted text
selectorstring|nullCSS selector actually used to extract content (per-URL, defaultSelector, or the legacy selectors list joined). null for full-page checks
selectorWarningstring|nullSet when a per-URL/default selector was invalid or matched nothing — the check fell back to full-page text. null when no issue occurred
diffSnippetPreviousstring|nullExcerpt of the previous content around the first difference. null when unchanged or first check
diffSnippetCurrentstring|nullExcerpt of the current content around the first difference. null when unchanged or first check
changeRationumber|nullFraction of content changed vs. the previous check (0-1), computed only when the hash differs and a previous check exists. null otherwise
belowChangeThresholdbooleantrue when the hash differed but changeRatio was below minChangeRatio — a real change was detected but suppressed as noise (not reported, not charged)

Pricing

This actor uses pay-per-event pricing. You are never charged for failed requests or quarantined results.

EventPriceDescription
page-checked$1.00 / 1,000 checksCharged once per URL checked, regardless of whether it changed
page-changed$10.00 / 1,000 changesCharged once per detected change (in addition to the check fee)

The page-changed event is only triggered when content actually differs from the previous check. Use a small maxItems for test runs. At $0.001/check, monitoring 10 URLs daily costs ~$0.30/month.


Scheduling

Page Watch is designed to be run on a schedule. Create a schedule in Apify Console (e.g. hourly, daily) and the actor will automatically compare against the previous run's hashes stored in its KeyValueStore.

To watch multiple URLs, batch them into a single run using the urls input array. This works around Apify's schedule limit (100 per account) and is more efficient than one schedule per URL.


Use with AI agents (MCP)

This actor is available as a tool for AI assistants via the Apify MCP server. Agents can call it to monitor web pages for content changes directly — no Apify console needed, just a single function call.

MCP setup

{
"mcpServers": {
"apify": {
"command": "npx",
"args": [
"mcp-remote",
"https://mcp.apify.com?tools=mfapitools/page-watch",
"--header",
"Authorization: Bearer <YOUR_API_TOKEN>"
]
}
}
}

Example agent prompts

  • "Monitor these five competitor pricing pages and tell me if any changed"
  • "Set up change detection for this government procurement portal URL"
  • "Check all my watched URLs and summarize the diffs"

The actor returns structured JSON with the same fields shown above. Agentic workflows (Claude Code, Cursor, any MCP-compatible AI) can monitor pages and analyze change excerpts in the same run.


Limitations

  • Text-based comparison — the actor extracts normalized body text (with <script>, <style>, <noscript> stripped to avoid false positives from inline nonces/timestamps) and computes a hash. On change, you get a small excerpt around the first divergence — not a full structural or visual diff, and not an LLM "what changed" summary (planned for a future version).
  • First run is always a baseline — the first check of any URL will show changed: false because there is no previous hash. The second run detects changes.
  • minChangeRatio compares only the first ~10,000 characters of extracted text from each side (the same window used for diff-snippet storage) — a size-bounded, cheap comparison. For most pages this is representative of the whole page; for very large pages, changes past that window won't move the ratio.
  • Public data only — this actor fetches public URLs. No login-gated content.
  • No notification delivery in v1.0 — changes are recorded in the dataset. You can wire up webhooks or Apify's email/slack integrations in Console to get notified when new results arrive.
  • Hash collisions are theoretically possible — using SHA-256 truncated to 16 hex chars (64 bits). The collision probability is negligible for this use case (~2^-32 for 100k checks).

Changelog

See ./CHANGELOG.md.