# Website HTML & Text Change Monitor (`automation-lab/website-html-text-change-monitor`) Actor

Monitor public webpages and export versioned HTML/text snapshots, structured additions and removals, hashes, timestamps, and threshold status.

- **URL**: https://apify.com/automation-lab/website-html-text-change-monitor.md
- **Developed by:** [Stas Persiianenko](https://apify.com/automation-lab) (community)
- **Categories:** Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per event

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Website HTML & Text Change Monitor

Website HTML & Text Change Monitor performs **website change monitoring** for supplied public webpages.
It fetches server-rendered HTML, creates versioned HTML and normalized-text snapshots, compares each page with its saved baseline, and exports machine-readable additions, removals, changed fragments, hashes, timestamps, and threshold decisions.

Use it for recurring documentation checks, policy and compliance monitoring, release intelligence, competitor-page tracking, or any workflow that needs structured content differences instead of screenshots.

### What does Website HTML & Text Change Monitor do?

For every supplied URL, the Actor:

1. Fetches anonymous public HTML with bounded timeouts and retries.
2. Optionally focuses on one CSS-selected content region.
3. Removes scripts, styles, configured noise selectors, and matching text patterns.
4. Creates normalized HTML and text snapshots.
5. Compares the current snapshot with the previous successful snapshot.
6. Calculates hashes, changed-character percentage, and threshold status.
7. Emits additions, removals, and typed HTML/text change fragments.
8. Saves the current successful snapshot as the next baseline when enabled.

The first successful check initializes a baseline. Later runs using the same Actor Task or `monitorId` produce actual differences.

### Who is this website change monitor for?

- **Compliance teams** tracking public terms, policies, standards, and disclosures.
- **Developer-relations teams** watching documentation and release pages.
- **SEO and content teams** auditing unexpected page copy or markup changes.
- **Competitive-intelligence teams** monitoring public pricing and feature pages.
- **QA engineers** checking server-rendered production content after deployments.
- **Automation developers** feeding structured change events to webhooks, Make, Zapier, n8n, Slack, or databases.

Choose this Actor when HTML structure and extracted text matter. For pixel-level layout, style, or rendered JavaScript changes, use [Website Visual Change Monitor](https://apify.com/automation-lab/website-visual-change-monitor).

### Why use structured HTML and text snapshots?

A screenshot can show that pixels moved, but it is harder to query or route through an automation.
This Actor returns the source content and typed differences directly.

Useful differentiators include:

- separate HTML and text hashes;
- raw normalized snapshot values in each successful dataset row;
- persistent baseline metadata with timestamps and lengths;
- text additions and removals ready for alerts;
- HTML/text change fragments with `added` or `removed` type;
- a configurable percentage threshold;
- CSS region targeting and noise removal;
- direct HTTP execution without browser overhead;
- optional, user-controlled Apify Proxy support with no automatic paid fallback.

### What data does the Actor return?

| Field | Meaning |
| --- | --- |
| `url`, `finalUrl` | Requested normalized URL and final URL after redirects |
| `status` | `success` or `error` for this webpage |
| `title` | Current HTML document title |
| `checkedAt` | ISO timestamp for the check |
| `monitorId` | Snapshot namespace supplied in input |
| `compareMode` | `html`, `text`, or `both` |
| `baselineInitialized` | `true` when no prior snapshot existed |
| `baselineUpdated` | Whether current content became the next baseline |
| `previousSnapshot` | Prior hashes, timestamp, and content lengths |
| `currentSnapshot` | Current hashes, timestamp, and content lengths |
| `html`, `text` | Current normalized HTML and extracted text |
| `htmlChanged`, `textChanged` | Hash-level change flags |
| `changePercent` | Changed-character percentage for the selected mode |
| `changeThresholdPercent` | Configured alert threshold |
| `thresholdExceeded` | Machine-readable alert decision |
| `addedText`, `removedText` | Up to 50 line-level text additions and removals |
| `changedSections` | Up to 100 typed HTML/text fragments |
| `error` | Actionable per-URL error when a page fails |

Error rows are not charged as item events. If every URL fails, the run fails after preserving those diagnostic rows.

### How to monitor a webpage for changes

1. Open the Actor in Apify Console.
2. Add one or more anonymously reachable public URLs.
3. Enter a stable, unique `monitorId`.
4. Choose HTML, text, or combined comparison.
5. Optionally set `contentSelector`, noise filters, and an alert threshold.
6. Run once to establish the baseline.
7. Save the input as an Actor Task and schedule recurring runs.
8. Read `thresholdExceeded`, `addedText`, and `removedText` in the dataset or a webhook integration.

A saved Actor Task is recommended for recurring work because its task ID automatically isolates the baseline namespace.

### Input parameters

| Parameter | Required | Default | Description |
| --- | --- | --- | --- |
| `startUrls` | Yes | Apify Actor docs | One to 100 public HTTP/HTTPS webpages |
| `monitorId` | Yes | `apify-actors-docs-monitor` | Stable unique snapshot namespace |
| `compareMode` | No | `both` | Compare `html`, `text`, or `both` |
| `contentSelector` | No | — | Limit capture to the first CSS match |
| `ignoreSelectors` | No | scripts/styles | Remove dynamic or irrelevant elements |
| `ignoreTextPatterns` | No | `[]` | Remove matching JavaScript regular expressions |
| `changeThresholdPercent` | No | `1` | Percentage required for `thresholdExceeded=true` |
| `updateBaseline` | No | `true` | Save successful current content for the next run |
| `resetBaseline` | No | `false` | Ignore and replace the saved baseline |
| `maxItems` | No | `20` | Maximum URLs processed, up to 100 |
| `maxContentBytes` | No | `2000000` | Reject unexpectedly large HTML responses |
| `requestTimeoutSecs` | No | `30` | Per-request timeout from 5 to 120 seconds |
| `maxRequestRetries` | No | `2` | Temporary-error retry limit from 0 to 5 |
| `proxyConfiguration` | No | direct | Optional Apify Proxy configuration |

Only enable a proxy when direct anonymous access is insufficient. Proxy transfer may add platform usage cost.

### Example input

```json
{
  "startUrls": [
    { "url": "https://docs.apify.com/platform/actors" }
  ],
  "monitorId": "apify-actors-docs-monitor",
  "compareMode": "both",
  "contentSelector": "main",
  "ignoreSelectors": ["script", "style", "noscript"],
  "changeThresholdPercent": 1,
  "updateBaseline": true,
  "maxItems": 1
}
```

The first run initializes the snapshot. Run the same input again after the source changes to receive additions and removals.

### Example output

This abbreviated record reflects the current Actor contract:

```json
{
  "url": "https://docs.apify.com/platform/actors",
  "finalUrl": "https://docs.apify.com/platform/actors",
  "status": "success",
  "title": "Actors | Platform | Apify Documentation",
  "checkedAt": "2026-08-19T06:22:11.713Z",
  "monitorId": "apify-actors-docs-monitor",
  "compareMode": "both",
  "baselineInitialized": false,
  "baselineUpdated": true,
  "htmlChanged": false,
  "textChanged": false,
  "changePercent": 0,
  "changeThresholdPercent": 1,
  "thresholdExceeded": false,
  "addedText": [],
  "removedText": [],
  "changedSections": [],
  "currentSnapshot": {
    "htmlHash": "a32bb16aa68d83ba70302d1617bec10948df1dfe85c3617f7089680a71a022cb",
    "textHash": "f5328eecc7dd817acb0f4d6ff797b99b8a15a8f017ee9ca8ac7c56554939e592",
    "htmlLength": 27369,
    "textLength": 2509
  }
}
```

Successful rows also contain the full normalized `html` and `text` snapshot values.

### How thresholds and baselines work

`changePercent` measures added and removed characters against the combined previous/current size for the selected comparison mode.
It is not a semantic importance score.

- A new baseline always reports `changePercent: 0` and `thresholdExceeded: false`.
- `htmlChanged` and `textChanged` compare SHA-256 hashes independently.
- `thresholdExceeded` becomes true when `changePercent` reaches the configured threshold.
- Set `updateBaseline: false` to keep comparing against one fixed snapshot.
- Set `resetBaseline: true` after intentionally changing selectors or normalization rules.

Changing `compareMode`, `contentSelector`, or ignore rules can create a large one-time difference. Reset the baseline when making those configuration changes.

### How much does it cost to monitor website changes?

The Actor uses pay-per-event pricing:

| Event | Price | Charged when |
| --- | ---: | --- |
| Start | $0.005 | Once when a run begins |
| Item, BRONZE tier | $0.052 | For each successful webpage snapshot |

At the BRONZE tier, 1 successful page costs **$0.057**, 10 pages cost **$0.525**, and 100 pages cost **$5.205** before any optional proxy platform usage.
The six subscription tiers decrease from $0.0598 per item on FREE to $0.01456 on DIAMOND; Apify Console applies the customer’s active tier.
Failed URL rows have no item event charge.

### Scheduling website change alerts

Save a tested input as an Actor Task, then connect it to an Apify Schedule.
A common workflow is:

1. Run hourly, daily, or weekly.
2. Filter dataset rows where `thresholdExceeded` is true.
3. Send `addedText`, `removedText`, and the URL to Slack or email.
4. Store snapshot hashes in a data warehouse for audit history.

The Actor reports decisions; it does not itself send email or Slack notifications.
Use Apify webhooks or an integration for delivery.

### Integration ideas

- **Make or Zapier:** trigger a notification when a dataset row exceeds its threshold.
- **n8n:** branch by `htmlChanged` versus `textChanged` and route markup-only changes separately.
- **Google Sheets:** append timestamp, URL, hashes, and threshold status for an audit ledger.
- **Slack:** post concise additions/removals for documentation or policy owners.
- **Data warehouse:** store snapshot hashes and content lengths for longitudinal analysis.
- **CI/CD:** run after deployment and fail a downstream check on unexpected production content changes.

### API usage

Run the Actor synchronously from Apify clients or start it through the REST API. Reuse the same saved Actor Task for persistent scheduled baselines.

#### Run with the Apify JavaScript client

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

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('automation-lab/website-html-text-change-monitor').call({
  startUrls: [{ url: 'https://docs.apify.com/platform/actors' }],
  monitorId: 'apify-actors-docs-monitor',
  compareMode: 'both',
  changeThresholdPercent: 1,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items);
```

#### Run with Python

```python
from apify_client import ApifyClient

client = ApifyClient('YOUR_APIFY_TOKEN')
run = client.actor('automation-lab/website-html-text-change-monitor').call(run_input={
    'startUrls': [{'url': 'https://www.w3.org/TR/WCAG22/'}],
    'monitorId': 'wcag-22-monitor',
    'compareMode': 'text',
    'contentSelector': '#abstract',
    'changeThresholdPercent': 0.1,
})
items = client.dataset(run['defaultDatasetId']).list_items().items
for item in items:
    print(item['url'], item.get('thresholdExceeded'))
```

#### Run with cURL

```bash
curl -X POST \
  'https://api.apify.com/v2/acts/automation-lab~website-html-text-change-monitor/runs?token=YOUR_APIFY_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{
    "startUrls": [{"url":"https://docs.apify.com/platform/actors"}],
    "monitorId": "apify-actors-docs-monitor",
    "compareMode": "both",
    "changeThresholdPercent": 1
  }'
```

Never commit an API token to source control. Use environment variables or your platform secret manager.

### Use Website HTML & Text Change Monitor through MCP

Add the Actor to Claude Code:

```bash
claude mcp add --transport http apify \
  "https://mcp.apify.com?tools=automation-lab/website-html-text-change-monitor"
```

For Claude Desktop, Cursor, VS Code, or another MCP client:

```json
{
  "mcpServers": {
    "apify": {
      "url": "https://mcp.apify.com?tools=automation-lab/website-html-text-change-monitor"
    }
  }
}
```

Example prompts:

- “Initialize a text baseline for the W3C WCAG abstract.”
- “Check these documentation URLs and list only threshold-exceeding changes.”
- “Show additions and removals from my scheduled pricing-page monitor.”

### Tips for reliable page change monitoring

- Target the smallest stable region with `contentSelector`.
- Remove clocks, randomized IDs, ads, and rotating widgets with ignore rules.
- Prefer one stable Actor Task for each monitoring workflow.
- Use a unique `monitorId` for direct Actor runs.
- Reset the baseline after changing selectors or normalization settings.
- Keep `maxContentBytes` conservative to catch accidental downloads or oversized pages.
- Start with direct requests and enable a proxy only when evidence requires it.
- Use a nonzero threshold when sources make harmless small changes.

### Limitations

- The Actor processes server-rendered HTML and does not execute page JavaScript.
- Login-only, private, CAPTCHA, and challenge pages are unsupported.
- It does not crawl links; only supplied URLs are checked.
- Highly personalized or A/B-tested content can produce different snapshots.
- Normalization cannot infer whether a textual change is legally or commercially important.
- `changedSections` and text arrays are capped to keep records usable; full current HTML and text remain available.
- Named snapshot state is intended for unique monitor IDs or Actor Tasks; do not reuse one monitor ID for unrelated workflows.
- The Actor emits structured decisions but does not send alerts itself.

### Troubleshooting

**The first run reports no changes**

That is expected. It initializes the baseline. Run the same task again after the page changes.

**A selector matched no content**

Verify `contentSelector` against the server-rendered response, not only the browser DOM. Remove it to inspect the full snapshot.

**A page returns a challenge or HTTP error**

Confirm it is anonymously accessible. If direct access is geographically restricted, try an appropriate Apify Proxy country. JavaScript-only challenges may require a browser-based Actor instead.

**Every run reports small changes**

Add stable `ignoreSelectors` or narrowly scoped `ignoreTextPatterns`, then reset the baseline. Avoid broad expressions that remove meaningful content.

**The run fails after writing error rows**

When all requested URLs fail, the Actor deliberately exits non-zero so schedules and monitoring systems do not mistake failure for “no changes.” Inspect each row’s `error` field.

### Responsible use and legality

Monitor only public pages you are authorized to access.
Respect website terms, robots guidance, rate limits, intellectual-property rights, privacy obligations, and applicable laws.
Do not use this Actor to bypass authentication, access controls, CAPTCHAs, or technical restrictions.
Choose a reasonable schedule and avoid excessive requests to third-party sites.

### FAQ

**Does the Actor preserve raw HTML?**

It returns normalized HTML after removing scripts, styles, configured ignored elements, comments, and redundant whitespace. This makes structural changes easier to compare but is not a byte-for-byte archive of the response.

**Can it detect text changes without markup noise?**

Yes. Use `compareMode: "text"`; the Actor extracts normalized semantic text lines and reports additions and removals.

**Can I monitor multiple pages?**

Yes. Supply up to 100 URLs and use `maxItems` to cap a run.

**Can I keep a fixed baseline?**

Yes. Initialize once, then set `updateBaseline: false` for subsequent checks.

**Does it work with scheduled Actor Tasks?**

Yes. Tasks are the recommended recurring workflow and receive task-scoped baseline isolation.

**Does it send a website change alert?**

It emits webhook-ready threshold decisions and diffs. Connect an Apify webhook, Make, Zapier, n8n, or your own integration to send notifications.

### Related Automation Lab Actors

- [Website Visual Change Monitor](https://apify.com/automation-lab/website-visual-change-monitor) — browser screenshots, pixel percentages, and visual diff overlays.
- [Webpage Structured Data Monitor](https://apify.com/automation-lab/webpage-structured-data-monitor) — track JSON-LD and structured metadata changes.
- [Lighthouse Website Audit](https://apify.com/automation-lab/website-lighthouse-seo-audit) — monitor performance, accessibility, best-practice, and SEO audit results.
- [Wayback Machine Checker](https://apify.com/automation-lab/wayback-machine-checker) — inspect public Internet Archive snapshot history.

# Actor input Schema

## `startUrls` (type: `array`):

Public HTTP/HTTPS webpage URLs. Up to 100 URLs per run.

## `monitorId` (type: `string`):

Stable unique ID used to isolate snapshots across runs. Scheduled Actor Tasks are additionally isolated by task ID.

## `compareMode` (type: `string`):

Compare normalized HTML, extracted text, or both.

## `contentSelector` (type: `string`):

Optional CSS selector that limits snapshots and diffs to the first matching page region, such as main or #pricing.

## `ignoreSelectors` (type: `array`):

Remove matching dynamic or irrelevant elements before extracting HTML and text. Maximum 50 selectors.

## `ignoreTextPatterns` (type: `array`):

JavaScript regular expressions removed from normalized HTML and text, useful for timestamps or rotating IDs. Maximum 50 patterns.

## `changeThresholdPercent` (type: `number`):

Set thresholdExceeded when the selected content changes by at least this percentage.

## `updateBaseline` (type: `boolean`):

Store the current successful snapshot as the baseline for the next run. Disable to repeatedly compare with the same baseline.

## `resetBaseline` (type: `boolean`):

Ignore the saved snapshot and initialize a fresh baseline during this run.

## `maxItems` (type: `integer`):

Maximum number of supplied URLs processed in this run.

## `maxContentBytes` (type: `integer`):

Reject unexpectedly large pages before parsing and storing them.

## `requestTimeoutSecs` (type: `integer`):

Maximum time for each webpage request.

## `maxRequestRetries` (type: `integer`):

Retries for temporary network, rate-limit, and server errors.

## `proxyConfiguration` (type: `object`):

Optional Apify Proxy configuration. Direct requests are used by default; no automatic paid fallback occurs.

## Actor input object example

```json
{
  "startUrls": [
    {
      "url": "https://docs.apify.com/platform/actors"
    }
  ],
  "monitorId": "apify-actors-docs-monitor",
  "compareMode": "both",
  "ignoreSelectors": [
    "script",
    "style",
    "noscript"
  ],
  "ignoreTextPatterns": [],
  "changeThresholdPercent": 1,
  "updateBaseline": true,
  "resetBaseline": false,
  "maxItems": 20,
  "maxContentBytes": 2000000,
  "requestTimeoutSecs": 30,
  "maxRequestRetries": 2,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `dataset` (type: `string`):

Dataset containing one current snapshot and comparison result per requested webpage.

# 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 = {
    "startUrls": [
        {
            "url": "https://docs.apify.com/platform/actors"
        }
    ],
    "monitorId": "apify-actors-docs-monitor"
};

// Run the Actor and wait for it to finish
const run = await client.actor("automation-lab/website-html-text-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 = {
    "startUrls": [{ "url": "https://docs.apify.com/platform/actors" }],
    "monitorId": "apify-actors-docs-monitor",
}

# Run the Actor and wait for it to finish
run = client.actor("automation-lab/website-html-text-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 '{
  "startUrls": [
    {
      "url": "https://docs.apify.com/platform/actors"
    }
  ],
  "monitorId": "apify-actors-docs-monitor"
}' |
apify call automation-lab/website-html-text-change-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,automation-lab/website-html-text-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/HYZtV3YloC0ASFbqq/builds/GsxtnrDdzC0fJzP4Z/openapi.json
