Website Change Monitor — Sitemap Monitor & Page Diff
Pricing
from $0.43 / 1,000 checked pages
Website Change Monitor — Sitemap Monitor & Page Diff
Track website changes via sitemaps: finds new, removed and updated pages (lastmod) and detects page content changes with normalized hashes and word-level diff previews. Pay per page checked ($0.0005) and per change detected ($0.002).
Pricing
from $0.43 / 1,000 checked pages
Rating
0.0
(0)
Developer
Owlabee
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
4 days ago
Last modified
Categories
Features
Share

Website change monitor driven by sitemaps, and optionally by RSS/Atom feeds and the WordPress REST API: give it site, sitemap or feed URLs and each run reports new, removed and updated pages and which pages' content changed, with word-level diff previews. Opt-in modes add robots.txt and llms.txt monitoring, JSON-LD price and stock diffs, PDF text diffs and conditional requests. No browser is needed, so it is cheap to schedule daily for competitor, pricing-page, documentation or SEO monitoring.
What it does
Website Change Monitor discovers every page of a site through its sitemaps and tells you what changed since the last run. Give it site URLs or sitemap URLs; it follows robots.txt Sitemap: lines, sitemap indexes, plain and gzipped sitemaps (.xml, .xml.gz, .txt), and falls back to /sitemap.xml and /sitemap_index.xml. In discover mode it lists every page URL with lastmod, changefreq and priority. In monitor mode it keeps a snapshot per site in a key-value store and emits one record per change: new-url, removed-url, lastmod-changed, content-changed and status-changed.
Content changes are detected without a browser: each HTML page (max 1 MB, 20 s timeout) is scoped to a CSS selector (main, article, body by default), stripped of scripts, styles, comments, SVG and whitespace noise, optionally stripped of numbers and dates, and hashed with SHA-256. Every record also carries the page title, metaDescription, canonical, h1, wordCount, a 300-character textPreview and, when content changed, a compact word-level changedPreview (added/removed word counts plus the first words of each).
It is built for SEO teams watching their own or competitors' sites, product and pricing-page monitoring, documentation and compliance teams that need an audit trail of content changes, and AI agents that need a cheap, structured "what changed on this site" feed. Results are available as JSON, CSV, Excel, through the API, webhooks and the Apify MCP server.
More sources and checks, each off by default so saved inputs behave as before:
- Feeds and WordPress (
discovery). Addfeedto read the site's RSS, Atom or JSON feed (the URL itself, the feeds its page advertises with<link rel="alternate">, or/feed,/rss.xml,/atom.xml), andwordpressto list every post and page with its modification time from the WordPress REST API. Posts and press releases often reach a feed long before a sitemap. URLs found by several sources are checked once. - Watched files (
watchFiles). robots.txt, llms.txt, llms-full.txt and security.txt are compared line by line; afile-changedrecord lists the added and removed lines. A robots.txt that starts blocking the whole site, or a file that disappears, ishighseverity. - Structured data (
trackStructuredData). The page's schema.org JSON-LD (Product price and availability, JobPosting, Event, Organization, FAQ and more) is compared field by field, so price and stock changes show up on any platform without a CSS selector:structuredDataChanges: [{ "path": "jsonld.Product.offers.price", "before": 100, "after": 110 }]. - Documents (
checkDocuments). PDFs and text files (CSV, JSON, plain text) up to 512 KB are compared by their text with the same word-level preview as pages; larger or binary files by Last-Modified and size, or ETag. - Conditional requests (
conditionalRequests). Pages are requested with their stored ETag and Last-Modified; a304 Not Modifiedanswer is not downloaded again.
Watching Shopify stores? Shopify Catalog Monitor & Price Tracker — Products Scraper reads the catalog directly and reports price and stock changes per variant, with the same monitor-and-webhook pattern.
Why this Actor
| This Actor | Typical scraper in this category | Do it yourself | |
|---|---|---|---|
| Sources covered | Any site with a sitemap: robots.txt Sitemap: lines, sitemap indexes, gzipped and plain-text sitemaps, many sites per run; optionally RSS, Atom and JSON feeds and the WordPress REST API | A fixed list of page URLs you type in by hand | Your own crawler or a page-by-page checker |
| Modes | discover (every URL with lastmod, changefreq, priority) and monitor with five change types, with or without content hashing; opt-in watched files (file-changed), JSON-LD diff (structured-data-changed), PDF and text documents, conditional requests | Content diff of the listed pages | Whatever you build |
| Output fields | changeType, changedFields, title, metaDescription, canonical, h1, wordCount, textPreview, a word-level changedPreview and the full before/after snapshots | A diff or a screenshot per page | Raw HTML or hashes |
| Pricing unit | Per page checked, per detected change and per sitemap file fetched; nothing per site or per minute | Per page or per site checked, sometimes per run | Server time |
| Proxies or API keys | None | Often a proxy for sites that block, sometimes a model key for summaries | None |
| Agent / MCP ready | Yes: pin link, three prompts and a copy-paste call-actor block below | Sometimes | No |
| Limits | Sitemap-listed pages only, HTML up to 1 MB, no JavaScript rendering, robots.txt honoured | Browser rendering costs more per page and is slower | Whatever you build and maintain |
- The sitemap is the watch list. New and removed pages are found without you naming them, which is how you catch a competitor's new pricing tier or a quietly deleted docs page.
- Noise control built in. Scope the hash to a CSS selector, strip numbers and dates, and exclude URL patterns, so counters and ads do not trigger alerts.
- A real diff, not just a flag. Every
content-changedrecord carries the previous and current snapshot and a word-level preview of what was added and removed. - Cheap at scale. Pages are hashed over plain HTTP; a thousand-page site is one run of page checks plus one sitemap event.
- Safe baselines. A run that hits its spend cap or time budget does not overwrite the snapshot, so the next run compares against a complete baseline.
How to use
- Enter one or more Sites or sitemap URLs (
https://example.comorhttps://example.com/sitemap.xml). - Pick a Mode:
discoverto list the sitemap, ormonitorto track changes. Optionally narrow the pages with Include/Exclude URL patterns, set Max pages per site, a Content CSS selector, and a Webhook URL. - Click Start. The first monitor run writes
baselinerecords; schedule the Actor (hourly, daily, weekly) and every later run emits only the changes.
Quick start input
The same input the Store runs in its daily automated test, so it is cheap and always available; paste it into the JSON tab of the input form or send it as the body of an API or MCP call:
{"sites": ["https://www.sitemaps.org/sitemap.xml"],"mode": "discover","maxPages": 15}
Ready-to-run inputs
Complete inputs for three common monitors; paste one into the JSON tab and change the URLs. Schedule each one and the first run writes the baseline.
Pricing-page content monitor (keep numbers in the hash, because a price change is a number change):
{"sites": ["https://example.com/sitemap.xml"],"mode": "monitor","includePatterns": ["/pricing", "/plans"],"contentSelector": "main","ignoreNumbers": false,"stateStoreName": "example-pricing-watch","webhookUrl": "https://hooks.example.com/site-changes"}
Docs or changelog monitor with include patterns (numbers ignored so version badges and dates do not count):
{"sites": ["https://docs.example.com"],"mode": "monitor","includePatterns": ["*/docs/*", "*/changelog*"],"excludePatterns": ["/\\/docs\\/v\\d+\\//"],"contentSelector": "article, main","ignoreNumbers": true,"maxPages": 2000,"stateStoreName": "example-docs-watch"}
Blog or newsroom via its feed and the WordPress API (new posts often reach the feed before the sitemap; posts that roll off the feed are not reported as removed):
{"sites": ["https://wordpress.org/news/"],"mode": "monitor","discovery": ["feed", "wordpress"],"maxPages": 200,"stateStoreName": "wp-news-watch"}
robots.txt, llms.txt and security.txt watch, with price and stock alerts from JSON-LD (add checkDocuments for PDF price lists):
{"sites": ["https://example.com/sitemap.xml"],"mode": "monitor","includePatterns": ["/products/*"],"watchFiles": ["robots.txt", "llms.txt", "security.txt"],"trackStructuredData": true,"checkDocuments": true,"conditionalRequests": true,"stateStoreName": "example-products-watch"}
Sitemap-only new-URL monitor (no page requests; new-url, removed-url and lastmod-changed only):
{"sites": ["https://www.sitemaps.org/sitemap.xml"],"mode": "monitor","checkContent": false,"maxPages": 5000,"stateStoreName": "sitemaps-org-urls","notifyOnlyOnChanges": true}
Input
| Field | Type | Required | Description |
|---|---|---|---|
sites | string[] | yes | Site or sitemap URLs, one per line |
mode | discover | monitor | no | discover lists sitemap URLs; monitor diffs against the previous run (default monitor) |
checkContent | boolean | no | Monitor mode: fetch and hash each HTML page (default true). Off = sitemap-level changes only |
contentSelector | string | no | Comma-separated CSS selectors; first match is hashed (default main, article, body) |
ignoreNumbers | boolean | no | Strip digits, dates and times before hashing (default false) |
maxPages | integer | no | Max page URLs per site after filtering, in sitemap order (default 500) |
includePatterns | string[] | no | Keep only matching URLs: globs (*/blog/*), regexes (/\.html$/) or substrings |
excludePatterns | string[] | no | Drop matching URLs, same syntax |
maxConcurrency | integer | no | Parallel page fetches, 1–20 (default 5); same-host requests are spaced ≥ 500 ms |
stateStoreName | string | no | Named key-value store holding one snapshot per site (default website-change-monitor-state) |
resetBaseline | boolean | no | Ignore the stored snapshot and write a fresh baseline |
webhookUrl | string | no | POST target for the run summary and change list |
notifyOnlyOnChanges | boolean | no | Call the webhook only when changes were found (default true) |
discovery | string[] | no | Where page URLs come from: sitemap, feed (RSS/Atom/JSON Feed), wordpress (REST API). Default ["sitemap"] |
watchFiles | string[] | no | Monitor mode: also watch robots.txt, llms.txt, llms-full.txt, security.txt per origin (default none) |
trackStructuredData | boolean | no | Monitor mode with content on: compare each page's JSON-LD field by field (default false) |
checkDocuments | boolean | no | Monitor mode with content on: check PDFs, CSV, JSON and other non-HTML URLs instead of skipping them (default false) |
conditionalRequests | boolean | no | Monitor mode with content on: send ETag / Last-Modified and skip unchanged pages answered with 304 (default false) |
Output example

A discover record:
{"changeType": "discovered","site": "https://www.sitemaps.org/sitemap.xml","url": "https://www.sitemaps.org/protocol.html","lastmod": "2022-12-15","sitemapUrl": "https://www.sitemaps.org/sitemap.xml","detectedAt": "2026-10-16T12:49:14.231Z"}
changefreq and priority are included whenever the sitemap provides them:
{ "changeType": "discovered", "url": "https://nodejs.org/en/download/current", "lastmod": "2026-10-15T14:35:17.575Z", "changefreq": "always", "priority": 0.7, "...": "..." }
A content-changed record from a monitor run (before/after hold the full previous and current page snapshots):
{"changeType": "content-changed","site": "https://example.com","url": "https://example.com/pricing","changedFields": ["contentHash", "lastmod"],"title": "Pricing — Example","lastmod": "2026-10-16","status": 200,"contentHash": "3f9a1c…","changedPreview": {"addedWords": 3,"removedWords": 1,"addedPreview": "$59 SSO included","removedPreview": "$49","similarity": 0.94,"changedRatio": 0.06,"numbersChanged": true},"severity": "medium","severityReason": "6% of the text changed, numbers or prices changed","before": { "url": "https://example.com/pricing", "status": 200, "lastmod": "2026-09-01", "contentHash": "a81b…", "title": "Pricing — Example", "h1": "Pricing that scales", "wordCount": 412, "textPreview": "Pricing that scales Team plan costs $49 per month…", "checkedAt": "2026-10-09T12:00:04.101Z" },"after": { "url": "https://example.com/pricing", "status": 200, "lastmod": "2026-10-16", "contentHash": "3f9a1c…", "title": "Pricing — Example", "h1": "Pricing that scales", "wordCount": 414, "textPreview": "Pricing that scales Team plan costs $59 per month…", "checkedAt": "2026-10-16T12:00:03.877Z" },"detectedAt": "2026-10-16T12:00:09.000Z"}
changeType values: discovered, baseline, new-url, removed-url, lastmod-changed, content-changed, status-changed, structured-data-changed, file-changed, error. When a page changes in several ways at once you get one record whose changeType is the most significant change (status > content > structured data > lastmod) and whose changedFields lists all of them. Error items look like { "changeType": "error", "site": "...", "error": "NO_SITEMAP_URLS", "errorMessage": "No page URLs found. Tried: ..." } and are charged at the page-checked rate, because the work to discover the problem was done.
Records of the opt-in sources and modes (local runs on 2026-11-07; the structured-data-changed and file-changed records are shaped exactly as the unit tests produce them, with example values):
{ "changeType": "discovered", "site": "https://wordpress.org/news/", "url": "https://wordpress.org/news/2026/11/wordpress-7-2-beta-4/", "lastmod": "2026-11-03T15:50:08.000Z", "sitemapUrl": "https://wordpress.org/news/wp-json/wp/v2/posts", "detectedAt": "2026-11-07T18:23:08.985Z", "source": "wordpress", "title": "WordPress 7.2 Beta 4" }
{"changeType": "structured-data-changed","site": "https://example.com/sitemap.xml","url": "https://example.com/products/runner","changedFields": ["structuredDataHash"],"structuredDataChanges": [{ "path": "jsonld.ProductGroup.offers.availability", "before": "InStock", "after": "OutOfStock" },{ "path": "jsonld.ProductGroup.offers.price", "before": 100, "after": 110 }],"severity": "high","severityReason": "structured data changed: ProductGroup.offers.availability, ProductGroup.offers.price","detectedAt": "2026-11-07T12:00:00.000Z"}
{"changeType": "file-changed","site": "https://example.com/sitemap.xml","url": "https://example.com/robots.txt","file": "robots.txt","changedFields": ["contentHash"],"changedLines": { "addedLines": 2, "removedLines": 0, "added": ["User-agent: GPTBot", "Disallow: /"], "removed": [] },"severity": "medium","severityReason": "robots.txt: 2 line(s) added, 0 removed","detectedAt": "2026-11-07T12:00:00.000Z"}
Page records (in before and after) gain these fields only when the matching option is on: source, entryTitle and published (feed and WordPress URLs), etag, lastModified and notModified (conditional requests), structuredData and structuredDataHash (JSON-LD), documentType, fingerprintSource and pages (documents).
If the output saves you time, a review on the Store page helps other buyers find this Actor; the Store invites you to leave one after you have run it a few times.
Example webhook payload
When webhookUrl is set, the Actor POSTs one JSON document per run (content-type: application/json, 10 s timeout, 2 retries; a failed delivery is logged and recorded as webhookDelivered: false in the SUMMARY record, never fails the run). In monitor mode it is sent only when at least one change was found unless notifyOnlyOnChanges is off; in discover mode after every run. The same document, without actor, runId and the changes list, is stored as the SUMMARY key-value record. A monitor run that found a new page, a removed page, a price change and a page that started failing posts this:
{"actor": "aBcDeFgHiJkLmNoPq","runId": "rStUvWxYz01234567","mode": "monitor","sites": [{"site": "https://example.com/sitemap.xml","sitemapSource": "input","sitemapsFetched": 1,"urlsFound": 4,"truncated": false,"pagesChecked": 4,"baseline": false,"changes": 4,"changesByType": { "new-url": 1, "removed-url": 1, "content-changed": 1, "status-changed": 1 },"changesBySeverity": { "low": 0, "medium": 1, "high": 3 },"highestSeverity": "high","snapshotSaved": true}],"totalUrlsFound": 4,"totalChanges": 4,"changesBySeverity": { "low": 0, "medium": 1, "high": 3 },"highestSeverity": "high","charges": { "sitemap-fetched": 1, "page-checked": 4, "change-detected": 4 },"limitReached": false,"finishedAt": "2026-10-27T03:58:24.976Z","changes": [{"changeType": "new-url","site": "https://example.com/sitemap.xml","url": "https://example.com/new-page","changedFields": ["url"],"after": { "url": "https://example.com/new-page", "sitemapUrl": "https://example.com/sitemap.xml", "lastmod": "2026-10-20", "checkedAt": "2026-10-27T03:58:21.475Z", "status": 200, "contentType": "text/html", "contentHash": "575681cd…", "title": "New", "wordCount": 3, "textPreview": "Brand new page." },"title": "New","lastmod": "2026-10-20","status": 200,"contentHash": "575681cd…","detectedAt": "2026-10-27T03:58:21.451Z","severity": "medium","severityReason": "new page in the sitemap"},{"changeType": "status-changed","site": "https://example.com/sitemap.xml","url": "https://example.com/status","changedFields": ["status", "lastmod"],"before": { "url": "https://example.com/status", "sitemapUrl": "https://example.com/sitemap.xml", "lastmod": "2026-10-01", "checkedAt": "2026-10-27T03:58:17.853Z", "status": 200, "contentType": "text/html", "contentHash": "846aebb8…", "title": "Status", "wordCount": 3, "textPreview": "All systems operational." },"after": { "url": "https://example.com/status", "sitemapUrl": "https://example.com/sitemap.xml", "lastmod": "2026-10-20", "checkedAt": "2026-10-27T03:58:21.475Z", "status": 503, "contentType": "text/html", "error": "HTTP 503" },"lastmod": "2026-10-20","status": 503,"detectedAt": "2026-10-27T03:58:21.451Z","severity": "high","severityReason": "page now fails with 503 (was 200)"},{ "changeType": "removed-url", "url": "https://example.com/old-page", "severity": "high", "severityReason": "page removed from the sitemap", "...": "..." },{ "changeType": "content-changed", "url": "https://example.com/pricing", "severity": "high", "severityReason": "13% of the text changed, numbers or prices changed", "...": "..." }],"changesTruncated": false}
| Field | Meaning |
|---|---|
actor, runId | The Actor and run IDs, so the receiver can fetch the full dataset through the Apify API. |
mode | discover or monitor. |
sites[] | One entry per site in the input: sitemap files fetched, URLs found (truncated when maxPages cut the list), pages checked, whether this run was the site's baseline, changes with changesByType and changesBySeverity, the site's highestSeverity (null when nothing changed), snapshotSaved, and error or truncatedBy: "TIME_BUDGET" when the site did not finish. With feed or WordPress discovery, sources lists what each source read and any error; with watched files, filesChecked. |
totalUrlsFound, totalChanges | Sums over all sites. |
changesBySeverity, highestSeverity | Change records per severity across the run and the highest one (low, medium, high, or null); route or filter alerts on these two fields. |
charges | Events charged in this run, by event name. |
limitReached | true when the run stopped at its maximum cost; snapshots of unfinished sites were not saved. |
finishedAt | When the run finished (ISO 8601). |
changes[] | The change records exactly as written to the dataset (see the output example above), each with severity and severityReason; baseline, discovered and error items are not included. At most 1,000 records are sent; changesTruncated is true when the dataset holds more. |
Alerts and delivery
- Schedules. Create a schedule in the Apify Console (hourly, daily, weekly or a cron expression) with a
monitorinput; each run appends only the change records to its dataset. - The Actor's own
webhookUrl. One POST per run with the run summary and up to 1,000 change records, sent only when something changed unlessnotifyOnlyOnChangesis off; the full document, field by field, is in "Example webhook payload" above. Route or filter alerts onhighestSeverityandchangesBySeverity, or on each record'sseverityandseverityReason. Delivery has a 10 s timeout and two retries; a failing endpoint never fails the run and is recorded aswebhookDelivered: falsein the run'sSUMMARYkey-value record. - Apify integrations. On the Actor's Integrations tab you can attach Slack, Make, Zapier, Google Sheets, email or a generic Actor webhook that fires on run events (
ACTOR.RUN.SUCCEEDED,ACTOR.RUN.FAILED,ACTOR.RUN.TIMED_OUT) and carries the run and dataset ids, so any downstream tool can pull the items. - API and dataset endpoints. Start a run and wait for its items in one call with
POST https://api.apify.com/v2/acts/owlabee~website-change-monitor/run-sync-get-dataset-items?token=<APIFY_TOKEN>(the input JSON as the body), or start it asynchronously withPOST /v2/acts/owlabee~website-change-monitor/runsand readGET /v2/datasets/{datasetId}/items?format=json(alsocsv,xlsx) when it finishes. The run'sSUMMARYrecord is atGET /v2/key-value-stores/{storeId}/records/SUMMARYand includeswebhookDeliveredwhen a webhook was set.
Pricing
Pay per event. You only pay for what the Actor produces; set a maximum cost per run in the run options to cap spend.
| Event | Price | Notes |
|---|---|---|
| Checked page | $0.0005 | Per URL listed in discover mode, per baseline record on the first monitor run, and per page checked on later monitor runs. Error items included |
| Detected change | $0.002 | Per change record (new-url, removed-url, lastmod-changed, content-changed, status-changed) |
| Sitemap file | $0.001 | Per sitemap file requested: the sitemap index, each child sitemap, and the sitemap.xml / sitemap_index.xml probes when robots.txt names none; files that match nothing and requests that fail are charged too, robots.txt itself is not |
| Actor start | $0.00005 | Apify platform event |
Example: checking 1,000 pages from one sitemap ≈ $0.50 + $0.001 for the sitemap file ≈ $0.501; each detected change adds $0.002. A daily monitor of a 300-page site with one sitemap file and 5 changes per day ≈ $0.16/day. The first monitor run writes one baseline record per page and is charged at the page-checked price only.
Feed entries and WordPress posts are charged like sitemap URLs (one checked page each); reading the feeds and the WordPress API is not charged separately. Each watched-file check is one checked page (a baseline record on its first run), and structured-data-changed and file-changed records are detected changes like the others. JSON-LD, documents and conditional requests add no event.
Store pricing discounts: users on the Scale plan (Silver) pay 10% less per event, Business (Gold) 15–20% less.
Limits and fair use
- Only sitemap-listed pages are tracked; pages missing from the sitemap are not discovered.
maxPagescuts the list in sitemap order, so in monitor mode set it above the site's page count or the tail of the sitemap will look "removed". - Pages are fetched with plain HTTP from Apify datacenter IPs, HTML only, up to 1 MB each, 20 s timeout, at most ~2 requests per second per host, and
robots.txtDisallow rules are honoured (such pages getstatus: "robots-disallowed"). JavaScript-rendered content is not executed; use a selector that targets server-rendered markup. - Each site has a time budget (
maxSecondsPerSite, default 1,800 s, sitemap walk included) and an idle limit of 60 s (pages timing out, or sitemap files yielding no URLs that match your patterns). When either runs out, pages already checked are kept and charged, pages not reached keep their previous record (so nothing is reported as removed), and a baseline run that did not finish is not saved. If no page URL was found when the budget ran out, you get one error item witherror: "TIME_BUDGET", charged like the other error items. Compressed sitemaps are limited to 64 MB once unpacked. Default run memory is 512 MB. - The word-level
changedPreviewis computed on the first 5,000 characters of normalized text kept in the snapshot; the hash covers the whole selected element. - A snapshot is saved only after a run completes within the spending limit, so a run that hits its maximum cost does not corrupt the baseline; raise the limit and run again.
- Feeds are read up to 512 KB (the newest entries; a feed is newest first), and at most 3 advertised feeds per site; a page used only to find feeds or the WordPress API is read up to 128 KB. Entries that roll off a feed are dropped from the snapshot without a
removed-urlrecord. The WordPress REST API can be disabled by the site owner; then that source reports an error in the run summary and the other sources still run. With include or exclude patterns, the WordPress listing stops after 3 API pages (300 items) in a row without a matching URL. - Watched files are read up to 512 KB (a longer llms-full.txt is compared on its first 512 KB,
truncated: true), compared line by line with blank lines and trailing spaces ignored; an HTML page served instead of the file counts asnot-text. Files other than robots.txt follow its rules. - JSON-LD: up to 100 fields per page; images, URLs, ids and long descriptions are left out. Documents: PDFs and text files up to 512 KB are compared by their text (PDFs on their first 50 pages); a larger or binary file by Last-Modified with size, else a strong ETag, else a hash of its first 512 KB. Conditional requests trust the server's validators: a server that answers 304 for a changed page hides that change, which is why the option is off by default.
- Only public pages are accessed. No login, no proxies, no personal data beyond what the site publishes.
Related Actors
Other pay-per-event Actors from the same account, all limited-permission and agent-friendly:
- Shopify Catalog Monitor & Price Tracker — Products Scraper — price, stock and new/removed product change events for Shopify stores.
- Podcast RSS Scraper — Episodes, Transcripts & Apple Podcasts — new-episode monitoring for podcast RSS feeds, with transcripts.
- SPF, DMARC & DKIM Checker — Bulk Domain WHOIS, DNS & SSL — WHOIS, DNS, email security, TLS and tech stack for the domains you watch.
Guide and code examples
- Guide: Sitemap Monitor: Track New, Removed and Changed Pages, covering the source, its fields and limits, and curl and MCP examples.
- No-code recipe: Website Change Alerts in Slack with n8n, step by step with Apify's official n8n app and a ready input.
- Runnable examples (curl, Python, Node.js and an MCP config): owlabee/owlabee on GitHub.
Use from AI agents (MCP)
Pin this Actor in the Apify MCP server with one link: https://mcp.apify.com?tools=owlabee/website-change-monitor, or let the agent find it with the search-actors tool. It is pay-per-event with limited permissions, so agents can discover it, inspect the input schema with fetch-actor-details and pay per run through agentic payments. Prompts that map directly onto the input:
- "Which pages on example.com were added, removed or changed since last week? Use the Website Change Monitor in monitor mode and summarize the content-changed records."
- "Watch https://competitor.com/pricing and https://competitor.com/features daily and tell me when their content changes — ignore numbers so view counters don't trigger alerts."
- "List every URL in acme.com's sitemap with its last-modified date and flag anything under /blog/ modified in the last 30 days."
The agent receives the same dataset records shown in the output example above.
Copy this block to call it through the Apify MCP server with the quick-start input; the agent gets the dataset items back in the tool result and can follow up with get-dataset-items for larger runs:
{"tool": "call-actor","arguments": {"actor": "owlabee/website-change-monitor","input": {"sites": ["https://www.sitemaps.org/sitemap.xml"],"mode": "discover","maxPages": 15}}}
FAQ
Does this need proxies or an API key? No. It reads public sitemaps and pages over plain HTTP from Apify's standard infrastructure. If a site blocks datacenter traffic, its pages come back as status-changed/fetch-error records rather than failing the run.
How is cost controlled? Cost = pages checked × $0.0005 + changes × $0.002. Limit pages with maxPages and includePatterns/excludePatterns, turn off checkContent for a sitemap-only check, and set the run's maximum total charge; the Actor stops cleanly when it is reached.
What happens on the first monitor run? There is nothing to compare against, so every page becomes a baseline record (charged as a page check). Changes appear from the second run on. Use resetBaseline to start over, or a different stateStoreName to keep separate baselines.
How do I avoid false positives from counters, dates or ads? Set contentSelector to the element that holds the real content (for example article or #main-content) and turn on ignoreNumbers. Scripts, styles, comments, SVG and whitespace differences are already ignored.
How is severity decided? Every change record carries severity (low, medium or high) and a one-phrase severityReason, so you can route alerts without reading each record. A page removed from the sitemap, or a page that starts failing (a non-2xx status, fetch-error or robots-disallowed), is high; a new page in the sitemap is medium; a lastmod change with unchanged content is low. A content change is scored by changedPreview.changedRatio, the share of words that differ between the previous and the current text sample: under 10% is low, from 10% medium, from 50% high; it is raised one level (never above high) when the page title or H1 changed, when numbers or prices changed and ignoreNumbers is off, or when the status code changed. A content change that cannot be sized (an older snapshot without a text sample) is medium. Severity does not affect the price: each change record is charged as one detected change. The run summary and the webhook payload add changesBySeverity and highestSeverity.
How are the newer change types scored? structured-data-changed is high when a JSON-LD price or availability changed and medium otherwise; a content-changed record whose JSON-LD price or availability also changed is raised one level. file-changed is high when the file now fails or robots.txt starts or stops blocking the whole site, medium when the file appeared or lines changed, and low when only comment lines changed.
What do feeds, WordPress, watched files and documents cost? See "Pricing": feed and WordPress URLs and watched-file checks are checked pages, their changes are detected changes, and nothing else is added.
Can an AI agent use this? Yes; see "Use from AI agents (MCP)" above for the pin link and example prompts. Schedules and webhooks work the same way from the Console or the API.
Does it work with sitemap indexes and gzipped sitemaps? Yes: sitemap indexes are followed recursively, .xml.gz files are decompressed, plain-text sitemaps are accepted, and robots.txt Sitemap: lines are used when you give a site URL.
What happens on errors? A site without a usable sitemap or an invalid URL produces an error item with a clear code instead of failing the whole run; a page that cannot be fetched is recorded with status: "fetch-error" and compared normally next time.
Can I try it for free? Yes. The Apify free plan comes with monthly platform credit, and the quick-start input above costs a few cents per run, so you can test it on your own inputs before paying anything.
How do I find this Actor again? Bookmark it on the Store page (the bookmark icon next to the title); bookmarked Actors are listed in your Console, and the Store page shows new versions as they ship.
Support
Questions, bugs or a source you'd like added? Open an issue on the Actor's Issues tab, or email jacob@owlabee.com.
Changelog
- 2026-11-07 — New opt-in sources and checks; saved inputs run exactly as before.
discoveryadds RSS/Atom/JSON feeds and the WordPress REST API to sitemaps;watchFilesmonitors robots.txt, llms.txt, llms-full.txt and security.txt (file-changed);trackStructuredDatadiffs JSON-LD (structured-data-changed, price and availabilityhigh);checkDocumentsdiffs PDFs and text files;conditionalRequestssends ETag / Last-Modified and skips pages answered 304. - 2026-10-27 — README: comparison table, ready-to-run inputs, alerts and delivery, MCP call example, changelog.
- 2026-10-27 —
severity(low,medium,high) andseverityReasonon every change record,changesBySeverityandhighestSeverityin the run summary and webhook,changedRatioandnumbersChangedinchangedPreview; webhook payload documented with an example. - 2026-10-25 — Pricing round (in the Console from 2026-10-26): new per-sitemap-file event for every sitemap index, child sitemap and probe requested, event titles pluralized, Store discount tiers for the Scale and Business plans; figures in the pricing table.
- 2026-10-23 — Per-site time budget (
maxSecondsPerSite) with an idle limit, request deadlines that include retries, chargedTIME_BUDGETerror items, unfinished baselines are not saved, and a unit-economics test. - 2026-10-23 — Store listing: SEO fields, quick start input, MCP section, pricing-page and docs-change task examples.
- 2026-10-17 — Published on the Apify Store with pay-per-event pricing; charged events verified on a platform run.
- 2026-10-16 — First release:
discoverandmonitormodes, sitemap index and gzip support, content hashing with CSS selector and number stripping, include/exclude patterns, webhook delivery.
Legal
Use the data in compliance with the source's terms and applicable law. The Actor honours robots.txt, keeps request rates polite and only reads publicly available pages. It is not a consumer reporting agency and its output must not be used for employment, credit, insurance, or tenant screening decisions.