# Website Change Monitor: AI Page Change & Pricing Alerts (`rich_minds/website-change-monitor-ai`) Actor

First 25 changes free. Website change monitor, pay per qualified change — noise never charged by us: track website changes on competitor pricing pages and get alerts when a website changes, each with an AI summary and significance. Free demo on any plan; live check runs on your own Apify account.

- **URL**: https://apify.com/rich\_minds/website-change-monitor-ai.md
- **Developed by:** [Rich Minds](https://apify.com/rich_minds) (community)
- **Categories:** Automation, E-commerce, AI
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.00 / 1,000 qualified change (rules only)s

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

## Website Change Monitor: AI Page Change & Pricing Alerts

**Every real change on your competitors' pages, explained, in your inbox every week.**

⚡ First **25 changes free** · 💵 **$0.003** per qualified change · 🤖 **$0.015** with the AI verdict · ⏱️ demo in seconds

![One row per real change — competitor, page, category, significance 1–5, score, tier, the AI's summary and the next action](https://api.apify.com/v2/key-value-stores/2GF6C8GCROaDdWcvY/records/shortlist.svg)

> **Try it in 30 seconds.** Click **Try it** — the form runs a free before / after demo of four sample pages. Your
> first **25 qualified changes** are free; baselines, unchanged pages and cosmetic noise cost nothing here, and up to
> 50 pages are fetched here on any Apify plan.
> → [What a run costs](#-pricing--what-a-run-really-costs) · [What the AI adds](#-what-the-ai-tier-adds) · [Run it weekly](#-run-it-weekly)

### ⚡ At a glance

| | |
|---|---|
| **What you get** | one row per real change: category, significance 1–5, before → after prices — with the AI a summary and the next action |
| **You provide** | the pages to watch (pricing, changelog, careers, terms, product pages) — or just your competitors' names |
| **First run** | new pages are compared with their latest Internet Archive capture, so run 1 already shows changes |
| **Output** | a sheet sorted by significance plus a digest by e-mail, Slack, Teams or Discord |
| **Typical run** | 30 pages checked daily → ≈ 40 real changes a month, 4.4 % of 900 page checks (estimate — no live month measured yet) |
| **Cost of that run** | per month **$0.27 rules only · $1.37 with the AI**, all in (worked example below) |
| **Free tier** | first 25 qualified changes per account |
| **Keys / setup** | no API key where your Apify plan includes model access — otherwise your own key: a free Gemini / Groq key gives 1–3 AI verdicts per run (the rest keep the rule verdict at the basic price); a paid key assesses every change |
| **Works with** | Schedules, e-mail, Slack, Microsoft Teams, Google Chat, Discord, webhooks (Zapier, Make, n8n), Google Sheets, MCP & AI agents |

### 🎯 What this Actor does

It fetches your pages (up to 50 itself, on any Apify plan; more with
[Website Content Crawler](https://apify.com/apify/website-content-crawler)), compares each with its last version and
bills only the changes that pass your filters:

- **Pay per real change, not per page checked** — no start fee; baselines, unchanged pages and noise are never
  billed by us, and up to 20 pages a run pay no per-page fee.
- **An AI verdict instead of a raw diff** — category, significance, verbatim facts, relevance, impact and the action.
- **A first run that delivers** — pages seen for the first time are compared with the Internet Archive.

#### 💲 Competitor pricing changes, price by price

Every moved price is paired inside its table row — `priceChanges: [{"before": "$49", "after": "$59"}]`, flag
`price_moved` — and never dropped as noise.

#### 🧹 Website change detection without the noise

Dates, counters, © years and tokens are removed before comparing (plus `ignoreTextPatterns`; `cssSelector` watches
one region). Small edits and flapping pages are free.

#### 🗂️ New pages, removed pages, and a first-run baseline

`watchMode: section` reports `page_added` / `page_removed` under each URL; new pages are compared with their latest
Internet Archive capture (`baselineFrom: wayback`, the default).

#### 📈 How has their pricing moved this year?

`historyBackfill: 12` diffs one archive capture per month of each page in order — a dated row per past change and a
price timeline on the REPORT page; $0.10 per page with a past change, once.

### 🚀 How to use it

1. **Click `Try it`** — the free demo: four sample pages of a fictional competitor, three real changes.
2. **Put your competitors' pages into `startUrls`** — or just name them in `goalDescription` ("we compete with Notion
   and Slack on pricing"), or pick a `preset`. Keep `enableAi` on: your first 25 changes show the AI tier free.
3. **Press Start** — new pages are compared with the Internet Archive, so the first run already delivers changes.
4. **Schedule it daily or weekly** with `notifyEmail` — only new changes are delivered and charged.

![The alert sheet: a ready-to-post line per change with its significance and business impact](https://api.apify.com/v2/key-value-stores/2GF6C8GCROaDdWcvY/records/alerts.svg)

📚 **Learn more:** [Step-by-step tutorial — how to monitor competitor websites with this Actor](https://api.apify.com/v2/key-value-stores/2GF6C8GCROaDdWcvY/records/tutorial.html)

### 🆚 Why this instead of running Website Content Crawler yourself?

| | [Website Content Crawler (`apify/website-content-crawler`)](https://apify.com/apify/website-content-crawler) | **This Actor** |
|---|---|---|
| **Price** | free Actor, ≈ $0.2 per 1,000 pages · 11,774 users / 30 days | from $0.003 per qualified change; ≤ 50 pages fetched here, no crawl bill |
| **What you get** | every page's full text again, every run — no memory | only what changed, classified and scored |
| **Same 100 changes, all in** | $0.45 of crawling (≈ 2,270 page checks at 4.4 %) — 2,270 unsorted pages you diff by hand | **100 ranked, explained changes: $0.68 rules only · $3.46 with the AI, all in** (30-page lists are fetched here) — junk costs $0 |

#### vs other website change monitors

100 changes all in (76 daily runs of 30 pages), Store prices of 2026-09-27/28:

| Actor | Users / 30 d | 100 changes | Lacks (source) |
|---|---|---|---|
| **This Actor** | new | **$0.68 · $3.46 with AI** | — |
| `santhej/web-watchdog` | 1 | $1.71 | "No LLM call", no JS rendering (its README) |
| `muhammad-bilal/web-drift-detector` | 1 | $1.82 | AI needs your OpenAI key (its README) |
| `fayoussef/website-change-monitor` | 1 | $4.54 | server-rendered HTML only (its README) |
| `inovaflow/website-change-tracker` | 2 | $9.60 | ≈ $0.10 start fee per run (its README) |
| `happitap/competitor-marketing-change-monitor` | 5 | $23.54 | $0.25 start fee, 5.7 % failed runs (Store) |
| `tagadanar/saas-pricing-monitor` | 1 | $25.08 | pricing pages only (its README) |
| `firecrawl-website-change-monitor---track-page-changes-with-ai` (#1 Store result) | 2 | $32.40 | $0.10 start + $0.01 per page check, changed or not; your own Firecrawl key; no AI verdict (its README) |
| `ryanclinton/website-change-monitor` | 9 | $227.00 | $0.10 per site per run, changed or not |

A measured head-to-head on the same URLs is dated in the roadmap (2026-10-15).

#### 💸 Cheaper than a Visualping or Distill.io seat

30 pages checked daily cost **$0.27 a month rules only · $1.37 a month with the AI** here, against
[Visualping Personal 5K](https://visualping.io/pricing) **$35 / month** (20 pages), [Distill.io Starter](https://distill.io/pricing/) **$15 / month**,
[ChangeTower Lite](https://changetower.com/pricing/) $12 / month or [PageCrawl Standard](https://pagecrawl.io/pricing)
$13.33 / month (list prices, checked 2026-09-27). Free tools win elsewhere: **Visualping Free** (5 pages), the
**Distill.io browser extension (free plan)** (logged-in pages), **Google Alerts** (mentions, never an edit), the
**Wayback Machine** (history — our first-run baseline).

### 💵 Pricing — what a run really costs

Pay per result: **one event per qualified change — no start fee.**

| Event | When it is charged | Price |
|---|---|---|
| `free-tier` | your first 25 qualified changes, any mode | **$0.00** |
| `qualified-change-basic` | AI off — category, significance, lines, prices, alert | **$0.003** |
| `qualified-change-ai` | AI on — plus summary, facts, relevance, impact, action | **$0.015** |
| `page-watched` | each page checked beyond the first 20 of a run — up to 20 pages pay nothing | **$0.0005** |
| `digest-ai-brief` | the AI brief on top of a watch list's digest, only on a run with new changes | **$0.02** |
| `client-report` | a white-label client report (a `monitors` entry with `clientName`), run with new changes | **$0.05** |
| `history-backfill-page` | `historyBackfill` on: once per page whose archive history holds ≥ 1 past change | **$0.10** |

**One free tier:** your first 25 qualified changes are free, whatever the tier.

**This Actor never charges for:** baselines, unchanged, noise, flap, quarantined or error pages, changes your
filters drop, repeats, sample rows, the rule digest, the first 20 pages of a run, fetching up to 50 pages. **Bigger
lists:** Website Content Crawler bills your Apify account ≈ $0.2 per 1,000 pages, capped by `maxPagesPerRun`.

**How that compares** — "website change monitor" results in the Store charge a median $0.002, mostly per page
check: $0.003 is 1.5× that, but only per qualified change — one replaces 23 page checks ($0.00013 per raw-row
equivalent). Same-unit rivals charge $0.008–$0.02 per change.

**Worked example** — 30 pages checked daily = 900 page checks a month (fetched here, no crawl bill) → ≈ 40 qualified
changes (4.4 %): 40 × $0.003 + 300 × $0.0005 (10 pages over the 20 free, 30 runs) = **$0.27, $0.0068 per change**;
with the AI 40 × $0.015 + ≈ $0.18 tokens + ≈ 22 × $0.02 briefs instead → **$1.37, $0.034 per change**.

**At volume** (same 4.4 % estimate) — *agency*: 10 clients × 30 pages daily with the AI and white-label reports = 9,000
page checks → ≈ 400 changes: crawl $1.80 + 8,400 × $0.0005 + 400 × $0.015 + ≈ $1.84 tokens + ≈ 220 × ($0.05 report +
$0.02 brief) = **≈ $29 a month**, vs Visualping Business 20K $140 / month for 200 pages. *E-commerce*: 200 product
pages daily = 6,000 checks → ≈ 264 price / stock changes: crawl $1.20 + 5,400 × $0.0005 + 264 × $0.003 = **≈ $4.70 a
month** rules only, vs ChangeTower Business $78 / month for 200 pages.

**AI tier, all in:** ≈ $0.0196 per change, 6.5× the basic price: $0.015 here + ≈ $0.0046 tokens (measured
2026-09-27: 1,306 in / 666 out tokens per change at Haiku 4.5's $1 / $5 per million).

### 🤖 What the AI tier adds

The demo's pricing change, AI **off** — the rules pair the price and stop there:

```json
{"label": "Northwind Analytics", "category": "pricing", "significance": 5, "score": 95, "tier": "hot",
 "summary": "Northwind Analytics pricing: $49 → $59 in 'Pro | $59 per seat / month | unlimited dashboards | email support'.",
 "recommendedAction": "review_pricing"}
```

AI **on** — judged against "flag price, plan and launch changes our sales team must know" (local run with
`groq:openai/gpt-oss-120b`, 2026-09-28; on Apify the default model is `anthropic/claude-haiku-4.5`):

```json
{"label": "Northwind Analytics", "category": "pricing", "significance": 5, "relevance": 90, "score": 93, "tier": "hot",
 "summary": "The Pro plan price increased from $49 to $59 per seat per month, and the Business plan now includes an audit log.",
 "businessImpact": "The price hike makes our offering more competitive on cost, while the new audit‑log feature may require us to emphasize our own security capabilities.",
 "recommendedAction": "notify_sales"}
```

The AI reads the whole table (the audit log the rules missed) and says what it means and who acts — ≈ 8 minutes
saved per change, based on the steps it replaces: open both versions (≈ 2 min), find what moved in a 20-line diff
(≈ 3 min), write the before → after note and the action (≈ 3 min). It also ranks down changes irrelevant to your goal.

**Try the AI on any plan:** sample verdicts in the demo, **5 live AI verdicts on us** on your first real run, then
your own key: a free [Gemini key](https://aistudio.google.com/apikey) assesses a few changes per run, a paid one all.

### ⚙️ Input

| Field | Default | What it does |
|---|---|---|
| `startUrls` | — | The pages to watch; typed next to the demo they start the live check |
| `sourceMode` | `actor` | `actor` checks live, `list` / `dataset` diff what you have; `{}` runs the free demo |
| `preset` | `custom` | One click: a ready watch list for pricing, launches, legal, hiring, docs or e-commerce |
| `goalDescription` | — | What matters to you — the AI scores relevance on it; competitors named here are watched |
| `minScore` | `35` | The field that decides what you pay: significance × 20 − 5, blended with relevance when the AI is on |
| `maxQualified` | `100` | Cap on changes (and on this Actor's charge); the rest come next run |
| `maxDiscoveryChargeUsd` | `0.5` | Spend cap passed to the crawl |
| `enableAi` | `true` | The AI verdict per change |
| `baselineFrom` | `wayback` | New pages are compared with the Internet Archive; `none` only stores them |
| `historyBackfill` | `0` | Months of archive history per page (e.g. 12), diffed in order — $0.10 per page, once |
| `fetcher` | `auto` | `auto`: ≤ 50 static pages fetched here (any plan), the crawler otherwise |

<details><summary>Every other option (all in the Input tab)</summary>

`notifyEmail`, `teamsWebhookUrl`, `googleChatWebhookUrl`, `minSignificance` (2), `categories`, `watchMode` (`pages`), `crawlerType` (`cheerio`), `targetFlags`, `suppressionList`, `minChangedChars`, `ignoreTextPatterns`, `cssSelector`,
`ignoreSelectors`, `waitForSelector`, `screenshots`, `useSitemaps`, `respectRobotsTxtFile`, `sectionDepth`,
`maxPagesPerSection`, `maxPagesPerRun`, `monitors` (client watch lists, white-label `clientName` / `logoUrl` /
`agencyName`), `stateStoreName` (snapshot store), `includeUnchangedRows` (unchanged pages into a separate dataset),
`historyDatasetName` (running change log), `outcomes` (acted / ignored per change), `demoOnly` (force the free demo),
`llmProvider`, `llmModel`, `llmApiKey`, `aiCandidateMultiplier`, `slackWebhookUrl`, `discordWebhookUrl`,
`webhookUrl`, `webhookHeaders`, `webhookBatchSize`, `dedupeAcrossRuns`, `dedupeStoreName`, `datasetId`, `itemsList`,
`discoveryActorId`, `discoveryInput`, `maxToProcess`. Webhook URLs, headers and keys are stored encrypted.

</details>

#### Flags

Put a name from the first column into `targetFlags` (`priceMoved` works too); an unknown name stops the run.

<details><summary>All flags</summary>

| Flag (`targetFlags` value) | Meaning |
|---|---|
| `price_moved` | a price on the page changed (before → after in `priceChanges`) |
| `price_increase` / `price_decrease` | every moved price went up / down |
| `plan_change` | plan names, seats, tiers or add-ons changed |
| `new_feature` | a launch / "now available" / changelog entry was added |
| `legal_change` | terms, privacy policy or DPA text changed |
| `hiring_signal` | a job opening was added or removed |
| `new_page` / `page_removed` | a page appeared in / disappeared from a watched section |
| `title_changed` | the page title or meta description changed (messaging) |
| `large_change` | 20 % or more of the page text changed |
| `docs_change` | a docs / API reference / changelog page changed |
| `wayback_baseline` | compared with the latest Internet Archive capture (first run) |
| `stock_change` | an in-stock / sold-out / back-order / sale line was added or removed (product pages) |

</details>

### 📤 Output

One dataset item per qualified change — the demo's pricing change (AI on), shortened:

```json
{"changeId": "0acb90ba7dbf9bf711eee79e4c52d1547001cddb", "monitor": "default", "label": "Northwind Analytics",
 "url": "https://northwind-analytics.example/pricing", "changeType": "content_changed", "category": "pricing",
 "significance": 5, "priceChanges": [{"before": "$49", "after": "$59", "currency": "USD"}],
 "relevance": 90, "recommendedAction": "notify_sales", "ruleScore": 95, "score": 93, "tier": "hot",
 "chargedEvent": "demo", "isSample": true}
```

<details><summary>Every field of a row</summary>

| Field | Description |
|---|---|
| `changeId`, `dedupeKey` | One id per change (sha1 of URL + previous hash + current hash); `dedupeKey` is the same value |
| `itemId` | The page's id — the same on every change of that page |
| `monitor`, `label`, `domain` | Watch list, competitor name, domain |
| `url`, `loadedUrl`, `canonicalUrl` | The watched page (canonical link to map), after redirects, its canonical URL |
| `pageTitle`, `languageCode`, `depth`, `discoveredFrom` | Title, language, 0 = listed / 1+ = in a section, linking page |
| `changeType` | `content_changed`, `page_added`, `page_removed`, `title_changed` |
| `category`, `significance` | Kind of change, 1 trivial … 5 critical (the AI's when it judged the change) |
| `summary`, `keyFacts`, `priceChanges` | ≤ 2 sentences; before → after facts with verbatim quotes; every moved price |
| `addedLines`, `removedLines`, `addedChars`, `removedChars`, `changedPercent` | The diff (≤ 20 lines × 300 chars) and its size |
| `relevance`, `businessImpact`, `recommendedAction`, `scoreReasons` | AI relevance 0–100, impact, action, reasons |
| `alertMessage` | A ready-to-post alert line for Slack, Teams or a CRM note, on every row |
| `previousCheckedAt`, `checkedAt`, `previousHash`, `currentHash`, `baselineSource` | Both versions' check times and hashes; `previous_run`, `wayback` or `wayback_history` (a past change) |
| `screenshotUrl` | Current screenshot (browser crawler + `screenshots`) |
| `flags`, `targetFlagsMatched` | Rule flags; which of your `targetFlags` matched |
| `ruleScore`, `fitScore`, `score`, `tier` | Rule score, AI score, final ranking score, hot / warm / cold |
| **Which score to trend on** | `ruleScore` — the same rule scale on every row and every run; `score` is the AI score where `aiModel` is set, else the rule score: sort one run on `score`, trend across runs on `ruleScore` |
| `assessmentSource`, `aiModel`, `billed`, `chargedEvent`, `billedAs`, `isSample`, `sourceActor` | Provenance and billing (`billedAs` = the price tier, also on free rows) |

</details>

**Upsert key for a CRM or Sheet:** `dedupeKey` — the change id of the cross-run memory; `itemId` is the page, `url`
the canonical link.

**Which record to read:** `OUTPUT` (= `RUN_SUMMARY`) is the run record with `OUTPUT.mode`; every run also writes a
**`DIGEST`** and a client-ready **`REPORT`** page.

<details><summary>DIGEST, REPORT, client reports, views and the OUTPUT funnel</summary>

The **`DIGEST`** (Markdown: changes, hot ones, **what changed since the last run** — "vs last run:
qualified 4 → 6, hot 1 → 2, price moves 0 → 1" — then per competitor the AI brief or the changes by category, and
"your team acted on 12 of 20 changes marked" once you send `outcomes`), **`DIGEST_JSON`**, a `DIGEST-<monitor>`
per client, a client-ready **`REPORT`** page (`OUTPUT.reportUrl`) and, for a `monitors` entry with `clientName`, a
white-label **`REPORT-<monitor>`** (`OUTPUT.clientReports`). Views: `Shortlist`, `Price moves`, `Alerts (Slack /
CRM)`, `Before → after lines`.

`OUTPUT` holds `mode`, the funnel (pages loaded → baseline / unchanged / noise / flap / quarantined / error → changes
→ qualified), `sinceArchive`, `historyBackfill`, `pages` with the URLs per outcome, `pageErrors`, `chargedEvents`, `pagesCharged`,
`aiCoverage`, `trend`, `history`, `historyDataset`, `outcomes`, `timing`.

</details>

⭐ **Found it useful? A review on the Store helps others find it** — it takes a minute on the Actor's page.

### 🔁 Run it weekly

1. **Open the saved task and click Schedule** after your first real run (`OUTPUT.scheduleTaskUrl`), with your address
   in `notifyEmail`: every run with new changes e-mails the digest **and the trend vs the last run**.
2. Keep `stateStoreName` the same (each page's last version); `dedupeAcrossRuns` never re-charges a change.
3. Add a Slack, Teams, Google Chat or Discord webhook, or a `webhookUrl`.
4. Name a `historyDatasetName`: one running, dated change log across runs.
5. Send `outcomes` (`<changeId> = acted`) — the digest counts what your team acted on.

Weekly cost is the new changes only; week 2 has as many changes as the competitors made: ≈ 4.4 % of page checks —
30 pages checked daily ≈ 40 changes a month, **$0.27 a month rules only, $1.37 a month with the AI**.

#### 🎯 Try it for your niche

Pick one in `preset` and press Start — three public pages, compared with the Internet Archive at once:
`competitor-pricing` · `product-launches` (changelogs) · `legal-policy` · `hiring-signals` (careers) ·
`docs-api-changes` · `ecommerce-price-stock` (price cuts, sales, sold out / back in stock).

<details><summary>competitor-pricing input</summary>

```json
{"preset": "competitor-pricing", "sourceMode": "actor", "startUrls": [{"url": "https://www.notion.com/pricing"}, {"url": "https://slack.com/pricing"}, {"url": "https://www.atlassian.com/software/jira/pricing"}], "baselineFrom": "wayback", "categories": ["pricing", "plan_packaging"], "minSignificance": 3}
```

</details>

### 🔌 Integrations, automation and API

<details><summary>Webhook, Google Sheets, CRM, AI agents</summary>

- **Webhook** — `webhookUrl` (or one per `monitors[]` entry) POSTs `{"event": "change.qualified", "change": {…},
  "runId": "…"}`; with `webhookBatchSize` > 1 `{"event": "changes.qualified", "changes": [{…}, …], "runId": "…"}`
  (the last batch may be shorter). A 429 / 5xx is retried once after 2 s, a failed POST is counted in
  `OUTPUT.webhook.failed` — a webhook failure never fails the run.
- **Google Sheets** — the Integrations tab, or `historyDatasetName` for one log across runs. **HubSpot / Pipedrive
  (CRM)** — a Make / Zapier "create or update" step: note = `alertMessage` + `businessImpact`, company = `label`,
  link = `url` — upsert on `dedupeKey`, never a duplicate.
- **AI agents / MCP** — plain typed inputs. The snippet runs the **real check**, capped by `maxDiscoveryChargeUsd`;
  the free first call (`{}`, `demoOnly: true` or `sourceMode: list` with page texts) is the commented line.

</details>

**API:** copy-paste snippets — the real check, capped by `maxDiscoveryChargeUsd`, timeout from the run-time FAQ.

<details><summary>Python</summary>

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_API_TOKEN>")
run = client.actor("rich_minds/website-change-monitor-ai").call(
    run_input={
        # free first call instead: {"demoOnly": True} or {"sourceMode": "list", "itemsList": [{"url": "…", "text": "…"}]}
        "startUrls": [{"url": "https://www.notion.com/pricing"}, {"url": "https://slack.com/pricing"}],
        "goalDescription": "Flag price, plan and seat-limit changes our sales team must know.",
        "maxQualified": 50,
        "maxDiscoveryChargeUsd": 0.5,  # spend cap on your account for the crawl
    },
    timeout_secs=3600,  # see "How long does a run take?"
    # build="0.3",  # pin the build your pipeline was tested with (Changelog below)
)
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item["url"], item["score"], item["tier"], item["summary"])
```

</details>

<details><summary>JavaScript</summary>

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

const client = new ApifyClient({ token: '<YOUR_API_TOKEN>' });
const run = await client.actor('rich_minds/website-change-monitor-ai').call({
    // free first call instead: { demoOnly: true } or { sourceMode: 'list', itemsList: [{ url: '…', text: '…' }] }
    startUrls: [{ url: 'https://www.notion.com/pricing' }, { url: 'https://slack.com/pricing' }],
    goalDescription: 'Flag price, plan and seat-limit changes our sales team must know.',
    maxQualified: 50,
    maxDiscoveryChargeUsd: 0.5, // spend cap on your account for the crawl
}, { timeout: 3600 /* , build: '0.3' — pin the build your pipeline was tested with */ });
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items.map((i) => [i.url, i.score, i.tier, i.summary]));
```

</details>

<details><summary>Use it from Claude, ChatGPT or any MCP client</summary>

```json
{"mcpServers": {"apify": {"url": "https://mcp.apify.com/?actors=rich_minds/website-change-monitor-ai"}}}
```

Then ask: *"Run website-change-monitor-ai with `{}` (free demo), then watch notion.com/pricing and tell me what changed."*

</details>

**Minimal agent input:** `{"startUrls": [{"url": "https://www.notion.com/pricing"}], "goalDescription": "…", "maxQualified": 20, "maxDiscoveryChargeUsd": 0.5}`

#### Which call runs what — demo, live search or your own rows

<details><summary>Input shape → <code>OUTPUT.mode</code> → can it charge?</summary>

| Input shape | `OUTPUT.mode` | Can it charge? |
|---|---|---|
| `{}`, the untouched form, or `demoOnly: true` / `demo: true` with anything else | `demo` | no — sample rows are `chargedEvent: demo` |
| the sample pages + your own `startUrls` typed next to them | `live` | yes — per qualified change (+ pages beyond 20); beyond 50 pages the crawl on your account, capped by `maxDiscoveryChargeUsd` |
| `sourceMode: "actor"` + `startUrls` / `monitors`, or a `goalDescription` naming competitors | `live` | yes — as above |
| `sourceMode: "list"` + your own `itemsList` pages | `list` | yes — per qualified change, no crawl |
| `sourceMode: "dataset"` + `datasetId` | `dataset` | yes — per qualified change, no crawl |

</details>

#### List-mode rows — what a pasted record needs

`sourceMode: "list"` runs on any plan: a row needs `url` and `text` (or `markdown`).

<details><summary>Fields a row can carry</summary>

| Field | Needed? | What it enables |
|---|---|---|
| `url` | required | the page (its snapshot key) |
| `text` | required (or `markdown`) | the version to compare |
| `previousText` (+ `previousTitle`, `previousCheckedAt`) | optional | compare with this instead of the stored snapshot |
| `title`, `metaDescription`, `label`, `monitor` | optional | `pageTitle`, `title_changed`, competitor name, watch list |
| `crawl.loadedTime`, `crawl.depth`, `crawl.httpStatusCode`, `metadata.canonicalUrl`, `screenshotUrl` | optional | Website Content Crawler's own fields: `checkedAt`, `depth`, error rows, `canonicalUrl`, `screenshotUrl` |

</details>

#### Run outcomes — what your integration sees

<details><summary>Every outcome: run status, dataset, <code>OUTPUT</code> field, charged?, what to do</summary>

| Outcome | Run status | Dataset | `OUTPUT` | Charged? | What to do |
|---|---|---|---|---|---|
| Free demo (the pre-filled sample) | SUCCEEDED | sample changes | `qualified`, `mode: demo` | no | put your pages into `startUrls` |
| First run (changes since the archive capture) | SUCCEEDED | the changes since each page's latest capture | `sinceArchive` | per qualified change | schedule it |
| First run, page never archived | SUCCEEDED | no row for that page | `pages.byStatus.baseline`, `scheduleInput` | no | the status names the date of the run with changes |
| Success | SUCCEEDED | qualified changes, most significant first | funnel, `chargedEvents`, `aiCoverage` | per qualified change | — |
| Nothing changed / nothing passed | SUCCEEDED | empty | `qualified: 0`, `pages` | pages beyond 20 only | loosen `minScore` / `minSignificance` |
| Some pages blocked or empty | SUCCEEDED | changes of the other pages | `pageErrors` | no, for those pages | try `crawlerType: playwright:adaptive` |
| Demo text still in place | SUCCEEDED | empty | `inputNeeded` | no | replace the field the status names |
| Invalid input | FAILED | empty | — | no | fix the field the status message names |
| Source failed (nothing loaded) | FAILED | empty | `sourceError` | no | follow the reason (plan, timeout); on any plan paste [list-mode rows](#list-mode-rows--what-a-pasted-record-needs) |
| Crawler refused by your plan | SUCCEEDED | changes of the listed pages, fetched here | `sourceStatus.crawlerRefused` | per qualified change | sections / JS pages need a plan that runs Store Actors |
| Source partial (the crawl timed out / was aborted) | SUCCEEDED | changes of the pages loaded | `sourceStatus` (`status`, `partial: true`) | per qualified change | raise `discoveryTimeoutSecs` or lower `maxPagesPerRun`; no page is marked removed |
| AI unavailable | SUCCEEDED | rule-verdict changes (the demo: labelled sample AI verdicts) | `aiError`, `freeAiReserve`, `aiCoverage.sampleVerdicts` | free while your free changes last, then basic price | `llmProvider: byok` with your own key |

</details>

### 👥 Who is it for?

| You are… | You run it to… | Start with |
|---|---|---|
| Product marketer / competitive intelligence | catch a rival's pricing, packaging or messaging change and update the battlecard | `preset: competitor-pricing` |
| Founder or PM | watch 5–10 competitors' pricing, changelog and careers pages; one weekly digest | `preset: product-launches` |
| E-commerce seller | track competitor product pages: price cuts, sale badges, sold out / back in stock — daily | `preset: ecommerce-price-stock` |
| Agency | every client's watch list in one run, a digest, webhook and white-label report each | `monitors` + `clientName` |
| Legal / vendor risk | flag changes to vendors' terms, privacy policies and DPAs | `preset: legal-policy` |

#### 🏷️ For agencies — white-label client reports

Give each `monitors` entry a `clientName`, `logoUrl` and `agencyName`: every run with new changes writes
`REPORT-<name>` with the client's logo and "Prepared by <your agency>" ($0.05 per report). Ten clients, one run.

### 🧠 How the AI works

The model judges one change at a time against your goal; what it cannot quote from the page is dropped.

<details><summary>Grounding, scores, model</summary>

- **Grounded.** The model sees one change — the `-` / `+` lines (≤ 4,000 chars), ≤ 600 chars of context, the rule
  verdict and your goal. A fact not quoted verbatim is dropped, a summary with a number the page lacks falls back
  to the rule summary, a moved price is never noise.
- **Derived scores.** `score` = significance × 20 − 5, blended 60 / 40 with relevance.
- **Tokens where they matter.** The best rule-scored changes first — every change at least one significance point
  under `minSignificance` — up to `aiCandidateMultiplier` × `maxQualified`; one digest brief per monitor.
- **Model.** `anthropic/claude-haiku-4.5` via Apify, or your own key; on a failure the rule verdict stays.

</details>

### 🔒 Data, compliance and limits

- Public pages only; `respectRobotsTxtFile` honours robots.txt; snapshots stay on your account. Respect the sites'
  terms and the law (GDPR, CCPA).
- A blocked or empty page is an error row, never a removal.

### ❓ FAQ

**How much will one run cost me?** 30 pages: no crawl bill (fetched here) + $0.003 per real change ($0.015 with the
AI) + $0.0005 per page beyond the first 20.

**What will my first real search cost?** The changes found: up to 50 pages and their archive captures are fetched here;
bigger lists add ≈ $0.2 per 1,000 pages crawled.

<details><summary>More questions</summary>

**Is this a Website Content Crawler with a diff?** Up to 50 pages are fetched by this Actor; bigger lists, sections and
JavaScript pages run `apify/website-content-crawler` on your account — plus memory.

**How does website change detection work here?** Lines are normalised and hashed; a new hash is diffed line by line.

**Can I track website changes on JavaScript pages?** Yes: `crawlerType: playwright:adaptive` ($0.5–5 per 1,000 pages).

**Can I get alerts when a website changes?** Yes — `notifyEmail`, Slack, Microsoft Teams, Google Chat, Discord or a
webhook, per run or per client.

**What counts as a page change alert?** A change that passes your filters and is no flap, churn or repeat.

**Is this a web page diff tool?** A web page diff with memory and a verdict.

**Does it catch competitor pricing changes?** Yes — every moved price lands in `priceChanges` (`$49 → $59`).

**Can it watch e-commerce product prices and stock?** Yes — `preset: ecommerce-price-stock`: every moved price
(`minChangedChars` 0) and every sold-out / back-in-stock / sale line (`stock_change`) on the product pages you list.

**How do I monitor competitor websites for several clients?** `monitors`: pages, goal, snapshots, `DIGEST-<name>`,
webhook and a white-label report per client, one run for all.

**Is it a Visualping alternative or a Distill io alternative?** For competitor website monitoring at scale, yes: no
seat, no page cap — $0.27–$1.37 a month for 30 pages checked daily, on any Apify plan, vs a $15–35 / month seat.

</details>

**Why pay when Visualping Free, the Distill.io browser extension (free plan), Google Alerts or the Wayback Machine are free?** They win for 5
pages, logged-in pages or brand mentions; none gives a typed, scored row per change across dozens of pages.

**How long does a run take?** The demo takes seconds, a 30-page check seconds too (built-in fetcher; the first run's
archive lookups add ≈ 1 min) plus the AI per change: ≈ 2 s via Apify, ≈ 34–160.9 s per change with a free own key
(measured) — pass `timeoutSecs` 3600 (≈ 300 for 30 pages, rules only).

<details><summary>Measured time per mode</summary>

| Mode | Time | `timeoutSecs` |
|---|---|---|
| Free demo (4 sample pages) | measured 0.0 s rules only, 6.1 s with the AI on an own key | 120 |
| Your own page texts (`list` / `dataset`), rules only | measured 0.0 s for 4 pages | 300 |
| Live check, built-in fetcher, rules only | measured 2026-09-28: 3 pages fetched in 0.7 s, 70 s in all on a first run (archive lookups) | 300 |
| Live check with Website Content Crawler (> 50 pages) | ≈ 1–2 min for 100 pages with `cheerio` (estimate, dated in the roadmap) | 900 |
| AI via Apify's model access | ≈ 2 s per change (estimate, dated in the roadmap) | 1800 |
| AI with a free own key (`byok`) | measured 2026-09-27: ≈ 34 s per change with a free `google:gemini-3.6-flash` key (50 changes ≈ 1880 s), ≈ 62.7–160.9 s per change with a free `groq:openai/gpt-oss-120b` key — 50 changes would need ≈ 8225 s, so the AI stops after its backoff budget and the rest keep their rule verdict | 3600 |
| AI with a paid own key | measured 1.6 s per change while the key was not rate-limited (Groq `openai/gpt-oss-120b`, 3 of 3 in 4.8 s, 2026-09-27) | 1800 |

Own key: 2 calls at a time, one after a rate limit, spaced to the key's retry hint (30 s measured on Groq); the AI stops
after 2 minutes of waiting (`OUTPUT.timing.aiBackoffSecs`). Worst case with a slow own key: 30 s × 100 × 2 ÷ 2 ≈ 50
min per 100 changes — the AI stops before the run's timeout, every row is still pushed. Own-key throughput: ≈ 37
changes per minute on a Groq key before its rate limit, 2 per minute after it (measured 2026-09-27).

</details>

**Which Apify plan do I need?** Any plan runs the demo, `list` mode and the live check of up to 50 static pages
(fetched here, `fetcher: auto`); bigger lists, sections, JavaScript pages and the built-in AI need a plan that runs
Store Actors — otherwise your own AI key (`llmProvider: "byok"`).

**Will I be charged for the same change twice?** No — `dedupeAcrossRuns` remembers every change.

<details><summary>Free changes, legality</summary>

**Do rule-scored changes use my free ones?** Yes, while the AI is unavailable — the AI reserve keeps the last 5 so your first AI-assessed changes are free too (`OUTPUT.chargeSplit.freeKeptForAi`).

**Is the data public / is this legal?** Public pages only; you choose them and respect their terms.

</details>

### 🧩 More Actors from the same developer

- **[Facebook Ad Library Winning Ads Finder](https://apify.com/rich_minds/fb-ad-library-winning-ads)** — See which of your competitors' ads run longest, with the hook, offer and angle broken down.
- **[LinkedIn Buying Intent Leads](https://apify.com/rich_minds/linkedin-intent-leads)** — Hand your sales team the people asking for a tool like yours this week.

### 🆘 Support

Something missing or wrong? Open an issue on the Actor's page — requests from buyers ship first.

### 📝 Changelog

- **0.3.2** (2026-09-28) — basic price $0.0057 → **$0.003** per qualified change.
- **0.3.1** (2026-09-28) — the live check runs on any Apify plan: up to 50 static pages are fetched by the Actor itself
  (Website Content Crawler for bigger lists, sections and JavaScript pages); `historyBackfill` — months of Internet
  Archive history per page on the first run; digests to Microsoft Teams and Google Chat; event `history-backfill-page`;
  new title "AI Page Change & Pricing Alerts".
- **0.2.2** (2026-09-28) — the measured own-key AI pace and its timeout in the run-time FAQ and the form.
- **0.2.1** (2026-09-28) — first run delivers (Internet Archive baseline by default), competitors from the goal, e-commerce preset, change log, `outcomes`, white-label reports, `OUTPUT.mode`; events `page-watched`, `digest-ai-brief`, `client-report`.
- **0.1.1** (2026-09-27) — first release: one row per real change on your watched pages, AI verdict, section watch, Wayback baseline, per-client digests.

# Actor input Schema

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

Competitor pages to watch — pricing, changelog, careers, terms, product pages. Each run compares every page with its last version (first run: its latest Internet Archive capture), one row per real change. Up to 50 static pages run on any Apify plan; bigger lists, sections and JavaScript pages need a plan that runs Store Actors.

## `sourceMode` (type: `string`):

<b>actor</b> — check the pages live (≤ 50 static pages on any plan; more via Website Content Crawler, ≈ $0.2 per 1,000 pages). <b>dataset</b> — a Website Content Crawler dataset. <b>list</b> — page texts you send. The chosen mode always runs; only the untouched sample next to typed pages switches to the live check. <code>{}</code> runs the free demo.

## `preset` (type: `string`):

Pick a job and press Start: it fills the goal, the categories, the significance bar and three public example pages you left empty, and compares them with the Internet Archive at once. Your own values always win. <b>Custom</b> = your own pages.

## `goalDescription` (type: `string`):

What matters to you, in a sentence or two — the AI scores each change's relevance against it. Name your competitors ("we compete with Notion and Slack") or their domains (<code>notion.com</code>) and, with no <code>startUrls</code>, their pricing / changelog / careers pages are watched — the status lists them for you to keep.

## `minScore` (type: `integer`):

The field that decides what you pay: changes scoring below this are dropped and <b>not charged</b>. Score = significance × 20 − 5 (1 → 15 … 5 → 95); with the AI it is blended with relevance to your goal. 35 = significance 2 and up.

## `maxQualified` (type: `integer`):

Hard cap on results — and the ceiling of what <b>this Actor</b> charges: at most this many × $0.015 with AI on (× $0.003 with AI off), e.g. 100 × $0.015 = $1.50. Changes over the cap are reported next run, never lost.

## `maxDiscoveryChargeUsd` (type: `number`):

Hard cap passed to the Website Content Crawler run in <b>actor</b> mode — $0.50 in the form and for an API or agent call that leaves it out. The crawler bills platform usage, so the page cap <code>maxPagesPerRun</code> is what keeps a big section small.

## `enableAi` (type: `boolean`):

Category, significance, summary, before → after facts quoted from the page, relevance to your goal and a recommended action per change. <b>On = $0.015 per qualified change + AI tokens; off = $0.003</b> (rule-based category, significance and price facts).

## `notifyEmail` (type: `string`):

After every run with at least one new change, the digest (per competitor, most significant first, with links) is e-mailed here through Apify's send-mail Actor. Schedule the run weekly and only new changes reach your inbox. Never sent for the free demo.

## `minSignificance` (type: `integer`):

Only changes at least this significant are delivered (1 trivial … 5 critical: a price or plan change is 4–5, a new blog post 1–2). Free filter — dropped changes are never charged.

## `categories` (type: `array`):

Deliver only changes of these kinds. Empty = every category.

## `targetFlags` (type: `array`):

Only deliver changes with at least one of these flags, e.g. <code>price\_moved</code>, <code>price\_increase</code>, <code>stock\_change</code>, <code>new\_page</code>, <code>legal\_change</code> (<code>priceMoved</code> works too); an unknown name stops the run with the valid ones.

## `suppressionList` (type: `array`):

URLs (or their start), globs such as <code>*/blog/*</code>, domains or labels whose changes are never reported or charged.

## `minChangedChars` (type: `integer`):

An edit smaller than this is noise — not delivered, not charged. A moved price is never noise.

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

Regular expressions removed before comparing, on top of the built-in ones (dates, clock times, '5 min ago', view / like counters, © years, session tokens), e.g. <code>Only \d+ left</code>.

## `watchMode` (type: `string`):

<b>pages</b> — only the listed pages. <b>section</b> — every page under each listed URL (<code>/blog</code>, <code>/changelog</code>, <code>/careers</code>): new pages are reported as <code>page\_added</code>, pages gone in two successful runs as <code>page\_removed</code>.

## `sectionDepth` (type: `integer`):

Section mode: how many links deep below each listed URL.

## `maxPagesPerSection` (type: `integer`):

Section mode: pages crawled per listed URL.

## `maxPagesPerRun` (type: `integer`):

Cost guard: the crawl never loads more pages than this (≈ $0.04 of platform usage for 200 pages with the default crawler).

## `crawlerType` (type: `string`):

<b>cheerio</b> (plain HTTP) is the cheapest and fine for most pricing and legal pages. Pick a browser when the page builds its content with JavaScript (the diff comes back empty or 'blocked').

## `fetcher` (type: `string`):

<b>auto</b> (default) — up to 50 listed pages with the plain-HTTP crawler are fetched by this Actor itself: any Apify plan, no second Actor, seconds for 30 pages; bigger lists, whole sections, browser crawlers and screenshots run Website Content Crawler, and if your plan refuses it the listed pages are fetched here instead. <b>builtin</b> — always fetch the listed pages here. <b>crawler</b> — always Website Content Crawler.

## `baselineFrom` (type: `string`):

<b>wayback</b> (default) — a page seen for the first time is compared with its latest Internet Archive capture, so your first run already shows what changed since then (one extra crawl per new page, at most 50). A page the archive never captured is stored, and the status names the run that shows its changes. <b>none</b> — only store the first version of each page.

## `historyBackfill` (type: `integer`):

Opt-in, e.g. <b>12</b>: for each watched page, one Internet Archive capture per month of the last N months is diffed in order — one dated row per past change (<code>baselineSource: wayback\_history</code>) and a price timeline on the REPORT page, so the first run answers "how has their pricing moved this year?". $0.10 per page whose history holds at least one past change, once per page (never again on later runs). 0 = off.

## `cssSelector` (type: `string`):

e.g. <code>.pricing-table</code> or <code>main</code> — everything else on the page is ignored.

## `ignoreSelectors` (type: `string`):

Added to the crawler's default removals (nav, footer, dialogs), e.g. <code>.testimonials, .ticker</code>.

## `waitForSelector` (type: `string`):

A CSS selector the browser waits for before reading the page.

## `useSitemaps` (type: `boolean`):

Section mode: also read the site's sitemap to find pages no link points to.

## `screenshots` (type: `boolean`):

Adds <code>screenshotUrl</code> to each change row; browser crawlers only.

## `respectRobotsTxtFile` (type: `boolean`):

Skip pages the site's robots.txt disallows.

## `monitors` (type: `array`):

For agencies: several named watch lists in one run, each with its own goal, snapshot history, DIGEST record and webhook, e.g. <code>\[{"name": "client-a", "urls": \["https://rival.com/pricing"], "goal": "…", "webhookUrl": "https://…", "clientName": "Acme Inc.", "logoUrl": "https://…/logo.png", "agencyName": "Your Agency"}]</code>. With <code>clientName</code> the entry also gets a white-label REPORT page under your agency's name (<code>REPORT-client-a</code>, $0.05 per client report on a run with new changes). The check runs Website Content Crawler on your Apify plan.

## `llmProvider` (type: `string`):

<b>Apify (no keys)</b> — the AI runs through Apify's built-in OpenRouter proxy; tokens are billed to your Apify account at OpenRouter's rates. <b>My own key</b> — use your OpenAI / Anthropic / Gemini / Groq key instead: with a free Gemini / Groq key expect 1–3 AI verdicts per run (the rest keep the rule verdict at the basic price); a paid key (billing on) assesses every change. Own-key pace, measured 2026-09-27: ≈ 34 s per change with a free google:gemini-3.6-flash key (50 changes ≈ 1880 s), ≈ 62.7–160.9 s per change with a free groq:openai/gpt-oss-120b key (2 calls at a time, one after a rate limit), so 50 changes need a timeout of ≈ 8225 s.

## `llmModel` (type: `string`):

Leave empty for the default (<code>anthropic/claude-haiku-4.5</code>). Apify mode takes an OpenRouter slug such as <code>openai/gpt-4.1-mini</code>; own-key mode takes <code>provider:model</code>, e.g. <code>anthropic:claude-haiku-4-5-20251001</code>.

## `llmApiKey` (type: `string`):

Required when <b>AI model access</b> is <i>My own API key</i>. Stored encrypted by Apify, never logged.

## `aiCandidateMultiplier` (type: `integer`):

How many changes the AI judges, as a multiple of <b>Max changes to output</b>: the best rule-scored changes first (every change at least one significance point under <code>minSignificance</code>), up to this × <code>maxQualified</code>.

## `slackWebhookUrl` (type: `string`):

A Slack incoming-webhook URL (<code>https://hooks.slack.com/services/…</code>). After every run with at least one new change, the digest (per competitor, most significant first, with links) is posted to that channel as a readable message. Never sent for the free demo.

## `discordWebhookUrl` (type: `string`):

A Discord channel webhook URL (<code>https://discord.com/api/webhooks/…</code> — Server settings → Integrations → Webhooks). After every run with at least one new change, the same digest as Slack (per competitor, most significant first) is posted to that channel. Never sent for the free demo.

## `teamsWebhookUrl` (type: `string`):

A Teams Workflows webhook URL (in the channel: ⋯ → Workflows → <i>Post to a channel when a webhook request is received</i>). After every run with at least one new change, the same digest as Slack (per competitor, most significant first, with links) is posted to that channel as a card. Never sent for the free demo.

## `googleChatWebhookUrl` (type: `string`):

A Google Chat space webhook URL (<code>https://chat.googleapis.com/v1/spaces/…</code> — space → Apps & integrations → Webhooks). After every run with at least one new change, the same digest as Slack is posted to that space. Never sent for the free demo.

## `webhookUrl` (type: `string`):

Qualified changes are POSTed here as JSON (Zapier, Make, n8n, your CRM). For Google Sheets / Slack you can also use Apify's built-in Integrations tab. Stored encrypted — Zapier / Make hook URLs carry their secret in the path.

## `webhookHeaders` (type: `object`):

Extra HTTP headers for the webhook, e.g. <code>{"Authorization": "Bearer …"}</code>. Stored encrypted by Apify, never logged.

## `webhookBatchSize` (type: `integer`):

1 = one POST per change the moment it is ready. Higher = one POST per N changes.

## `dedupeAcrossRuns` (type: `boolean`):

Remembers every change you were charged for (in a named key-value store on your account), so re-running on the same pages or dataset never re-delivers or re-charges it.

## `dedupeStoreName` (type: `string`):

Key-value store for the delivered-change memory. Left at the default, the memory is kept per watch list (source, pages, monitors): rewording <code>goalDescription</code> keeps it. Running it for several clients on one account, type one name per client (or use <code>monitors</code>).

## `historyDatasetName` (type: `string`):

Optional. Type a name (e.g. <code>website-change-monitor-log</code>) and every run appends its new changes — date, competitor, category, price before → after — to that named dataset on your account: one change log across months to connect to Google Sheets or your BI tool once. Empty = off. Costs nothing extra.

## `outcomes` (type: `array`):

One line per change you followed up: <code>\<changeId, dedupeKey or page URL> = acted</code> (or <code>= ignored</code>). Remembered per watch list; the digest and <code>OUTPUT.outcomes</code> then say "your team acted on 12 of 20 changes marked" — the return a schedule is kept for. Free.

## `stateStoreName` (type: `string`):

Named key-value store on your account with the last version of every watched page (one namespace per monitor). Keep it the same on every scheduled run; a new name starts a new baseline.

## `includeUnchangedRows` (type: `boolean`):

Baseline, unchanged and noise pages go to a separate named dataset (<code>website-change-monitor-pages</code>) — free; the main dataset stays one row per change.

## `datasetId` (type: `string`):

Only for <b>dataset</b> mode: a Website Content Crawler dataset (any run you already schedule). Each page is compared with its stored snapshot like a live run.

## `itemsList` (type: `array`):

Only for <b>list</b> mode: JSON array of pages — <code>url</code> and <code>text</code> (or <code>markdown</code>) at least; optional <code>title</code>, <code>label</code>, <code>loadedTime</code>, and a <code>previousText</code> to compare against instead of the stored snapshot.

## `discoveryActorId` (type: `string`):

Actor used in <b>actor</b> mode. Any Actor whose output has Website Content Crawler's fields (<code>url</code>, <code>text</code> / <code>markdown</code>, <code>metadata.title</code>) works.

## `discoveryInput` (type: `object`):

Only for <b>actor</b> mode: keys merged over the Website Content Crawler input this Actor builds (e.g. <code>{"proxyConfiguration": {"useApifyProxy": true, "apifyProxyGroups": \["RESIDENTIAL"]}}</code>). Setting it sends the watch list to the crawler (a Store Actor on your Apify plan) instead of the built-in fetcher.

## `demoOnly` (type: `boolean`):

On = the run is the free demo on the sample pages whatever else the input says — nothing is crawled or charged (<code>"demo": true</code> works too). <code>OUTPUT.mode</code> says what every run was: <code>demo</code>, <code>live</code>, <code>list</code> or <code>dataset</code>.

## `maxToProcess` (type: `integer`):

Upper bound on how many detected changes are scored in one run (the rest are reported next run). Default = 3 × max changes.

## Actor input object example

```json
{
  "sourceMode": "list",
  "preset": "custom",
  "goalDescription": "We sell a B2B analytics tool that competes with Northwind: flag price, plan and launch changes our sales team must know this week.",
  "minScore": 35,
  "maxQualified": 100,
  "maxDiscoveryChargeUsd": 0.5,
  "enableAi": true,
  "minSignificance": 2,
  "categories": [],
  "minChangedChars": 20,
  "watchMode": "pages",
  "sectionDepth": 1,
  "maxPagesPerSection": 50,
  "maxPagesPerRun": 200,
  "crawlerType": "cheerio",
  "fetcher": "auto",
  "baselineFrom": "wayback",
  "historyBackfill": 0,
  "useSitemaps": false,
  "screenshots": false,
  "respectRobotsTxtFile": false,
  "llmProvider": "apify",
  "aiCandidateMultiplier": 2,
  "webhookBatchSize": 1,
  "dedupeAcrossRuns": true,
  "dedupeStoreName": "website-change-monitor-ai-seen",
  "historyDatasetName": "",
  "stateStoreName": "website-change-monitor-state",
  "includeUnchangedRows": false,
  "itemsList": [
    {
      "url": "https://northwind-analytics.example/pricing",
      "label": "Northwind Analytics",
      "title": "Pricing — Northwind Analytics",
      "loadedTime": "2026-09-27T08:00:00Z",
      "previousCheckedAt": "2026-09-20T08:00:00Z",
      "previousTitle": "Pricing — Northwind Analytics",
      "previousText": "Simple pricing for growing teams\nStarter | $0 | up to 3 seats | 1 dashboard\nPro | $49 per seat / month | unlimited dashboards | email support\nBusiness | $99 per seat / month | SSO | priority support\nEnterprise | Contact sales\nLast updated 3 days ago",
      "text": "Simple pricing for growing teams\nStarter | $0 | up to 3 seats | 1 dashboard\nPro | $59 per seat / month | unlimited dashboards | email support\nBusiness | $99 per seat / month | SSO | priority support | audit log included\nEnterprise | Contact sales\nLast updated 2 hours ago"
    },
    {
      "url": "https://northwind-analytics.example/changelog",
      "label": "Northwind Analytics",
      "title": "Changelog — Northwind Analytics",
      "loadedTime": "2026-09-27T08:00:00Z",
      "previousCheckedAt": "2026-09-20T08:00:00Z",
      "previousTitle": "Changelog — Northwind Analytics",
      "previousText": "Changelog\nv4.12 — Scheduled exports to Google Sheets\nv4.11 — Faster dashboard loading",
      "text": "Changelog\nv4.13 — Launched: AI anomaly alerts, now available on the Pro and Business plans\nv4.12 — Scheduled exports to Google Sheets\nv4.11 — Faster dashboard loading"
    },
    {
      "url": "https://northwind-analytics.example/careers",
      "label": "Northwind Analytics",
      "title": "Careers — Northwind Analytics",
      "loadedTime": "2026-09-27T08:00:00Z",
      "previousCheckedAt": "2026-09-20T08:00:00Z",
      "previousTitle": "Careers — Northwind Analytics",
      "previousText": "Join Northwind\nOpen roles\nProduct Designer — Remote (EU)\nBackend Engineer — Berlin",
      "text": "Join Northwind\nOpen roles\nSenior Sales Engineer — London (new EMEA team)\nAccount Executive, Enterprise — London\nBackend Engineer — Berlin"
    },
    {
      "url": "https://northwind-analytics.example/blog",
      "label": "Northwind Analytics",
      "title": "Blog — Northwind Analytics",
      "loadedTime": "2026-09-27T08:00:00Z",
      "previousCheckedAt": "2026-09-20T08:00:00Z",
      "previousTitle": "Blog — Northwind Analytics",
      "previousText": "How we cut dashboard load time in half\nSeptember 12, 2026 · 1,204 views\n© 2025 Northwind Analytics",
      "text": "How we cut dashboard load time in half\nSeptember 12, 2026 · 1,873 views\n© 2026 Northwind Analytics"
    }
  ],
  "discoveryActorId": "apify/website-content-crawler",
  "discoveryInput": {},
  "demoOnly": false
}
```

# Actor output Schema

## `qualified` (type: `string`):

All qualified changes as JSON, best-scoring first.

## `sheet` (type: `string`):

The same changes as a spreadsheet.

## `runSummary` (type: `string`):

JSON record with the funnel (loaded -> filtered -> AI-assessed -> qualified), per-reason skip counts, charged events by type, free-tier changes used and remaining, whether a budget limit was reached, webhook delivery counts and the dedupe store size.

# 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 = {
    "sourceMode": "list",
    "goalDescription": "We sell a B2B analytics tool that competes with Northwind: flag price, plan and launch changes our sales team must know this week.",
    "maxDiscoveryChargeUsd": 0.5,
    "itemsList": [
        {
            "url": "https://northwind-analytics.example/pricing",
            "label": "Northwind Analytics",
            "title": "Pricing — Northwind Analytics",
            "loadedTime": "2026-09-27T08:00:00Z",
            "previousCheckedAt": "2026-09-20T08:00:00Z",
            "previousTitle": "Pricing — Northwind Analytics",
            "previousText": "Simple pricing for growing teams\nStarter | $0 | up to 3 seats | 1 dashboard\nPro | $49 per seat / month | unlimited dashboards | email support\nBusiness | $99 per seat / month | SSO | priority support\nEnterprise | Contact sales\nLast updated 3 days ago",
            "text": "Simple pricing for growing teams\nStarter | $0 | up to 3 seats | 1 dashboard\nPro | $59 per seat / month | unlimited dashboards | email support\nBusiness | $99 per seat / month | SSO | priority support | audit log included\nEnterprise | Contact sales\nLast updated 2 hours ago"
        },
        {
            "url": "https://northwind-analytics.example/changelog",
            "label": "Northwind Analytics",
            "title": "Changelog — Northwind Analytics",
            "loadedTime": "2026-09-27T08:00:00Z",
            "previousCheckedAt": "2026-09-20T08:00:00Z",
            "previousTitle": "Changelog — Northwind Analytics",
            "previousText": "Changelog\nv4.12 — Scheduled exports to Google Sheets\nv4.11 — Faster dashboard loading",
            "text": "Changelog\nv4.13 — Launched: AI anomaly alerts, now available on the Pro and Business plans\nv4.12 — Scheduled exports to Google Sheets\nv4.11 — Faster dashboard loading"
        },
        {
            "url": "https://northwind-analytics.example/careers",
            "label": "Northwind Analytics",
            "title": "Careers — Northwind Analytics",
            "loadedTime": "2026-09-27T08:00:00Z",
            "previousCheckedAt": "2026-09-20T08:00:00Z",
            "previousTitle": "Careers — Northwind Analytics",
            "previousText": "Join Northwind\nOpen roles\nProduct Designer — Remote (EU)\nBackend Engineer — Berlin",
            "text": "Join Northwind\nOpen roles\nSenior Sales Engineer — London (new EMEA team)\nAccount Executive, Enterprise — London\nBackend Engineer — Berlin"
        },
        {
            "url": "https://northwind-analytics.example/blog",
            "label": "Northwind Analytics",
            "title": "Blog — Northwind Analytics",
            "loadedTime": "2026-09-27T08:00:00Z",
            "previousCheckedAt": "2026-09-20T08:00:00Z",
            "previousTitle": "Blog — Northwind Analytics",
            "previousText": "How we cut dashboard load time in half\nSeptember 12, 2026 · 1,204 views\n© 2025 Northwind Analytics",
            "text": "How we cut dashboard load time in half\nSeptember 12, 2026 · 1,873 views\n© 2026 Northwind Analytics"
        }
    ],
    "discoveryInput": {}
};

// Run the Actor and wait for it to finish
const run = await client.actor("rich_minds/website-change-monitor-ai").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 = {
    "sourceMode": "list",
    "goalDescription": "We sell a B2B analytics tool that competes with Northwind: flag price, plan and launch changes our sales team must know this week.",
    "maxDiscoveryChargeUsd": 0.5,
    "itemsList": [
        {
            "url": "https://northwind-analytics.example/pricing",
            "label": "Northwind Analytics",
            "title": "Pricing — Northwind Analytics",
            "loadedTime": "2026-09-27T08:00:00Z",
            "previousCheckedAt": "2026-09-20T08:00:00Z",
            "previousTitle": "Pricing — Northwind Analytics",
            "previousText": """Simple pricing for growing teams
Starter | $0 | up to 3 seats | 1 dashboard
Pro | $49 per seat / month | unlimited dashboards | email support
Business | $99 per seat / month | SSO | priority support
Enterprise | Contact sales
Last updated 3 days ago""",
            "text": """Simple pricing for growing teams
Starter | $0 | up to 3 seats | 1 dashboard
Pro | $59 per seat / month | unlimited dashboards | email support
Business | $99 per seat / month | SSO | priority support | audit log included
Enterprise | Contact sales
Last updated 2 hours ago""",
        },
        {
            "url": "https://northwind-analytics.example/changelog",
            "label": "Northwind Analytics",
            "title": "Changelog — Northwind Analytics",
            "loadedTime": "2026-09-27T08:00:00Z",
            "previousCheckedAt": "2026-09-20T08:00:00Z",
            "previousTitle": "Changelog — Northwind Analytics",
            "previousText": """Changelog
v4.12 — Scheduled exports to Google Sheets
v4.11 — Faster dashboard loading""",
            "text": """Changelog
v4.13 — Launched: AI anomaly alerts, now available on the Pro and Business plans
v4.12 — Scheduled exports to Google Sheets
v4.11 — Faster dashboard loading""",
        },
        {
            "url": "https://northwind-analytics.example/careers",
            "label": "Northwind Analytics",
            "title": "Careers — Northwind Analytics",
            "loadedTime": "2026-09-27T08:00:00Z",
            "previousCheckedAt": "2026-09-20T08:00:00Z",
            "previousTitle": "Careers — Northwind Analytics",
            "previousText": """Join Northwind
Open roles
Product Designer — Remote (EU)
Backend Engineer — Berlin""",
            "text": """Join Northwind
Open roles
Senior Sales Engineer — London (new EMEA team)
Account Executive, Enterprise — London
Backend Engineer — Berlin""",
        },
        {
            "url": "https://northwind-analytics.example/blog",
            "label": "Northwind Analytics",
            "title": "Blog — Northwind Analytics",
            "loadedTime": "2026-09-27T08:00:00Z",
            "previousCheckedAt": "2026-09-20T08:00:00Z",
            "previousTitle": "Blog — Northwind Analytics",
            "previousText": """How we cut dashboard load time in half
September 12, 2026 · 1,204 views
© 2025 Northwind Analytics""",
            "text": """How we cut dashboard load time in half
September 12, 2026 · 1,873 views
© 2026 Northwind Analytics""",
        },
    ],
    "discoveryInput": {},
}

# Run the Actor and wait for it to finish
run = client.actor("rich_minds/website-change-monitor-ai").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 '{
  "sourceMode": "list",
  "goalDescription": "We sell a B2B analytics tool that competes with Northwind: flag price, plan and launch changes our sales team must know this week.",
  "maxDiscoveryChargeUsd": 0.5,
  "itemsList": [
    {
      "url": "https://northwind-analytics.example/pricing",
      "label": "Northwind Analytics",
      "title": "Pricing — Northwind Analytics",
      "loadedTime": "2026-09-27T08:00:00Z",
      "previousCheckedAt": "2026-09-20T08:00:00Z",
      "previousTitle": "Pricing — Northwind Analytics",
      "previousText": "Simple pricing for growing teams\\nStarter | $0 | up to 3 seats | 1 dashboard\\nPro | $49 per seat / month | unlimited dashboards | email support\\nBusiness | $99 per seat / month | SSO | priority support\\nEnterprise | Contact sales\\nLast updated 3 days ago",
      "text": "Simple pricing for growing teams\\nStarter | $0 | up to 3 seats | 1 dashboard\\nPro | $59 per seat / month | unlimited dashboards | email support\\nBusiness | $99 per seat / month | SSO | priority support | audit log included\\nEnterprise | Contact sales\\nLast updated 2 hours ago"
    },
    {
      "url": "https://northwind-analytics.example/changelog",
      "label": "Northwind Analytics",
      "title": "Changelog — Northwind Analytics",
      "loadedTime": "2026-09-27T08:00:00Z",
      "previousCheckedAt": "2026-09-20T08:00:00Z",
      "previousTitle": "Changelog — Northwind Analytics",
      "previousText": "Changelog\\nv4.12 — Scheduled exports to Google Sheets\\nv4.11 — Faster dashboard loading",
      "text": "Changelog\\nv4.13 — Launched: AI anomaly alerts, now available on the Pro and Business plans\\nv4.12 — Scheduled exports to Google Sheets\\nv4.11 — Faster dashboard loading"
    },
    {
      "url": "https://northwind-analytics.example/careers",
      "label": "Northwind Analytics",
      "title": "Careers — Northwind Analytics",
      "loadedTime": "2026-09-27T08:00:00Z",
      "previousCheckedAt": "2026-09-20T08:00:00Z",
      "previousTitle": "Careers — Northwind Analytics",
      "previousText": "Join Northwind\\nOpen roles\\nProduct Designer — Remote (EU)\\nBackend Engineer — Berlin",
      "text": "Join Northwind\\nOpen roles\\nSenior Sales Engineer — London (new EMEA team)\\nAccount Executive, Enterprise — London\\nBackend Engineer — Berlin"
    },
    {
      "url": "https://northwind-analytics.example/blog",
      "label": "Northwind Analytics",
      "title": "Blog — Northwind Analytics",
      "loadedTime": "2026-09-27T08:00:00Z",
      "previousCheckedAt": "2026-09-20T08:00:00Z",
      "previousTitle": "Blog — Northwind Analytics",
      "previousText": "How we cut dashboard load time in half\\nSeptember 12, 2026 · 1,204 views\\n© 2025 Northwind Analytics",
      "text": "How we cut dashboard load time in half\\nSeptember 12, 2026 · 1,873 views\\n© 2026 Northwind Analytics"
    }
  ],
  "discoveryInput": {}
}' |
apify call rich_minds/website-change-monitor-ai --silent --output-dataset

```

## MCP server setup

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

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/cqRijLsJDrtThp3By/builds/612WytXG3QYFagan1/openapi.json
