# Selector-Scoped Content Change Monitor (`kingii98/selector-content-change-monitor`) Actor

Monitor selected parts of public web pages: fetch HTML over HTTP, extract text or inner HTML by CSS selector, and report only selector-level changes after a quiet baseline.

- **URL**: https://apify.com/kingii98/selector-content-change-monitor.md
- **Developed by:** [kingii98](https://apify.com/kingii98) (community)
- **Categories:** SEO tools, Developer tools, Automation
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$2.00 / 1,000 page checkeds

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/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

## Selector-Scoped Content Change Monitor

Monitor specific parts of public web pages and get notified only when they change. Given a set of page URLs and CSS selectors, the Actor fetches each page over plain HTTP, extracts the selected text or inner HTML deterministically, normalizes whitespace, stores a snapshot per `monitorId` in Apify key-value storage, and emits only selector-level changes — after a quiet first-run baseline.

This is a selector-scoped monitor, not a full-page differ: only the elements you name are extracted, hashed, and compared, and only changes are written to the dataset.

**JavaScript-rendered content is not supported.** Pages are fetched with plain HTTP requests only — no browser, no rendering. Content that appears only after JavaScript execution will be reported as selector-not-found (or missing/changed values).

### What it detects

- `added` — a selector matches now but did not match in the previous snapshot (or the page is new)
- `removed` — a selector matched in the previous snapshot but no longer matches (or the page was dropped from the input)
- `changed` — the extracted value's hash differs from the previous snapshot
- `unchanged` — reported only when `includeUnchanged` is enabled
- `error` — the page could not be fetched (DNS, TLS, timeout, too many redirects, redirect loop, or a redirect to a non-public address)

Page-level fetch, status, and redirect changes are surfaced through `issueCodes` on every selector record of the affected page: `http-4xx`, `http-5xx`, `status-changed`, `redirect-changed`, `fetch-error`, plus `multiple-matches` (selector matched more than one element; the first is used) and `page-removed`.

The first run of a `monitorId` is always a quiet baseline: it stores the snapshot and emits only the summary record, so you are not flooded with "added" records on day one.

### Input

```json
{
  "monitorId": "competitor-pricing",
  "urls": ["https://example.com/pricing", "https://example.com/features"],
  "selectors": [
    {"name": "headline", "selector": "h1", "mode": "text"},
    {"name": "price-table", "selector": "#pricing table", "mode": "html"}
  ],
  "maxPages": 100,
  "concurrency": 10,
  "timeoutSecs": 20,
  "maxRedirects": 5,
  "normalizeWhitespace": true,
  "includeUnchanged": false
}
```

| Field | Description |
|---|---|
| `monitorId` | Required. Stable monitor identifier (letters, numbers, `_`, `-`; max 64). Snapshots are stored per `monitorId` in a named key-value store (`selector-content-<monitorId>`); reuse the same ID across runs. |
| `urls` | Public HTTP(S) page URLs, 1-1000 entries, normalized and deduplicated. Only the first `maxPages` are checked per run. |
| `selectors` | 1-50 entries, each with a unique `name`, a CSS `selector`, and `mode` `text` (selected text) or `html` (inner HTML of the first match). Names must be unique; selector syntax is validated before any network work. |
| `maxPages` | Cap on pages checked per run. Default 100; maximum 1,000. |
| `concurrency` | Concurrent page fetches. Default 10; range 1-30. |
| `timeoutSecs` | Per-request timeout. Default 20 seconds; range 2-60. |
| `maxRedirects` | Maximum redirect hops followed per page. Default 5; range 0-10. Longer chains and loops fail the page with a fetch error. |
| `normalizeWhitespace` | Collapse whitespace runs in extracted values to single spaces before hashing, so formatting-only changes are ignored. Enabled by default. |
| `includeUnchanged` | Also emit records for selectors that did not change. Disabled by default. |

Invalid input (bad URLs, duplicate selector names, invalid CSS syntax, out-of-range caps) fails fast with a clear validation error before any network work.

### Output

Every run writes one summary record plus one `selector-result` record per detected change to the default dataset.

Summary:

```json
{
  "recordType": "summary",
  "monitorId": "competitor-pricing",
  "baseline": false,
  "checkedAt": "2026-08-07T09:15:00+00:00",
  "pageCount": 2,
  "selectorCount": 2,
  "added": 0,
  "removed": 0,
  "changed": 1,
  "unchanged": 0,
  "errors": 0,
  "fetchErrors": 0
}
```

Selector result:

```json
{
  "recordType": "selector-result",
  "monitorId": "competitor-pricing",
  "baseline": false,
  "pageUrl": "https://example.com/pricing",
  "selectorName": "headline",
  "selector": "h1",
  "mode": "text",
  "changeType": "changed",
  "previousValue": "Old price: $10",
  "currentValue": "New price: $12",
  "previousValueTruncated": false,
  "currentValueTruncated": false,
  "previousHash": "2c26b46b…",
  "currentHash": "a665a459…",
  "status": 200,
  "finalUrl": "https://example.com/pricing",
  "redirects": [],
  "issueCodes": [],
  "checkedAt": "2026-08-07T09:15:00+00:00"
}
```

Extracted values are capped at 100 KB per selector in the persisted snapshot; dataset record values are capped at a 10 KB preview (`previousValueTruncated` / `currentValueTruncated` flag truncation). Hashes are SHA-256 over the stored value, so change detection stays exact even when previews are truncated. Full page content is never written to the dataset.

### Pricing

The Actor uses Apify pay-per-event pricing with the `page-checked` charge event. When monetization is enabled, users are charged **$0.002 per page checked ($2 per 1,000 pages)**. One `page-checked` event corresponds to one page fetch including all of its selector extractions.

Apify platform usage (compute units and other resources consumed by the run) may still be shown to users according to their plan and Apify's pricing rules, as described in the Actor's listing.

The Actor respects the run's maximum total charge: if the remaining budget cannot cover every page, it checks only the chargeable prefix; if no page can be charged, it stops before any page checks.

Final pricing is configured in the Apify Store listing and may change subject to Apify's pricing-change notice rules.

### Security and privacy

- Only public HTTP(S) targets are accepted.
- URL credentials, localhost, and non-public, loopback, link-local, multicast, unspecified, or reserved addresses are rejected.
- Every redirect target is resolved and validated before it is followed; a redirect to a private address fails that page with an error record instead of being fetched.
- URL counts, selector counts, concurrency, redirects, response bytes, extracted value sizes, and timeouts are all capped before or during network work.
- The Actor does not use a browser, proxy, LLM, external database, or third-party analytics service.
- Snapshots live in a named key-value store scoped to the `monitorId`; dataset records live in the run's default dataset, subject to the retention and access settings of the Apify account running the Actor.

Do not place secrets, private URLs, or personal data in any input field.

### Limitations

- JavaScript-rendered content is not supported: fetches use plain HTTP responses only, with no browser rendering. Selectors that only match client-rendered DOM will report as missing.
- If a selector matches multiple elements, the first match in document order is used and the record is flagged `multiple-matches`.
- The first run of each `monitorId` is a quiet baseline and reports no changes.
- Network failures and rate limits are reported as per-page error records; they are not automatically retried indefinitely.
- The Actor does not send notifications itself. Use Apify schedules, webhooks, or an automation platform.

### Support

For reproducible issues, open an issue from the Actor page and include the Apify run ID, sanitized input, expected result, and affected public URL. Do not include API tokens or private data.

This Actor monitors selector-scoped page content; it does not provide legal, security-audit, or uptime guarantees.

# Actor input Schema

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

Stable identifier for this monitor. Snapshots are stored per monitorId in a named key-value store, so reuse the same ID across runs of the same monitor.

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

Public HTTP(S) page URLs to check (1-1000). Only the first maxPages URLs are checked per run. Pages are fetched over plain HTTP; JavaScript-rendered content is not supported.

## `selectors` (type: `array`):

CSS selectors to extract from every page (1-50). Each entry has a unique name, a CSS selector, and a mode: text (selected text) or html (inner HTML). Selector syntax is validated before any network work.

## `maxPages` (type: `integer`):

Maximum number of page URLs checked per run. URLs beyond this cap are skipped.

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

Maximum number of pages fetched concurrently.

## `timeoutSecs` (type: `integer`):

Per-request timeout applied to page fetches.

## `maxRedirects` (type: `integer`):

Maximum redirect hops followed per page. Longer chains fail the page with a fetch error.

## `normalizeWhitespace` (type: `boolean`):

Collapse whitespace runs in extracted values to single spaces before hashing and comparison, so layout-only formatting changes are ignored.

## `includeUnchanged` (type: `boolean`):

Also emit selector-result records for selectors that did not change. Disabled by default: only changes are reported.

## Actor input object example

```json
{
  "monitorId": "default-store-check",
  "urls": [
    "https://example.com/"
  ],
  "selectors": [
    {
      "name": "title",
      "selector": "title",
      "mode": "text"
    }
  ],
  "maxPages": 100,
  "concurrency": 10,
  "timeoutSecs": 20,
  "maxRedirects": 5,
  "normalizeWhitespace": true,
  "includeUnchanged": false
}
```

# 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 = {};

// Run the Actor and wait for it to finish
const run = await client.actor("kingii98/selector-content-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("kingii98/selector-content-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 kingii98/selector-content-change-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,kingii98/selector-content-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/XwfxdnbHBCZEKyiaM/builds/YyBEsqwczWdD5gBVo/openapi.json
