Website Change Monitor & AI Alerts
Pricing
$3.50 / 1,000 page checks
Website Change Monitor & AI Alerts
Monitor public web pages for price, policy, product, job, and content changes. Get structured diffs, optional AI summaries, and webhook alerts.
Pricing
$3.50 / 1,000 page checks
Rating
0.0
(0)
Developer
Inus Grobler
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
8 days ago
Last modified
Categories
Share
Monitor public web pages for the changes that matter. Paste one or more URLs, run the Actor once to create a baseline, then run it again—or schedule it—to receive clear, structured change results.
Use it to track price updates, product availability, policy and legal pages, job listings, documentation, public announcements, and status pages. It can return concise change summaries, detailed added and removed text, optional AI classifications, and webhook alerts for your workflows.
Start in three steps
- Add the public page URLs you want to watch.
- Run the Actor. The first successful run saves a baseline for each page.
- Run the same input again or schedule it. Later runs show whether the page changed and why.
You do not need to configure storage, timeouts, retries, or AI settings for a normal monitoring workflow. Each URL and page-selection setting keeps its own comparison history automatically.
Simple input
Paste URLs into the Pages to monitor field in the Apify Console, or send the same small JSON object through the API:
{"urls": ["https://example.com/pricing", "https://example.com/terms"]}
The first result for a page has changeType: "baseline". That is expected: it means the Actor saved the starting version for later comparison.
Pricing
$3.50 per 1,000 successful page checks ($0.0035 each). Error rows are free. With Only return meaningful changes enabled, unchanged checks filtered from the dataset are also free.
When optional AI is enabled and a detected change is sent for classification, the Actor reserves one page-check charge before contacting the provider. That same charge covers the resulting dataset row, so it is never charged twice. The charge still applies when AI classifies a change as cosmetic and Only return meaningful changes filters the row, because the AI work was completed. Apify platform usage can be billed separately according to the client's Apify plan and the Actor's current platform-usage billing setting.
Set a maximum spend for each run in Apify to keep a firm budget. The Actor stops processing further results as soon as the page-check spending limit is reached.
Optional controls
The Console groups less-common settings into collapsible sections.
- Monitor a page section: Add one CSS selector, such as
main,.price,#availability, orarticle, to compare only that part of every page in the run. - Ignore page noise: Add selectors such as
.cookie-banneror.last-updatedso routine banners and timestamps do not create false alerts. - Keep results focused: Enable Only return meaningful changes to suppress routine unchanged rows. First-run baselines and errors are still returned.
- Send webhook alerts: Provide a public endpoint to receive meaningful changes automatically. If it needs bearer authentication, add your own Webhook bearer token secret. Receivers should deduplicate repeated
eventIdvalues. Console alerts use meaningful changes by default. - Add AI summaries: AI is opt-in. When it is available, bounded text from changed public pages is sent to the configured provider for classification; normal change detection still works without it. AI requests run one at a time so spend limits stop cleanly.
The existing History name and operational controls remain available under Advanced settings. Existing Tasks and API inputs keep the same shared-history behavior, defaults, and accepted limits. Internally, the Actor lowers effective concurrency for unusually large configured responses to keep memory use bounded.
Optional AI setup
AI summaries require the OPENROUTER_API_KEY Actor secret. The established aiModel input and CONTENT_CHANGE_MODEL environment variable remain supported for the approved model. No credentials are required for ordinary monitoring, selectors, webhook alerts, or history.
What you receive
The default dataset contains one row per checked page. The Change overview view highlights the fields most clients need:
| Field | Meaning |
|---|---|
status | ok when a page was checked, or error when it could not be fetched or extracted. |
changed | Whether the normalized page content differs from the prior successful check. |
isMeaningful | Whether the change passed the importance rules. |
changeType | A useful category such as price, availability, policy, job, product, news, or cosmetic. |
summary | A readable explanation of the result. |
addedLines / removedLines | Bounded text showing the detected change. |
errorCode | A machine-readable reason when a check could not complete. |
Example change result:
{"status": "ok","url": "https://example.com/pricing","changed": true,"isMeaningful": true,"changeType": "price","summary": "1 line added and 1 line removed (8.33% changed).","addedLines": ["Pro plan: $29/month"],"removedLines": ["Pro plan: $19/month"]}
Each run also stores a RUN_SUMMARY record with the number of pages checked, changes found, errors, alerts sent, AI usage, and run duration.
Schedule recurring checks
After the baseline run, schedule the same task or input at the interval you need. Keep the interval longer than the expected run time so two runs do not update the same history at once. For price or availability monitoring, a short interval may fit; for policies and documentation, daily or weekly checks are often enough.
Recovering one failed webhook manually
Delivery is best-effort. maxRetries retries temporary errors within the current
execution (up to maxRetries + 1 attempts); it does not create a durable queue.
When a changed result reaches the dataset, its baseline can advance even if its
webhook failed. A later unchanged check will not resend that change.
After checking your receiver, you can explicitly replay one original emitted
row with status: ok, changed: true and a notificationError. Keep the original
eventId, timestamp and payload. A timeout can mean the receiver accepted the
change but its acknowledgement was lost. Receivers should persist processed event
IDs and safely ignore repeats. A successful HTTP response acknowledges receipt,
not completion of every downstream action.
This optional Python example uses your own API token and receiver token from your
environment. Set FAILED_RUN_ID, REPLAY_EVENT_ID, REPLAY_WEBHOOK_URL (public
HTTPS), and optionally REPLAY_WEBHOOK_TOKEN. It reads at most 100 rows from that
run and makes one POST, with a 15-second timeout and no automatic redirects
or retry loop:
import jsonimport osfrom urllib import error, parse, requestfrom apify_client import ApifyClientclient = ApifyClient(os.environ["APIFY_TOKEN"])run = client.run(os.environ["FAILED_RUN_ID"]).get()if run is None:raise RuntimeError("Original run is unavailable")dataset_id = run["defaultDatasetId"] if isinstance(run, dict) else run.default_dataset_idpage = client.dataset(dataset_id).list_items(limit=100)items = page["items"] if isinstance(page, dict) else page.itemsevent_id = os.environ["REPLAY_EVENT_ID"]matches = [row for row in items if row.get("eventId") == event_id]if len(matches) != 1:raise RuntimeError("Select one available original event; do not reconstruct missing evidence")row = matches[0]if row.get("status") != "ok" or not row.get("changed") or not row.get("notificationError"):raise RuntimeError("Selected row is not an emitted failed change notification")payload = {"event": "content_change", **{key: row[key] for key in ("eventId", "watchName", "url", "isMeaningful", "importance", "changeType","summary", "reason", "changePercent", "addedLines", "removedLines",)}, "changedAt": row["checkedAt"]}endpoint = os.environ["REPLAY_WEBHOOK_URL"]url = parse.urlsplit(endpoint)if url.scheme != "https" or not url.hostname or url.username or url.password:raise RuntimeError("Use your intended public HTTPS receiver without URL credentials")headers = {"content-type": "application/json", "x-content-change-event-id": event_id}if os.environ.get("REPLAY_WEBHOOK_TOKEN"):headers["authorization"] = "Bearer " + os.environ["REPLAY_WEBHOOK_TOKEN"]class NoRedirect(request.HTTPRedirectHandler):def redirect_request(self, req, fp, code, msg, response_headers, newurl):return Nonepost = request.Request(endpoint, data=json.dumps(payload).encode("utf-8"), headers=headers, method="POST")try:with request.build_opener(NoRedirect()).open(post, timeout=15) as response:print(json.dumps({"eventId": event_id, "receiverStatus": response.status}))except error.HTTPError as exc:raise SystemExit(f"Replay not acknowledged (HTTP {exc.code}); inspect the receiver before another attempt")except (error.URLError, TimeoutError):raise SystemExit("Delivery uncertain; check receiver receipts before explicitly attempting again")
Keep this manual receipt in your own records. The original dataset row and Actor history are not rewritten, and this replay does not launch another Actor or AI request. Your receiver/client infrastructure can have its own costs.
Recovery needs the original emitted row before its dataset expires under your
Apify retention settings. Filters can omit a failed alert's row: for example,
Only return meaningful changes combined with notifyOn: any-change can notify
about a cosmetic change that is absent from the dataset. Expired, suppressed or
missing evidence is not recoverable with this recipe. Review RUN_SUMMARY for
notification failures as well as the dataset.
Webhooks run before result delivery. A webhook may already have arrived when a spending limit refuses its row or a dataset write fails; a subsequent run can repeat the same transition. Webhooks, datasets and baseline updates are not atomic, and there is no exactly-once guarantee. Do not reset your history to force a resend. Keep checks using the same history sequential; separate histories are independent.
Privacy and safe use
- This Actor monitors publicly accessible HTTP or HTTPS pages only. It does not log in, bypass access controls, or access private or local networks.
- AI analysis is disabled by default. If you enable it, only bounded normalized text and change data from changed public pages are sent to the configured AI provider.
- Webhook delivery is best-effort and may repeat an event. Use
eventIdto make your receiver idempotent. - The Actor applies download, retry, concurrency, AI-prompt, and text-size limits to keep runs predictable.
Troubleshooting
- The first result says
baseline: This is normal. Run the same input again to compare the page with that saved starting version. - A page reports
error: Confirm that it is publicly accessible without a login and that its important content appears in the initial HTML response. - Routine updates create unwanted changes: Use Elements to ignore for timestamps, cookie notices, banners, and similar page noise.
- No webhook arrived: Check
notificationSent,notificationAttempts, andnotificationErrorin the dataset. Your endpoint must be public and should accept repeatedeventIdvalues. - No AI summary appears: AI must be enabled and available for the Actor. The built-in analysis remains active when AI is unavailable.
Limitations
- This version reads the initial HTTP response. It may not capture content that exists only after JavaScript rendering or user interaction.
- If a page blocks automated requests, requires a login, or returns an unsupported format, the dataset returns an error row instead of a change result.
- Very large pages are safely capped; the result indicates when downloaded or retained text was truncated.
- AI classification is a helpful summary, not a substitute for human review in high-stakes legal, financial, safety, or compliance decisions.
Python API example
import osfrom apify_client import ApifyClientclient = ApifyClient(os.environ["APIFY_TOKEN"])run = client.actor("thescrapelab/ai-content-changes-detector").call(run_input={"urls": ["https://example.com/pricing"],"onlyMeaningfulChanges": True,})if run is None:raise RuntimeError("Actor call returned no run or storage metadata")for item in client.dataset((run["defaultDatasetId"] if isinstance(run, dict) else run.default_dataset_id)).iterate_items():print(item["url"], item["changed"], item["changeType"], item["summary"])
For the simplest experience, run the Actor once in the Console, review the baseline result, and then schedule the same task for ongoing website change monitoring.
Notifications
Webhook delivery is unavailable. The Actor still produces content-change datasets and reports, with notification status showing WEBHOOK_DISABLED_SOURCE_POLICY and zero send attempts. Read those outputs through Apify for downstream processing.
Output example
Illustrative output; replace source values with your own authorized evidence.
{"dataInputSource": "provided","sourceVerified": false}