# Website Change Feed (`sapph1re/website-change-feed`) Actor

Monitor public web pages without mandatory selectors. Get stable snapshots, readable text diffs, and explicit change states without false removals after temporary failures.

- **URL**: https://apify.com/sapph1re/website-change-feed.md
- **Developed by:** [Roman V](https://apify.com/sapph1re) (community)
- **Categories:**
- **Stats:** 3 total users, 2 monthly users, 88.9% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 page checks

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

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

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

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

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

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

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

# README

## Website Change Feed

Turn public web pages into stable snapshots and change states. Use snapshot mode
for normalized current content or changes mode to compare each successful check
with a previous dataset and return `NEW`, `UPDATED`, `UNCHANGED`, or `REMOVED`.

### Why this Actor

- The default path needs only a URL. CSS selectors are optional.
- Stable `canonical_page_id` and `content_hash` values make recurring checks
  deterministic.
- Common navigation, header, footer, consent, popup, script, and style content
  is excluded before hashing by default.
- Duplicate canonical URLs produce one result.
- A temporary fetch failure never becomes a false removal. The prior page state
  is preserved in the next snapshot and the failure stays in `OUTPUT`.
- Confirmed HTTP 404 and 410 responses are completed missing-page checks, so a
  previously existing page becomes `REMOVED`. They are not described as
  successful page fetches.
- Gzip, deflate, and Brotli responses are decoded within the configured content
  limit. Binary-looking bodies mislabeled as text are rejected without billing.

### Input

Add one to 100 public HTTP or HTTPS pages to `urls`. You can attach a label and
an optional simple selector such as `main`, `#pricing`, or `div.article`.

For a recurring change feed, choose `changes` and pass the prior run's
`current_snapshot_dataset_id` as `previousDatasetId`. Each changes run stores a
fresh full snapshot and reports its dataset ID in the `OUTPUT` summary. You can
also pass prior normalized records through `previousPages`, but do not use both
inputs together.

Use `ignoreTextPatterns` for volatile lines such as update timestamps. Patterns
are case-insensitive and intentionally bounded. Browser rendering is not part
of the first version, so pages that require JavaScript may return limited text.

### Output

Every completed check includes its canonical URL, final URL, stable ID, change
state, existence state, HTTP metadata, normalized title and text, content hash,
and warnings. `UPDATED` results also contain bounded added and removed text
lines. `REMOVED` results retain the previous title and text as evidence.

The default key-value-store record `OUTPUT` contains requested, successful,
missing, failed, and deduplicated counts; event totals; explicit errors; and the
next full-snapshot dataset ID.

An unchanged page and a confirmed 404 or 410 missing-page outcome are completed
page checks and are returned as normal billable results. Failed, blocked,
malformed-compression, unsupported-content, and binary-looking checks do not
emit primary dataset results.

### Safety and scope

This Actor reads public HTML, plain-text, and Markdown pages over HTTP or HTTPS.
It rejects local, private,
reserved, and non-public network destinations, including redirects. It caps
redirects, request time, response size, concurrency, text size, and diff size.
It does not log in, bypass access controls, accept cookies, or crawl links from
a seed page.

# Actor input Schema

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

One object per HTTP or HTTPS page. label and selector are optional. Simple selectors such as main, #pricing, or div.article are supported.

## `outputMode` (type: `string`):

snapshot returns normalized current pages. changes compares every successful check with the previous snapshot and reports its state.

## `previousDatasetId` (type: `string`):

Recommended for recurring checks. Select the dataset named by current\_snapshot\_dataset\_id in the preceding run's OUTPUT summary.

## `previousPages` (type: `array`):

Optional prior normalized items supplied inline. Use this or previousDatasetId, not both.

## `ignoreBoilerplate` (type: `boolean`):

Remove scripts, styles, navigation, headers, footers, asides, and common consent or popup containers.

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

Optional case-insensitive regular expressions. A line matching any pattern is removed before hashing.

## `maxConcurrency` (type: `integer`):

Number of public pages checked at once.

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

Maximum time in seconds for each page request.

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

Hard ceiling for both the transferred response body and its decoded gzip, deflate, or Brotli content.

## `includeHtml` (type: `boolean`):

Attach the bounded raw response body. Off by default to keep datasets compact.

## Actor input object example

```json
{
  "urls": [
    {
      "url": "https://example.com/",
      "label": "Example home page"
    }
  ],
  "outputMode": "snapshot",
  "previousPages": [],
  "ignoreBoilerplate": true,
  "ignoreTextPatterns": [],
  "maxConcurrency": 5,
  "timeoutSecs": 20,
  "maxContentBytes": 1000000,
  "includeHtml": false
}
```

# Actor output Schema

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

No description

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

No description

# API

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

## JavaScript example

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

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

// Prepare Actor input
const input = {};

// Run the Actor and wait for it to finish
const run = await client.actor("sapph1re/website-change-feed").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("sapph1re/website-change-feed").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 sapph1re/website-change-feed --silent --output-dataset

```

## MCP server setup

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

```

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/iZQnnV7pDLy6bgoh2/builds/DveJp9CiaZt8lok6p/openapi.json
