# Web Page Change Monitor (`afkdigital/web-page-change-monitor`) Actor

Track public page titles, normalized text extracted from HTML, selected fields and links across runs.

- **URL**: https://apify.com/afkdigital/web-page-change-monitor.md
- **Developed by:** [AFK Digital](https://apify.com/afkdigital) (community)
- **Categories:** Developer tools, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$4.50 / 1,000 successful 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

## Web Page Change Monitor

Apify Actor for comparing public, static web-page content across separate runs. It monitors titles, normalized document text, CSS-selected content and links. It does not extract product prices or stock availability.

### How to run

1. Enter 1–25 page objects in `urls`, each with a public HTTP(S) `url` and optional `label` or CSS `selector`.
2. Choose one or more `watch` fields: `title`, `text`, `selector`, `links`.
3. Reuse the same `monitorId` on scheduled runs. The Actor initializes a baseline on the first successful check.
4. Schedule the Actor with the same input and monitor ID to detect later changes.

Example input: [examples/input.json](examples/input.json)

The Actor stores only the latest successful snapshot for each URL in a named Apify Key-Value Store. Store names are derived from the initiating Apify user ID and monitor ID; URL record keys are derived from the normalized source URL. No page content is written to logs.

### Results

Each default dataset item includes `status`, `sourceUrl`, `checkedAt`, `fieldsChanged`, `changes`, and a capped current `snapshot` preview.

- `baseline_initialized`: first successful check for this monitor and URL.
- `no_change`: identical content or a difference below `minChangedCharacters`.
- `changed`: at least one watched field changed above the threshold. Changes contain `added`, `removed`, or `changed` values.
- `source_error`: temporary HTTP, timeout, parse, missing-selector, or safety failure. The last successful snapshot remains intact.

Example result: [examples/output.json](examples/output.json)

### Pricing

**$0.0045 per successful page check.** The Actor charges the `successful-page-check` event once after each page is fetched and all selected fields are extracted. Baseline and unchanged results count as successful checks. Source errors do not trigger this event. Input validation happens before a run starts. Apify platform usage may be billed separately from this custom event.

### Limits and safety

- Static HTML only; pages rendered in a browser by JavaScript are not evaluated.
- Text comes from parsed HTML, not computed browser visibility; CSS-hidden text may appear.
- No login, CAPTCHA, paywall, or access-control bypass.
- Targets resolving to local, private, link-local, reserved, or mixed public/private IPs are rejected. Connections pin to a checked public IP.
- Redirects are not followed. Submit the final public URL.
- Request timeout: 3–30 seconds; per-host spacing: 0.5–10 seconds; concurrency: 1–3; retries: 0–2; total requests: at most 50; response size: at most 5 MB; runtime: at most 10 minutes.
- CSS selectors can stop matching when a site's HTML changes.
- Only the latest good snapshot is retained. Run dataset history follows the retention settings of Apify storage.
- The Actor requires an Apify user ID and persistent Key-Value Store access.

### Local development

Requirements: Node.js 20+.

```sh
npm ci
npm test
npm start
```

`npm start` must run inside Apify or an Apify-compatible local runtime with an input, user ID and persistent storage. Tests use a local HTTP server and injected in-memory storage to verify repeated-run behavior without credentials.

# Actor input Schema

## `urls` (type: `array`):

1–25 public HTTP(S) page objects with a URL and optional label or CSS selector.

## `watch` (type: `array`):

Select page title, normalized document text, selector content, or links.

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

Stable ID used with your Apify user ID to keep separate monitor history.

## `minChangedCharacters` (type: `integer`):

Ignore field diffs smaller than this character count.

## `timeoutMs` (type: `integer`):

Request timeout (ms).

## `delayMs` (type: `integer`):

Per-host request spacing (ms).

## `concurrency` (type: `integer`):

Concurrent requests.

## `maxRetries` (type: `integer`):

Retries for temporary source failures.

## `maxTotalRequests` (type: `integer`):

Maximum HTTP requests.

## `maxRunSeconds` (type: `integer`):

Maximum Actor runtime (seconds).

## `maxResponseBytes` (type: `integer`):

Maximum HTML response bytes.

## Actor input object example

```json
{
  "urls": [
    {
      "url": "https://example.com/",
      "label": "IANA example page"
    }
  ],
  "watch": [
    "title",
    "text"
  ],
  "monitorId": "apify-qa-example",
  "minChangedCharacters": 1,
  "timeoutMs": 15000,
  "delayMs": 1000,
  "concurrency": 1,
  "maxRetries": 1,
  "maxTotalRequests": 25,
  "maxRunSeconds": 120,
  "maxResponseBytes": 2000000
}
```

# Actor output Schema

## `dataset` (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 = {
    "urls": [
        {
            "url": "https://example.com/",
            "label": "IANA example page"
        }
    ],
    "monitorId": "apify-qa-example"
};

// Run the Actor and wait for it to finish
const run = await client.actor("afkdigital/web-page-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 = {
    "urls": [{
            "url": "https://example.com/",
            "label": "IANA example page",
        }],
    "monitorId": "apify-qa-example",
}

# Run the Actor and wait for it to finish
run = client.actor("afkdigital/web-page-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 '{
  "urls": [
    {
      "url": "https://example.com/",
      "label": "IANA example page"
    }
  ],
  "monitorId": "apify-qa-example"
}' |
apify call afkdigital/web-page-change-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,afkdigital/web-page-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/MpvjXoKeDsKqPMgC3/builds/ZgVrnFpn4gSYdcy9f/openapi.json
