# Website Change Monitor: Diff, Price & Stock, $3/1K (`conserving_celerytop/website-change-monitor`) Actor

Website change monitor for public pages. Each URL is one check that returns a text diff, the changed CSS selector, price and stock status. Respects robots.txt. $0.003 per URL. Schedule it for alerts.

- **URL**: https://apify.com/conserving_celerytop/website-change-monitor.md
- **Developed by:** [Don Mangu](https://apify.com/conserving_celerytop) (community)
- **Categories:** Automation, E-commerce
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.10 / 1,000 url 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?

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: Diff, Price & Stock, $3/1K

**Website Change Monitor** checks the public web pages, RSS or Atom feeds and JSON API endpoints you list and tells you what changed since the last check. Each URL is one check at $0.003 ($3 per 1,000 checks) and returns one result row with a text diff, the change inside a CSS selector, the price and the stock status. The Actor reads robots.txt for every host, keeps one baseline per URL in your own Apify account, and works with Apify schedules, webhooks and the API, so a change can start an alert in your own tools. For feeds and JSON endpoints it returns only the new and changed items, and it can send them to a webhook URL.

### Website change monitor sample output

The examples use public GOV.UK pages. This Actor is not affiliated with GOV.UK or with any site you monitor. This row comes from the saved task "Track Price Changes on Any Web Page". It reads the first fee in the GOV.UK passport fees table on the first run, which stores the baseline:

```json
{
  "url": "https://www.gov.uk/passport-fees",
  "status": "baseline",
  "changed": false,
  "changeTypes": [],
  "summary": "First check: baseline stored, nothing to compare yet",
  "httpStatus": 200,
  "title": "Passport fees - GOV.UK",
  "watching": "price-stock",
  "price": 102,
  "priceCents": 10200,
  "currency": "GBP",
  "priceText": "£102",
  "priceSource": "selector",
  "priceTargetMet": false,
  "availability": null,
  "checkCount": 1,
  "error": null
}
```

### How to monitor website changes in 5 steps

1. Open the Actor and add the pages to watch under **Page URLs**. Each URL is one check.
2. Narrow the check if you want. Enter a **CSS selector** to watch one part of a page, a **Price selector** and a **Stock selector** for products, or a **Target price** to get a yes or no when the price falls to it. Product pages with schema.org JSON-LD or price meta tags are read without any selector.
3. Click **Start**. The first run stores a baseline for every URL and returns the status `baseline`.
4. Open **Schedules** and run the Actor daily or hourly with the same input. Every later run compares each URL with its baseline and returns `changed` or `unchanged`.
5. Read the results in the **Check results**, **Text diffs** and **Price and stock** tables, or through the API. The key-value store record `SUMMARY` lists the URLs that changed in the run.

To find a CSS selector, right-click the element in your browser, choose Inspect, right-click the highlighted tag and choose Copy selector.

### What the website change monitor returns

One row per URL. These are the main fields:

| Field | Meaning |
|---|---|
| `status` | `baseline`, `changed`, `unchanged`, `unavailable`, `robots-disallowed`, `blocked`, `error`, `invalid-url` or `skipped-host-blocked`. A webhook status row uses `webhook-sent`, `webhook-failed` or `webhook-refused` |
| `sourceType`, `items`, `newCount`, `changedCount`, `itemsSeen` | Feed and JSON sources: the type, the new and changed items, their counts and the number of items in the response |
| `changed`, `changeTypes`, `summary` | Whether something changed, what kind (`text`, `selector`, `file`, `price`, `availability`, `page-removed`, `page-restored`, and for feeds and JSON `new-items`, `changed-items`, `document`) and one sentence about it |
| `diff`, `diffText` | The changed lines as `replace`, `add` and `remove` entries, and the same as text with `-` and `+` lines |
| `addedLineCount`, `removedLineCount` | Size of the text change |
| `price`, `priceCents`, `currency`, `previousPrice`, `priceChangePercent` | Current price, price in hundredths of the currency unit, and the change since the last check |
| `priceTargetMet` | True when the price is at or below your target price |
| `availability`, `previousAvailability` | `in_stock`, `out_of_stock`, `preorder`, `backorder` or `unknown` |
| `checkedAt`, `previousCheckedAt`, `checkCount` | When this URL was checked now and before |
| `notes`, `error` | Hints such as a selector that matched nothing, and the reason when a check could not finish |

If a page returns 404 or 410 after a successful check, the row has `changed` true and the change type `page-removed`. When the page comes back, the change type is `page-restored`. PDFs and other files are compared by a hash of their bytes.

### Price and stock alert with the website change monitor

Give the Actor a product page and it looks for the price in this order: your **Price selector**, schema.org JSON-LD offers, then price meta tags such as `product:price:amount`. Stock comes from your **Stock selector**, the JSON-LD `availability` value or the availability meta tag. Prices such as `£94.50`, `1.299,00 €` and `$1,299.00` are read into a number, a currency and cents.

When a price or stock selector is set and no CSS selector is set, only the price and stock fields are compared, so banners and counters on the page do not create changes. To get an alert, schedule the Actor, add an Apify webhook to the run and act on the rows where `changed` is true, or where `priceTargetMet` is true.

### Monitor an RSS feed or a JSON API

Set **Source type** to choose what the URLs are. The default is `page`, which works as described above. All URLs of one run use the same type.

| Source type | What it compares | What a changed row holds |
|---|---|---|
| `page` | Page text, a selector, price and stock | `diff`, `diffText`, price and stock fields |
| `feed` | Items of an RSS 2.0, RSS 1.0 or Atom feed. An item is new when its `guid` (or Atom `id`, or link) was not seen before, and changed when its title, link, text or date changed | `items`: only the new and changed items |
| `json` with **ID field** | Records of a JSON response. A record is new when its ID was not seen before, and changed when any field of the record changed | `items`: only the new and changed records |
| `json` without ID field | The whole JSON document. Key order is ignored | `diff` and `diffText` of the pretty printed document |

The first run stores a baseline and returns no items. Every later run returns only what is new or changed since the last run. Items that leave the feed or the response are not reported. The Actor remembers up to 5,000 item IDs per source, so an item that comes back is not reported as new again.

**Feed example.** The input below uses the Federal Register RSS feed. The first run on 13 October 2026 returned one row with the status `baseline` and the summary "First check: baseline stored (200 items), nothing to compare yet". The second run returned `unchanged` with "No new or changed items (200 items checked)".

```json
{
  "startUrls": [{ "url": "https://www.federalregister.gov/api/v1/documents.rss?conditions%5Btype%5D%5B%5D=RULE" }],
  "sourceType": "feed",
  "monitorName": "federal-register-rules"
}
```

**JSON example.** The openFDA drug enforcement endpoint lists recalls. Each recall has a `recall_number`:

```json
{
  "startUrls": [{ "url": "https://api.fda.gov/drug/enforcement.json?limit=5&sort=report_date:desc" }],
  "sourceType": "json",
  "idField": "recall_number",
  "itemsPath": "results",
  "monitorName": "fda-recalls"
}
```

On 13 October 2026 the first run stored 4 records ("First check: baseline stored (4 items)"). The endpoint returned 5 records, and one had an empty `recall_number`, so it was skipped and the row notes say so. The second run returned `unchanged`. `itemsPath` is optional. Without it the Actor takes a top level list, or a field named `results`, `items`, `data` or `records`.

**Changed row.** This row comes from a test run against a small local test server that serves a feed. The feed grew from 3 to 5 items and the first item was edited:

```json
{
  "status": "changed",
  "changed": true,
  "changeTypes": ["new-items", "changed-items"],
  "summary": "2 new items, 1 changed item of 5 in the feed.",
  "newCount": 2,
  "changedCount": 1,
  "itemsSeen": 5,
  "items": [
    { "change": "changed", "id": "g1", "title": "Post 1 (edited)", "link": "http://x.test/1", "published": "Mon, 01 Oct 2026 10:00:00 GMT", "summary": "Body 1" },
    { "change": "new", "id": "g4", "title": "Post 4", "link": "http://x.test/4", "published": "Mon, 04 Oct 2026 10:00:00 GMT", "summary": "Body 4" },
    { "change": "new", "id": "g5", "title": "Post 5", "link": "http://x.test/5", "published": "Mon, 05 Oct 2026 10:00:00 GMT", "summary": "Body 5" }
  ]
}
```

For `json` the entries in `items` hold the full `record` instead of title and link. **Maximum diff lines** limits how many items one row lists, and `itemsTruncated` is true when more changed. `newCount` and `changedCount` always show the full numbers.

### Send changes to a webhook

Enter a **Webhook URL** and the Actor sends one `POST` with a JSON body at the end of the run. The body holds only the sources that changed. When nothing changed, nothing is sent.

```json
{
  "monitorName": "fda-recalls",
  "runId": "abc123",
  "finishedAt": "2026-10-13T17:25:54.296Z",
  "changedSources": 1,
  "truncated": false,
  "changes": [
    {
      "url": "https://api.fda.gov/drug/enforcement.json?limit=5&sort=report_date:desc",
      "sourceType": "json",
      "summary": "2 new items, 1 changed item of 4 in the response.",
      "changeTypes": ["new-items", "changed-items"],
      "newCount": 2,
      "changedCount": 1,
      "itemsTruncated": false,
      "items": [{ "change": "new", "id": "R-3", "record": { "rid": "R-3", "status": "Ongoing", "name": "Rec 3" } }]
    }
  ]
}
```

The example body shows the structure. The `items` entry was captured from a local test run, and the other values are shortened. A `page` source sends `diffText`, and the price and stock fields when they changed, in place of `items`.

- Only `https` URLs are accepted. URLs with a user name or password, and URLs that point to private or internal addresses, are refused. The address is checked again when the connection opens.
- Redirects are not followed. A `2xx` answer counts as delivered. After a network error or a `5xx` answer the Actor tries once more after two seconds.
- A bad or failing webhook does not fail the run. The results get one extra row with the status `webhook-sent`, `webhook-failed` or `webhook-refused`, the reason, and the host of the URL. The path of the URL is not stored, because it can hold a secret. The same information is in `SUMMARY` under `webhook`.
- The body is cut to about 1 MB. Then `truncated` is true.

### Website change monitor pricing

The Actor uses pay per event. One event is charged for each URL after its result row is saved:

| Event | Price (no discount) | Bronze | Silver | Gold |
|---|---|---|---|---|
| `url-checked` | $0.003 | $0.0027 | $0.0024 | $0.0021 |
| Actor start (every run) | $0.00005 | | | |

That is $3.00 per 1,000 checks without a discount and $2.10 per 1,000 at Gold.

- 1 URL equals 1 charged check. The first check, an unchanged page, a changed page and a page that answers 404, 403 or an error are all one check.
- A URL that robots.txt does not allow is charged as one check, because the Actor requested robots.txt from that host. No page request is sent for it.
- Feeds and JSON endpoints use the same `url-checked` event, charged once per source. The first check and a check that finds new or changed items are charged. A check with no change is free and returns a status row. A feed or JSON check that ends in an error, a block or a robots.txt refusal is also free. A webhook is not charged.
- URLs that are not valid web addresses, URLs that point to private network addresses and URLs above your **Maximum URLs** limit are not charged. After a host refuses three requests in a run, its remaining URLs are skipped and not charged.
- The Actor stops when the maximum charge you set for the run is reached.

**Worked example.** You watch 50 product pages once a day for 30 days. That is 30 runs with 50 checks each, 1,500 checks, which cost 1,500 x $0.003 = $4.50, plus 30 run starts x $0.00005 = $0.0015, so $4.5015 in a month at the no discount price.

### Input example

```json
{
  "startUrls": [
    { "url": "https://www.gov.uk/passport-fees" },
    { "url": "https://www.gov.uk/bank-holidays.json" }
  ],
  "priceSelector": "main table tbody tr:first-child td:first-of-type",
  "priceBelow": 100,
  "monitorName": "price-watch",
  "ignoreSelectors": [".cookie-banner"],
  "ignorePatterns": ["Updated on .*"],
  "maxDiffLines": 50,
  "maxUrls": 100
}
```

The fields are: `startUrls` (Page URLs), `sourceType`, `idField`, `itemsPath`, `webhookUrl`, `selector`, `priceSelector`, `stockSelector`, `priceBelow` (Target price), `monitorName`, `ignoreSelectors` (Ignored elements), `ignorePatterns` (Ignored text patterns), `maxDiffLines` (Maximum diff lines), `maxUrls` (Maximum URLs) and `resetBaseline`. Use a different **Monitor name** for each watchlist. Runs with the same name share baselines, runs with different names keep them apart.

### Output example for a changed page

This is an example of a row after a price change on a product page. The values are illustrative:

```json
{
  "url": "https://shop.example/widget",
  "status": "changed",
  "changed": true,
  "changeTypes": ["price", "availability"],
  "summary": "Price changed from 49.90 EUR to 39.90 EUR (-20.04%). Availability changed from in_stock to out_of_stock.",
  "price": 39.9,
  "priceCents": 3990,
  "currency": "EUR",
  "previousPrice": 49.9,
  "priceChanged": true,
  "priceChangePercent": -20.04,
  "priceTargetMet": true,
  "availability": "out_of_stock",
  "previousAvailability": "in_stock",
  "availabilityChanged": true,
  "diff": [],
  "checkedAt": "2026-10-05T08:00:00.000Z",
  "previousCheckedAt": "2026-10-04T08:00:00.000Z"
}
```

A text change returns `diff` entries such as `{ "op": "replace", "from": "Opening hours: 9 to 5", "to": "Opening hours: 10 to 4" }`, and `diffText` holds the same change as `- Opening hours: 9 to 5` and `+ Opening hours: 10 to 4`.

### Run it every day

Use this to get an alert when a page, a price or a stock status changes.

1. Put your page URLs in the list.
2. Give the watchlist a **Monitor name** (`monitorName`), for example `price-watch`. Use one name for each watchlist.
3. Save the input as a task and add a daily schedule. Use an hourly schedule for fast-moving pages.
4. Add an Apify webhook to the run to start your alert.

The first run saves a baseline for each URL. Each later run compares every URL with its baseline and returns `changed` or `unchanged`. Example: 20 URLs checked once a day cost 20 x $0.003 + $0.00005 = $0.06005 a day, so 30 daily runs cost $1.8015.

### Related Actors

- [Web Page to Markdown](https://apify.com/conserving_celerytop/web-page-to-markdown) turns a page into clean Markdown text.
- [Sitemap Extractor](https://apify.com/conserving_celerytop/sitemap-url-extractor) lists all URLs of a site, ready to paste into Page URLs.
- [RSS Feed Reader](https://apify.com/conserving_celerytop/rss-feed-reader) reads all items of a feed on every run, without a baseline.
- [Broken Link Checker](https://apify.com/conserving_celerytop/broken-link-checker) finds pages that return 404.
- [Website Screenshot API](https://apify.com/conserving_celerytop/website-screenshot-api) saves a picture of a page.

These Actors are built by the same author. Each one does one step, so you can pick the step you need:

| What you need | Actor |
| --- | --- |
| Named fields as checked JSON (prices, names, dates) from any page | [AI Web Scraper](https://apify.com/conserving_celerytop/ai-web-scraper) |
| Text and tables from PDF files | [PDF Text Extractor](https://apify.com/conserving_celerytop/pdf-text-extractor) |
| Word, Excel and PowerPoint files as Markdown | [Document to Markdown API](https://apify.com/conserving_celerytop/document-to-markdown-api) |
| Official public data: Federal Register, FDA recalls, Treasury, ECB rates, SEC filings | [Federal Register API](https://apify.com/conserving_celerytop/federal-register-api), [FDA Recalls](https://apify.com/conserving_celerytop/openfda-scraper), [Treasury Fiscal Data API](https://apify.com/conserving_celerytop/treasury-fiscal-data-api), [Exchange Rates API](https://apify.com/conserving_celerytop/exchange-rates-api), [SEC EDGAR Filings API](https://apify.com/conserving_celerytop/sec-edgar-filings-api) |

### FAQ

#### Is it legal to monitor a website for changes?

The Actor requests only public pages that you list. It sends no login, no cookies and no form data, does not solve CAPTCHAs and does not change IP addresses to avoid limits. It reads robots.txt for every host, refuses paths that robots.txt disallows for its user agent `WebsiteChangeMonitor`, and waits at least one second between two requests to the same host, longer when robots.txt sets a Crawl-delay. You are responsible for following the terms of the sites you monitor and the laws that apply to you. For example, the GOV.UK reuse page says: "You can scrape GOV.UK as long as you respect our robots.txt policy." Check the terms of each site before you add it, and do not monitor pages that contain personal data.

#### Which pages does it work on?

Pages whose content is in the HTML the server sends. Pages that build their content with JavaScript after loading show little or no text, and the row notes that. Pages that need a login, show a CAPTCHA or return an access challenge are reported as `blocked`, and the Actor stops requesting a host after three refused requests in one run.

#### What are the limits?

Up to 500 URLs per run, a response of up to 3 MB and about 100,000 page elements, 10 seconds for a page to answer (one retry) and 24 seconds in total per URL, up to 5 redirects, and 4 URLs in parallel across different hosts. The baseline of a URL keeps up to 400,000 characters of text. Page text is compared line by line.

#### Where are the baselines stored?

In a named key-value store in your Apify account called `website-change-monitor-` followed by your **Monitor name**. Delete that store to forget all baselines of a watchlist, or select **Reset baseline** to store the current state again. Do not start two runs with the same Monitor name at the same time.

#### Why does a page show a change on every run?

Something on the page changes on every visit, for example a clock, a visitor counter or an advert. Add its CSS selector to **Ignored elements**, add a regular expression to **Ignored text patterns**, or set a **CSS selector** to watch only the part you care about. A change of settings stores a new baseline and returns `baseline` once.

#### Why is a row `blocked`, `robots-disallowed` or `error`?

`blocked` means the site answered 401, 403, 429, 503 or a challenge page. The Actor retries 429 and 503 once, then reports it and does not try to get around it. `robots-disallowed` means robots.txt does not allow the path, or robots.txt could not be read. `error` means a network error, a timeout, a server error, a page above the size limit or a page that is too complex to read. The `error` field gives the reason.

#### How do I get notified?

Enter a **Webhook URL** to get one POST with only the changes after each run. You can also add an Apify webhook on the run and read the dataset rows where `changed` is true. The `SUMMARY` record lists the changed URLs of the run with their summary sentences.

#### Does a feed or JSON check respect robots.txt?

Yes. Feed and JSON URLs go through the same fetcher as pages, with the same robots.txt check, the same delay between requests to one host and the same size limit of 3 MB. A site that refuses the request, such as a feed behind a block, returns the status `blocked` and is not charged.

Made by Don Mangu. If this saved you time, a rating helps others find it.

# Actor input Schema

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

Add the public URLs to check: web pages, RSS or Atom feeds, or JSON API endpoints, depending on Source type. Each URL is one source. The first check stores a baseline, later checks return what changed. Up to 500 URLs per run.

## `sourceType` (type: `string`):

Choose what the URLs are. Page compares page text, a selector, price and stock. Feed returns only the new or changed items of an RSS or Atom feed. JSON returns only the new or changed records of a JSON response. All URLs of one run use the same type.

## `idField` (type: `string`):

For Source type JSON: the field that identifies one record, for example recall_number or id. A dot path such as meta.id also works. Leave it empty to compare the whole document instead and get a line diff.

## `itemsPath` (type: `string`):

For Source type JSON with an ID field: the path to the list of records, for example results or data.items. Leave it empty to find the list automatically (a top-level list, or a field named results, items, data or records).

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

Optional. After the run, the Actor sends one POST with a JSON body that holds only the changes. Nothing is sent when nothing changed. Only https URLs are accepted, private and internal addresses are refused. A failed webhook does not fail the run and is listed as a status row in the results.

## `selector` (type: `string`):

Enter a CSS selector to watch one part of the page, for example main h1 or #offers. The diff then covers only that part, so changes elsewhere on the page are ignored.

## `priceSelector` (type: `string`):

Enter the CSS selector of the element that shows the price, for example .product-price. Pages with product data in JSON-LD or meta tags are read automatically. Prices are returned as a number, a currency and cents.

## `stockSelector` (type: `string`):

Enter the CSS selector of the element that shows the stock status, for example .availability. The text is returned as in_stock, out_of_stock, preorder or backorder.

## `priceBelow` (type: `number`):

Enter the price you want to be told about. The result row shows priceTargetMet as true when the current price is at or below this number.

## `monitorName` (type: `string`):

Name the watchlist that keeps your baselines. Use letters, numbers and hyphens, for example price-watch. Runs with the same name compare against each other, runs with different names keep separate baselines.

## `ignoreSelectors` (type: `array`):

Add CSS selectors of page parts to remove before comparing, for example .cookie-banner or #live-clock. Use this for banners, counters and ads that change on every visit.

## `ignorePatterns` (type: `array`):

Add regular expressions for text to remove from every line before comparing, for example Updated on .\* or \d+ visitors. Matching ignores letter case.

## `maxDiffLines` (type: `integer`):

Set how many changed lines (pages and whole JSON documents) or changed items (feeds and JSON records) one result row lists. The counts always show the full size of the change.

## `maxUrls` (type: `integer`):

Set how many URLs one run checks. Extra URLs are skipped and cost nothing.

## `resetBaseline` (type: `boolean`):

Select this to store the current state of every URL as the new baseline. The run returns the status baseline for each URL instead of a diff.

## Actor input object example

```json
{
  "startUrls": [
    {
      "url": "https://www.gov.uk/passport-fees"
    }
  ],
  "sourceType": "feed",
  "idField": "recall_number",
  "itemsPath": "results",
  "webhookUrl": "https://hooks.example.com/changes",
  "selector": "main table",
  "priceSelector": ".product-price",
  "stockSelector": ".availability",
  "priceBelow": 100,
  "monitorName": "price-watch",
  "ignoreSelectors": [
    ".cookie-banner",
    "#live-clock"
  ],
  "ignorePatterns": [
    "Updated on .*",
    "\\d+ visitors"
  ],
  "maxDiffLines": 50,
  "maxUrls": 100,
  "resetBaseline": false
}
```

# Actor output Schema

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

No description

## `diffs` (type: `string`):

No description

## `priceStock` (type: `string`):

No description

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

No description

## `stats` (type: `string`):

No description

## `items` (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 = {
    "startUrls": [
        {
            "url": "https://www.gov.uk/passport-fees"
        },
        {
            "url": "https://www.gov.uk/bank-holidays.json"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("conserving_celerytop/website-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 = { "startUrls": [
        { "url": "https://www.gov.uk/passport-fees" },
        { "url": "https://www.gov.uk/bank-holidays.json" },
    ] }

# Run the Actor and wait for it to finish
run = client.actor("conserving_celerytop/website-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 '{
  "startUrls": [
    {
      "url": "https://www.gov.uk/passport-fees"
    },
    {
      "url": "https://www.gov.uk/bank-holidays.json"
    }
  ]
}' |
apify call conserving_celerytop/website-change-monitor --silent --output-dataset

```

## MCP server setup

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