# Pricing Page Monitor - Competitor Price Alerts (`data_foundry/pricing-page-monitor`) Actor

Competitor pricing page scraper and price monitor. Extract every price point from pricing pages, diff them against the last run, and get a webhook when a price changes. No browser, no proxies.

- **URL**: https://apify.com/data_foundry/pricing-page-monitor.md
- **Developed by:** [Data Foundry](https://apify.com/data_foundry) (community)
- **Categories:** E-commerce, Marketing, Automation
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$2.00 / 1,000 url scanneds

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

## Pricing Page Monitor

Track competitor pricing pages and get told the moment a price changes. No browser, no screenshots, no diffing by eye - a clean price set per page, compared against the previous run, delivered to your webhook.

<table>
<tr>
<th>What you get</th>
<th>Built for</th>
</tr>
<tr>
<td valign="top">

💰 **Every price point** on the page, normalised to numbers <br>
📊 **Price-set diff** - what was added, what was removed <br>
💶 **Currency detection** per page (USD, EUR, GBP) <br>
🧩 **SPA-aware** - JSON-LD, microdata, `data-*`, `__NEXT_DATA__` <br>
🔔 **Webhook on change** with HMAC signing <br>
🧠 **Copy changes separated** from price changes

</td>
<td valign="top">

📈 **Competitive intel** for product marketing <br>
🧾 **Price-change audits** for sales teams <br>
🛍️ **MAP and reseller checks** for ecommerce <br>
🤖 **LLM-ready output** - JSON, one record per page <br>
🔗 **API, schedules, integrations** via Apify <br>
🧪 **Free test runs** on the Free plan

</td>
</tr>
</table>

### What data do I get?

One dataset record per URL per run. Prices are numbers, not strings, so you can chart them or alert on thresholds.

- **`prices`** - the set of price points found, rounded and deduplicated: `[0, 8, 10, 20]`
- **`newPrices` / `removedPrices`** - the exact difference against the previous run
- **`pricesChanged`** - true only when money moved
- **`pageChanged`** - true when the page text changed but the prices did not (A/B tested marketing copy)
- **`priceStrings`** - the raw strings as rendered: `["$0", "$10", "$20", "$8"]`
- **`currency`** - detected from JSON-LD, meta tags or the symbols on the page
- **`suspicious`** - a page that had prices and now has none is flagged, not reported as "all prices removed"

### How much does it cost?

Pay per event: **$2.00 per 1,000 URLs scanned** ($0.002 each). You are not charged for the Apify platform usage, only for pages actually fetched.

| You monitor | Runs | Monthly cost |
|---|---|---|
| 10 competitors, daily | 300 | **$0.60** |
| 50 competitors, daily | 1,500 | **$3.00** |
| 200 competitors, daily | 6,000 | **$12.00** |

Pages that fail to load are **not charged**.

### How do I use it?

1. [Create](https://console.apify.com/sign-up) a free Apify account.
2. Open **Competitor Pricing Page Monitor** in Apify Console.
3. Paste one or more pricing page URLs under **URLs**.
4. Optionally add a `webhookUrl` so you hear about changes instead of polling.
5. Click **Start**, then run it again later - the second run is where the diff comes from.
6. Download results as JSON, CSV, Excel or XML, or pull them via the API.

Schedule it daily and the webhook fires only when something actually changed.

### Input

| Field | Type | Description |
|---|---|---|
| `urls` | array | Public pricing page URLs. One record per URL per run. |
| `webhookUrl` | string | Optional https endpoint. Called only when prices changed. |
| `webhookSecret` | string | Optional. Adds `X-Signature-256: sha256=...` over the raw body. |
| `alertOnPageChange` | boolean | Also alert on copy/design changes, not just prices. Off by default. |
| `proxyConfiguration` | object | Optional. Set it if a target site blocks datacenter IPs. |
| `concurrency` | integer | Pages fetched in parallel (default 4). |

```json
{
  "urls": [
    "https://www.notion.com/pricing",
    "https://linear.app/pricing"
  ],
  "webhookUrl": "https://hooks.slack.com/services/...",
  "concurrency": 4
}
```

### Output

```json
{
  "url": "https://www.notion.com/pricing",
  "title": "Notion Pricing Plans: Free, Plus, Business, & Enterprise.",
  "prices": [0, 8, 10, 20],
  "priceStrings": ["$0", "$10", "$20", "$8"],
  "currency": "USD",
  "pageHash": "29ecc593675d46f60bee6ab1e7f010f19b45702ab8996fe49a0512468a15d9a3",
  "changed": false,
  "pricesChanged": false,
  "pageChanged": false,
  "newPrices": [],
  "removedPrices": [],
  "suspicious": false,
  "scrapedAt": "2026-11-06T09:29:58+00:00"
}
```

The `PRICING_SIGNALS` key-value record carries a per-URL summary plus totals, so a dashboard can read one object instead of scanning the dataset.

### Notes and exceptions

- **Two runs are needed for a diff.** The first run is a baseline; `changed` only becomes meaningful afterwards.
- **Prices injected by JavaScript at runtime are not seen.** JSON-LD, microdata, `data-*` attributes and embedded state blobs (`__NEXT_DATA__`, `window.__INITIAL_STATE__`) are parsed, which covers most React/Next/Vue pricing pages, but a price that only exists after a client-side interaction (a monthly/annual toggle) is not captured.
- **Prices are a set, not a plan table.** Two plans at $10 yield one `10`. If you need plan names attached, that is not in this version.
- **Currency support**: USD, EUR and GBP symbols plus ISO codes in JSON-LD. Other symbols are ignored rather than guessed.
- **Pages behind a login, a CAPTCHA or a paywall are out of scope.** The actor only reads public pages.

### What can I do with the data?

**Competitive pricing intelligence.** Track the five competitors you lose deals to. When one drops a tier price, you know the same day - before your reps hear it from a prospect.

**Price-change audit trail.** Keep every dataset run. Six months of `prices` plus `scrapedAt` is a defensible record of what a supplier charged and when.

**Reseller and MAP monitoring.** Point it at distributor pages and catch a listed price below the floor.

### FAQ

**Do I need a browser or proxies?** No. The actor reads the HTML and its embedded data directly. A proxy is optional and only helps when a site blocks datacenter IPs.

**What if the page is behind Cloudflare?** It will be reported as a failed URL and not charged. Bot-protected pages are out of scope by design.

**Can I track a price that only appears behind a login?** No.

**How do I get alerted?** Set `webhookUrl`. Delivery retries on 5xx and 429 and never fails the run. Only https endpoints are accepted, and private/internal addresses are refused.

**How do I run it daily?** Create a schedule in Apify Console, or use the API. See the API tab.

### Related Actors

- **[Review Scraper](https://apify.com/data_foundry/review-scraper)** - reviews across 11 sources with a new-since-last-run diff.
- **[Policy & Terms Change Monitor](https://apify.com/data_foundry/policy-change-monitor)** - terms, privacy and DPA pages, HTML or PDF.
- **[App Store Rank Tracker](https://apify.com/data_foundry/aso-app-rank-tracker)** - chart rank, rating and version changes for iOS apps.

### Changelog

- **0.6** - AggregateOffer price ranges; European `29,99 EUR` format.
- **0.5** - SPA payload extraction (`__NEXT_DATA__`, Redux state, `data-*`).
- **0.4** - `pricesChanged` separated from `pageChanged`, `alertOnPageChange`.
- **0.1** - First release.

# Actor input Schema

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

Public pricing pages to monitor, one URL per row. Use the exact page that lists the plans, e.g. https://www.notion.com/pricing. Duplicates are removed; http and https are both accepted.

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

Leave empty by default - public pricing pages are normally reachable directly. Enable a residential proxy if a target blocks datacenter IPs; it then applies to every URL in the run.

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

If set, a JSON digest is POSTed when at least one pricing page changed since the previous run. Must be https; private, loopback and internal addresses are refused.

## `webhookSecret` (type: `string`):

When set, every request carries X-Signature-256: sha256=HMAC(secret, raw body), so your endpoint can verify the payload really came from this run.

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

How many pricing pages to fetch at once.

## `alertOnPageChange` (type: `boolean`):

By default only price changes trigger the webhook, because marketing copy is A/B tested and would fire constantly. Turn this on to alert on any page change, including copy.

## Actor input object example

```json
{
  "urls": [
    "https://www.notion.com/pricing"
  ],
  "concurrency": 4,
  "alertOnPageChange": false
}
```

# Actor output Schema

## `prices` (type: `string`):

One record per URL: title, sorted price points, raw price strings, currency, page hash, changed flag, new and removed prices, and the scrape timestamp.

## `signals` (type: `string`):

Per URL: changed, newPrices, removedPrices, isFirstRun and suspicious, plus totals for the run.

# 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": [
        "https://www.notion.com/pricing"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("data_foundry/pricing-page-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": ["https://www.notion.com/pricing"] }

# Run the Actor and wait for it to finish
run = client.actor("data_foundry/pricing-page-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": [
    "https://www.notion.com/pricing"
  ]
}' |
apify call data_foundry/pricing-page-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,data_foundry/pricing-page-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/hJiNv39WOpM2Fdo4L/builds/RawxD8Fvou5bU0CLa/openapi.json
