# Noise-Filtered Website Change Monitor (`f108/noise-filtered-website-change-monitor`) Actor

Monitor public webpages for text changes. Use CSS selectors, ignore regex, normalized text and thresholds with persistent baselines and structured diffs for automated workflows.

- **URL**: https://apify.com/f108/noise-filtered-website-change-monitor.md
- **Developed by:** [Fumiya Hanzawa](https://apify.com/f108) (community)
- **Categories:** Developer tools, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 1,000 page checks

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Noise-Filtered Website Change Monitor

Monitor public webpages for text changes while filtering common page noise. Select content with CSS
selectors, ignore dynamic text with regex, persist baselines across runs, and receive structured diffs.

### Features

- Stateful monitoring with a reusable stateKey and persistent baselines.
- CSS selectors for selected page sections.
- Text normalization removes scripts, styles and common page noise.
- Ignore regex patterns suppress timestamps or other dynamic text.
- A change threshold filters small edits; differences accumulate against the last accepted baseline.
- Structured Dataset output for automation, with statuses, ratios, timestamps and bounded diffs.

This Actor compares text. It does not determine business importance. It uses HTTP fetches without
browser rendering, so JavaScript-only pages may not be suitable.

### Input

```json
{
  "targets": [{"url": "https://example.com", "selector": "body"}],
  "stateKey": "my-monitor",
  "ignorePatterns": [],
  "minChangeRatio": 0.01
}
```

Targets accept only url and optional selector. Unknown fields are rejected. Maximum 20 targets,
10 regex patterns of 200 characters each, and stateKey of 1–40 letters, digits or hyphens.
An unmatched selector or empty extraction produces an error and preserves the previous baseline.

### Output

One Dataset row per checked page: `first_seen`, `unchanged`, `changed`, or `error`. Successful rows
include url, selector, checkedAt, changed, changeRatio and summary. Changed rows have diffPreview
(up to 4,000 characters). Errors expose only exception classes, not third-party response bodies.
Check OUTPUT.errors even when the Run succeeds. OUTPUT also reports checked, changed, skipped,
duplicateTargetsSkipped, chargedCounts and stopReason. CHARGE\_RECEIPTS records each billed count.

The first run establishes a baseline. Checks below the threshold preserve that baseline, allowing
small changes to accumulate. Duplicate URL/selector targets in one run are processed once.

### Pricing and spending limits

- **page-check: $0.002 per successful page check**, including baselines and unchanged pages.
- **apify-actor-start: $0.00005 per started GB, minimum one event.** This Actor uses 256–512 MB,
  normally one start event per run.
- Failed fetches/normalization are not charged as successful checks; the start event still applies.
- No automatic Dataset-item charge and no separate platform-usage surcharge in the offered PPE price.

Set maximum charge per run in Console or maxTotalChargeUsd via API. Allow at least $0.00205 for one
start plus one check. A saved successful result is charged afterward. Processing stops when the event
budget cannot cover another check. Your own storage/export costs remain subject to your Apify plan.

Limits: 2 MB uncompressed responses, 100,000 normalized characters, 35-second fetch deadline,
5 redirects and bounded regex time. Storage/billing failures fail the Run. Storage, charging and
baseline writes are not atomic. The same cloud Run refuses replay after reboot/resurrection; a new
Run is a new billable check. Do not delete PROCESSING\_STARTED and restart old Runs. Exactly-once
delivery across separate Runs is not promised.

### Scheduling

**Simultaneous Runs with the same stateKey in one account are not supported.** Serialize calls and
await completion. Space schedules beyond the configured timeout. Use different stateKeys for
independent monitors. There is no distributed lock: overlapping Runs can overwrite newer baselines,
repeat change signals and bill checks in each Run.

### Data retention and deletion

Normalized page-body snapshots are saved in the named key-value store `meaningful-change-<stateKey>`.
This stable prefix preserves existing monitors. Each URL/selector/ignorePatterns combination gets
one key with its most recent accepted baseline. Removing a target does not delete stored data.
Full original HTML is not archived. No page content is sent to an LLM.

Dataset rows retain URLs, timestamps, comparison results and diff previews. Default key-value stores
hold input, summary, charge receipts and the replay marker. Named snapshots remain until you delete
them. Unnamed run storages follow your Apify plan's retention settings; naming them can preserve them
indefinitely. You manage retention; review named stores at least every 30 days or automate deletion
in your workflow. No automatic snapshot-expiry job resets your baselines unexpectedly.

Deletion procedure:

1. Stop the monitor's schedules/integrations and wait for active Runs to finish.
2. Console → Storage → Key-value stores: verify `meaningful-change-<stateKey>`, then Actions → Delete.
   This resets all of that monitor's baselines; its next Run starts fresh.
3. For each related Run, identify and delete its Dataset and default key-value store, including unnamed
   storages. Deleting the named snapshot store alone does not remove Dataset diffs.
4. Remove unneeded Runs/logs, exported files and downstream copies separately. Do not restart old Runs
   after deleting their replay marker. Never delete another monitor's resources.

For automation use authenticated DELETE `/v2/key-value-stores/{storeId}`, `/v2/datasets/{datasetId}`
and `/v2/actor-runs/{runId}` for verified IDs. Protect storage access using Restricted settings.
Actor visibility and storage visibility differ. Do not share tokens or storage links unintentionally.
[Apify retention and deletion](https://docs.apify.com/storage)

### Allowed targets and responsibility

**Public HTTP/HTTPS pages only. No login, CAPTCHA or access-control bypass.** Do not submit URLs or
pages containing personal information, credentials or confidential information. Internal/non-public
IPs, credential URLs and nonstandard ports are blocked. DNS is checked at connection time, connections
are pinned to verified public IPs, and redirects are checked again.

You must confirm target terms, robots policies, rights and rate limits before submitting URLs. Public
availability is not permission to scrape. The Actor does not automatically determine or enforce all
robots.txt/site terms. The operator does not manually approve every customer URL; normal operation
is unattended within these technical controls. New Actor publication and serious security or rights
exceptions require human review.

### Local development

Python 3.12 / Apify SDK 4.0.2, entrypoint `python -m src`. Use `apify run` or the prepared Windows
wrapper `../apify.ps1 run --input-file input.example.json`.

# Actor input Schema

## `targets` (type: `array`):

Public HTTP/HTTPS pages. Each target accepts url and optional CSS selector only.

## `stateKey` (type: `string`):

Use the same stateKey on scheduled runs so snapshots persist.

## `ignorePatterns` (type: `array`):

At most 10 regex patterns of 200 characters each; execution has a timeout.

## `minChangeRatio` (type: `number`):

Minimum difference ratio from the last accepted baseline.

## Actor input object example

```json
{
  "targets": [
    {
      "url": "https://example.com"
    }
  ],
  "stateKey": "default",
  "ignorePatterns": [],
  "minChangeRatio": 0.01
}
```

# Actor output Schema

## `results` (type: `string`):

No description

## `summary` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {};

// Run the Actor and wait for it to finish
const run = await client.actor("f108/noise-filtered-website-change-monitor").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {}

# Run the Actor and wait for it to finish
run = client.actor("f108/noise-filtered-website-change-monitor").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{}' |
apify call f108/noise-filtered-website-change-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,f108/noise-filtered-website-change-monitor"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/EXrVmtV9I6MccVosd/builds/DrjJfpzwGcrIkUUeK/openapi.json
