# SaaS Pricing Watcher (`vkuprin/saas-pricing-watcher`) Actor

Track 10-30 competitor SaaS pricing pages in one batch run — Stripe, Notion, Linear, Vercel, OpenAI, Anthropic, or any URL in any language. Extract plans + features + billing cycles. Returns change-detection filters for ongoing monitoring. No API key needed. MCP-ready.

- **URL**: https://apify.com/vkuprin/saas-pricing-watcher.md
- **Developed by:** [Vitaly Kuprin](https://apify.com/vkuprin) (community)
- **Categories:** Marketing, AI, Business
- **Stats:** 2 total users, 0 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per event

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/platform/actors/running/actors-in-store#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

## SaaS Pricing Watcher

**Monitor competitor SaaS pricing pages — Stripe, Notion, Linear, Vercel, Cloudflare, OpenAI, Anthropic, or any other SaaS pricing page in any language.** Extract plans, prices, features, billing cycles **plus the CSS selectors + regex filters to wire ongoing change detection** into your stack.

- ✅ **Batch mode** — track 10-30 competitor pricing pages in a single run via the `urls` array input. Each URL gets its own dataset row.
- ✅ **Pricing + change-detection filters in one call.** Most scrapers give you data. This Actor returns data *and* the filters to monitor the page going forward — feed them into changedetection.io, n8n, Zapier, your own code, or any MCP-compatible AI agent.
- ✅ **No OpenAI/Anthropic API key needed.** AI cost is included in the per-call event price.
- ✅ **Works on any SaaS pricing page in any language** (English, Russian, Spanish, Chinese, Arabic, …). Emits feature strings in the page's native language.
- ✅ **MCP-ready** for Claude Desktop, Cursor, Codex, Gemini Code Assist, Cline, Continue, and every MCP-compatible client.
- ✅ **Typical cost: ~$0.075/URL.** Tracking 20 competitors in one batch run ≈ $1.50.

### Why this exists

Tracking competitor pricing is a recurring product-management headache. Existing tools either (a) give you a CSV export once and leave you to glue together your own monitoring, or (b) cost $200+/mo for a managed dashboard you outgrow quickly. This Actor is the cheaper, agentic version: ~$0.075/run, drop it into any workflow that knows how to call an Apify Actor.

The differentiator over generic pricing scrapers is the `trackingFilters` object — CSS selectors + regex patterns that tell a change-detection system exactly what to monitor on the page. Hand them to changedetection.io, set up an n8n flow, or call the Actor again next week — your choice.

### What it does

1. **Fetches the pricing page** (Playwright by default for modern React SPAs; HTTP mode available for server-rendered pages at lower cost).
2. **JSON-LD short-circuit** — if the page exposes `Product.offers[]` schema.org data (rare on modern SaaS sites, common on older marketplaces), parses it deterministically. Zero LLM cost on this path.
3. **LLM extraction (default path)** — uses Defuddle to extract clean content, then `gpt-4.1-mini` populates a strict zod schema with each tier's name, monthly/annual prices, currency, features list, CTA, and "Most popular" flag.
4. **Tracking filters (the wedge)** — runs the same smart-config engine that powers production change-detection services. Returns `includeFilters` (where pricing lives), `subtractiveSelectors` (ads/cookies/nav to strip), `triggerText` (regex for price-change patterns), and `ignoreText` (regex for noise like view counters).

### Inputs

| Field | Type | Required | Description |
|---|---|:-:|---|
| `urls` | string\[] | one of `urls`/`url` | **Preferred for batch monitoring.** Each URL produces its own dataset row. |
| `url` | string | one of `urls`/`url` | Single-URL convenience field. Ignored when `urls` is set. |
| `currencyHint` | string | no (default `USD`) | ISO 4217 code — USD, EUR, GBP, BRL, etc. |
| `fetchMode` | `http` | `playwright` | no (default `playwright`) | `http` is cheaper but doesn't render JS |
| `extractTrackingFilters` | bool | no (default `true`) | Return CSS/regex filters for ongoing change detection |
| `screenshot` | bool | no (default `false`) | Save a full-page PNG (Playwright mode only) |
| `useEasyList` | bool | no (default `true`) | Strip ads + cookie banners before analysis |
| `userAgent` | string | no | Override the default User-Agent |

### Output

One row per analyzed page:

```jsonc
{
  "url": "https://stripe.com/pricing",
  "fetchedAt": "2026-05-11T00:00:00.000Z",
  "fetchMode": "playwright",
  "source": "llm",
  "plans": [
    {
      "name": "Standard",
      "monthly_price": null,
      "annual_price": null,
      "currency": "USD",
      "billing_period": "usage_based",
      "features": ["Pay-as-you-go 2.9% + $0.30 per transaction", "Online + in-person", "24/7 support"],
      "cta_text": "Start now",
      "is_highlighted": false
    },
    {
      "name": "Custom",
      "monthly_price": null,
      "annual_price": null,
      "currency": "USD",
      "billing_period": null,
      "features": ["Volume discounts", "Country-specific rates", "Multi-product discounts"],
      "cta_text": "Contact sales",
      "is_highlighted": false
    }
  ],
  "hasFreeTier": false,
  "hasUsageBased": true,
  "explanation": "Stripe presents one usage-based standard tier plus a custom enterprise tier.",
  "trackingFilters": {
    "includeFilters": ["main"],
    "subtractiveSelectors": [".cookie-banner", "nav"],
    "triggerText": ["/(\\$|€|£)\\d+(\\.\\d+)?\\/(mo|month|yr|year)/i"],
    "ignoreText": ["/\\d+\\s*(customers|companies)/i", "/©\\s*20\\d{2}/"]
  },
  "screenshotKey": null,
  "aiTokens": { "input": 1840, "output": 380 }
}
```

The `trackingFilters` object plugs directly into:

- **[changedetection.io](https://github.com/dgtlmoon/changedetection.io)** — paste `includeFilters` + `subtractiveSelectors` into the watch's filter fields.
- **n8n / Zapier / Make.com** — pass the regex patterns to your scheduled scraper step.
- **Your own code** — match `triggerText` regex against future fetches to detect pricing changes.
- **Any MCP-compatible AI agent** — agents can read `trackingFilters` and set up monitoring autonomously.

### Pricing

Pay-per-event. Configured in the Apify Console:

| Event | Price | When it fires |
|---|---|---|
| `init` | $0.01 | Once per run |
| `page-fetched` | $0.01 | When the pricing page loads successfully |
| `ai-call` | $0.05 | When the LLM extracts plan tiers (skipped on JSON-LD short-circuit) |
| `screenshot` | $0.005 | When `screenshot: true` |
| `result-pushed` | $0.005 | Once per dataset row |

**Typical run** (Playwright + LLM extraction + tracking filters, no screenshot): **~$0.075**.
**Best case** (JSON-LD short-circuit, no LLM): ~$0.025. Rare on modern SaaS pages — don't count on it.
**20 pricing pages monthly**: ~$1.50/mo.
**100 pricing pages monthly** (large competitor coverage): ~$7.50/mo.

### MCP usage (any MCP client)

Apify exposes Store Actors as MCP tools natively. Add the Apify MCP server to your client and this Actor appears as a callable tool with the inputs above. Works in:

- **Claude Desktop** — `~/Library/Application Support/Claude/claude_desktop_config.json`
- **Cursor** — `~/.cursor/mcp.json`
- **OpenAI Codex / Codex CLI** (with MCP enabled)
- **Google Gemini Code Assist** (when MCP is enabled)
- **Cline** (VS Code extension)
- **Continue.dev**
- Any custom MCP client

```jsonc
{
  "mcpServers": {
    "apify": {
      "command": "npx",
      "args": ["-y", "@apify/actors-mcp-server"],
      "env": { "APIFY_TOKEN": "<your-apify-token>" }
    }
  }
}
```

Then ask your agent:

> *"Track Stripe and Notion's pricing pages weekly. Alert me if any plan's monthly price changes by more than 10%."*

The agent calls this Actor for both pages, captures the `trackingFilters` for ongoing monitoring, and uses the structured `plans` data to detect price deltas across runs.

### Examples

```jsonc
// Batch — track your top 5 competitors in one run (PREFERRED for monitoring)
{
  "urls": [
    "https://stripe.com/pricing",
    "https://www.notion.so/pricing",
    "https://linear.app/pricing",
    "https://vercel.com/pricing",
    "https://www.cloudflare.com/plans/"
  ],
  "currencyHint": "USD",
  "fetchMode": "playwright",
  "extractTrackingFilters": true
}

// Single URL — one-off scrape
{ "url": "https://www.anthropic.com/pricing", "currencyHint": "USD" }

// Cheap one-off scrape — HTTP mode, no filters, no screenshot
{
  "url": "https://www.example-saas.com/pricing",
  "fetchMode": "http",
  "extractTrackingFilters": false
}

// Non-USD pricing page
{
  "url": "https://www.example-saas.de/preise",
  "currencyHint": "EUR"
}
```

### Limits

- Single URL per run. For multi-page workflows, run the Actor once per URL or use Apify's Task scheduler.
- Modern SaaS pricing pages are JS-rendered React apps — use `fetchMode: "playwright"` (the default). HTTP mode works for server-rendered pages and is cheaper.
- LLM uses `gpt-4.1-mini` under the hood. The Actor handles cost; no API key for you to manage.
- Currency detection works best when the page consistently displays a single currency. For multi-region pricing pages, set `currencyHint` to disambiguate.
- "Custom" / "Contact us" / "Enterprise" tiers return `monthly_price: null` — that's correct, not a bug.

### Source & methodology

Engine: `@site-spy/smart-config-core` — the same module used in production by a change-detection service. **Tested on 1,066 real production pages with 99.4% configure-success** — works on sites that break generic scrapers (A/B-tested layouts, hashed CSS classes, JS-heavy SPAs, non-English content).

Two-stage extraction architecture:

1. **JSON-LD fast path** — deterministic parse of `Product.offers[]` when available. Free, no LLM call. Empirically rare on modern React-rendered pricing pages.
2. **LLM fallback** — Defuddle-cleaned markdown + `gpt-4.1-mini` with a zod-validated extraction schema. Handles "Custom" pricing, multi-region pages, non-English content, missing annual/monthly disambiguation.

Source is bundled into the Actor image at publish time.

# Actor input Schema

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

Track multiple competitor pricing pages in one run. Each URL produces its own dataset row. Use this for monitoring 10-30 competitor brands in one task. If empty, the single 'url' field is used.

## `url` (type: `string`):

Single pricing page URL. Convenience field for one-off runs. Ignored when 'urls' (batch) is set.

## `currencyHint` (type: `string`):

ISO 4217 code (USD, EUR, GBP, BRL, JPY, etc.). Helps the engine pick the right currency on multi-region pricing pages or pages with ambiguous currency display. Defaults to USD.

## `fetchMode` (type: `string`):

'http' is cheaper but doesn't render JS — works for server-rendered marketing pages. 'playwright' renders modern React-based pricing pages (Stripe, Notion, Linear, etc.).

## `extractTrackingFilters` (type: `boolean`):

When true, returns CSS selectors + regex filters alongside the pricing data so you can wire ongoing change detection into changedetection.io, n8n, Zapier, your own code, or any MCP-compatible AI agent. The differentiator over plain pricing scrapers.

## `screenshot` (type: `boolean`):

Save a full-page PNG of the pricing page to the key-value store. Only available with Playwright fetch mode. Adds a screenshot event to billing.

## `useEasyList` (type: `boolean`):

Apply EasyList + Fanboy's Cookie List to strip ads and cookie banners before analysis. Improves accuracy on pricing pages that bury content behind GDPR banners.

## `userAgent` (type: `string`):

Override the default User-Agent string. Leave empty to use the Apify default.

## Actor input object example

```json
{
  "urls": [
    "https://stripe.com/pricing",
    "https://www.notion.so/pricing",
    "https://linear.app/pricing"
  ],
  "currencyHint": "USD",
  "fetchMode": "playwright",
  "extractTrackingFilters": true,
  "screenshot": false,
  "useEasyList": true
}
```

# 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://stripe.com/pricing",
        "https://www.notion.so/pricing",
        "https://linear.app/pricing"
    ],
    "currencyHint": "USD"
};

// Run the Actor and wait for it to finish
const run = await client.actor("vkuprin/saas-pricing-watcher").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://stripe.com/pricing",
        "https://www.notion.so/pricing",
        "https://linear.app/pricing",
    ],
    "currencyHint": "USD",
}

# Run the Actor and wait for it to finish
run = client.actor("vkuprin/saas-pricing-watcher").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).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://stripe.com/pricing",
    "https://www.notion.so/pricing",
    "https://linear.app/pricing"
  ],
  "currencyHint": "USD"
}' |
apify call vkuprin/saas-pricing-watcher --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=vkuprin/saas-pricing-watcher",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/lo5xEcP0sQGxBkCw8/builds/dpuZH7QmzX2VQRZW5/openapi.json
