# Wayback Change Monitor — Website History Diff & Alerts (`foxlabs/wayback-change-monitor`) Actor

See when any URL changed on the Wayback Machine. Dated change timeline per URL: change type (content/removed/restored/redirect/status), byte delta, title and status transitions, meaningful text diff (noise filtered) and archived proof links. Not a raw snapshot dump.

- **URL**: https://apify.com/foxlabs/wayback-change-monitor.md
- **Developed by:** [Berkan Kaplan](https://apify.com/foxlabs) (community)
- **Categories:** Business, Developer tools, SEO tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $4.00 / 1,000 change events

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

## 🕰️ Wayback Change Monitor — Website History Diff & Alerts

See **when any URL changed** on the Internet Archive's Wayback Machine — and **what** changed. Not a raw
snapshot dump: a dated change timeline per URL with classified change types, byte deltas, title and status
transitions, meaningful text diffs (noise filtered), and an archived proof link for every change.

***

### 🎯 What it does

Give it a URL (or a whole domain). For each page it reads the Wayback Machine's capture history, keeps only the
points where the **content actually changed**, and returns one row per change:

- **When** it changed (`changeTimestamp`) and the previous version's timestamp
- **What kind** of change (`changeType`): `content-change`, `removed` (2xx→4xx/5xx), `restored`, `redirect-change`,
  `status-change`, `type-change`, `first-seen`, or `noise`
- **How much** (`byteDelta`), the HTTP `statusCode` and the previous status
- With deep diff on: `titleFrom`→`titleTo`, added/removed text blocks (with samples), and an `isNoise` flag
- An **archived proof URL** (`archivedUrl`) and the raw original body URL (`rawUrl`) for every row

### 💡 Why it's different

The Wayback Machine records thousands of captures, but most "changes" are **noise** — dynamic tokens, timestamps
and whitespace churn that flip the content hash without changing anything a human sees. Raw snapshot scrapers dump
all of them. This actor **normalizes the content and filters noise**, so you get the handful of changes that
actually matter: a price edit, a removed page, a rewritten headline, a page that went dead.

### 🚀 Quick start

```json
{
  "urls": ["stripe.com/pricing", "competitor.com/product"],
  "from": "2023",
  "to": "2024",
  "deepDiff": true,
  "maxChangesPerUrl": 200
}
```

Returns the change timeline for each URL between 2023 and 2024, with text diffs. Leave `from`/`to` empty for the
full history. Use `matchType: "domain"` to monitor every page under a site.

### 🔧 Input

| Field | Type | Default | Description |
|---|---|---|---|
| `urls` | array | — (required) | Pages or domains to check. Bare domains work. |
| `matchType` | string | `exact` | `exact` · `prefix` (path) · `host` · `domain` (+ subdomains) |
| `from` / `to` | string | all history | Date bound: `YYYY`, `YYYYMM` or `YYYYMMDD` |
| `deepDiff` | boolean | `false` | Fetch archived bodies to extract title/text diffs + filter noise |
| `includeNoise` | boolean | `false` | Also emit changes with no real visible-content change |
| `maxChangesPerUrl` | integer | `200` | Cap on change rows per URL (oldest first) |
| `maxDeepDiffsPerUrl` | integer | `500` | Work cap (deep diff on): most versions compared per URL — bounds time/cost on noisy pages |
| `proxyConfiguration` | object | Apify proxy | Rotation recommended (see below) |

### 📤 Output

One dataset row per change event. Key fields: `url`, `changeTimestamp`, `changeType`, `statusCode`, `statusFrom`,
`byteDelta`, `byteSize`, `archivedUrl`, `rawUrl`, `previousTimestamp`, and (deep diff) `titleChanged`,
`titleFrom`, `titleTo`, `addedBlocks`, `removedBlocks`, `addedSample`, `removedSample`, `isNoise`.

### 🧩 Use cases

- **Competitive & sales intelligence** — know the day a competitor changed pricing, product or positioning.
- **SEO & content monitoring** — title/content/status history of any page.
- **Legal, compliance & journalism** — dated, archived proof of when terms/policy pages changed.
- **Due diligence** — how often and how heavily a site changes over time.

### 🤖 AI agents & x402

This actor runs in standard batch mode and is **agent-payable** (x402): an autonomous agent can call it, pay
per event, and receive structured change rows — no login, no API key for the source. A Standby ("… API")
sibling can be published from the same code base for real-time, request/response use (see below).

### 💳 Pricing (pay-per-event)

Proposed events (final price set at publish):

- `url-lookup` — per URL checked
- `change-event` — per delivered change row (the value: change + classification)
- `deep-diff` — per computed content diff (only when deep diff is on)

Noise-only changes are **not emitted and not billed as change events** (so you only pay `change-event` for
real changes). When deep diff is on, each archived version compared is billed as one `deep-diff` — including the
comparisons that turn out to be noise, since that comparison is the work that detected and filtered them.

### 🔌 Integration

- **API / SDK:** start the actor and read its dataset via the Apify API or client SDKs.
- **Scheduling:** schedule a daily run on your watchlist; new rows = new changes since last run.
- **Webhooks:** wire an Apify webhook on run finish to push changes into Slack, a sheet or your CRM.

### 🛡️ Proxy & reliability

Archive.org throttles a single IP under load. The actor defaults to **Apify datacenter proxy** (free on your
plan) with in-run rotation, plus exponential-backoff retries. Residential proxy is **not** required.

### 📚 Data source & ethics

Data comes from the public **Internet Archive Wayback Machine CDX API** (keyless, documented). Only archival
metadata and already-public archived page content are read. No personal data is collected.

### ⚠️ Limits

- History depends on what the Wayback Machine captured — thinly-archived pages yield few change points.
- Deep diff fetches archived bodies, which adds time and cost; cap it with `maxChangesPerUrl`.
- Very large sites (`matchType: domain`) are paginated; use date bounds to keep runs focused.

### 📝 Changelog

See `CHANGELOG.md`.

# Changelog

This Actor's version history is a separate document: https://apify.com/foxlabs/wayback-change-monitor/changelog.md

# Actor input Schema

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

Pages or sites to check for historical changes. A bare domain works; use matchType to widen to a whole site.

## `matchType` (type: `string`):

exact = that URL only; prefix = URLs under the path; host = that host; domain = host + all subdomains.

## `from` (type: `string`):

Earliest capture to consider. Accepts YYYY, YYYYMM or YYYYMMDD. Empty = all history.

## `to` (type: `string`):

Latest capture to consider. Accepts YYYY, YYYYMM or YYYYMMDD. Empty = up to now.

## `deepDiff` (type: `boolean`):

Fetch the archived body at each change point to extract title changes, added/removed text blocks and filter out noise-only changes (dynamic tokens). Adds cost per change.

## `includeNoise` (type: `boolean`):

When deep diff is on, also emit changes whose visible content did not really change (digest churn from dynamic tokens). Off by default.

## `maxChangesPerUrl` (type: `integer`):

Cap on change events returned per URL (oldest first).

## `maxDeepDiffsPerUrl` (type: `integer`):

Work cap when deep diff is on: the most archived versions compared per URL. Protects run time and cost on heavily-captured, noisy pages. Narrow the date range to reach older changes.

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

Archive.org throttles a single IP under load, so proxy rotation is recommended. Apify datacenter proxy is free on your plan and enough here.

## Actor input object example

```json
{
  "urls": [
    "stripe.com/pricing",
    "openai.com/index/"
  ],
  "matchType": "exact",
  "from": "2023",
  "to": "2024",
  "deepDiff": false,
  "includeNoise": false,
  "maxChangesPerUrl": 200,
  "maxDeepDiffsPerUrl": 500,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

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

No description

# API

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

## JavaScript example

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

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

// Prepare Actor input
const input = {
    "urls": [
        "stripe.com/pricing"
    ],
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("foxlabs/wayback-change-monitor").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "urls": ["stripe.com/pricing"],
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("foxlabs/wayback-change-monitor").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "urls": [
    "stripe.com/pricing"
  ],
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call foxlabs/wayback-change-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,foxlabs/wayback-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/oyzXgkpf87invqcAz/builds/GkhacUrcgziSmIHtc/openapi.json
