# AppExchange Change Monitor – New Apps, Reviews & Pricing Alerts (`puchus-monitoring/appexchange-change-monitor`) Actor

Monitors Salesforce AppExchange listings, searches and categories and outputs only what changed since the last run: new apps, new reviews, rating, review count, pricing and listing detail changes, and removed listings.

- **URL**: https://apify.com/puchus-monitoring/appexchange-change-monitor.md
- **Developed by:** [Andres Garcia](https://apify.com/puchus-monitoring) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per usage

This Actor is paid per platform usage. The Actor is free to use, and you only pay for the Apify platform usage, which gets cheaper the higher subscription plan you have.

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

## 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

## AppExchange Change Monitor – New Apps, Reviews & Pricing Alerts

Track Salesforce AppExchange listings and get **only what changed since your last run**: new apps in a category or search, new reviews, rating and review-count changes, pricing changes, listing detail changes and removed listings.

Run it on a schedule (daily or weekly) and send the changes to Slack, email or your own tools. When nothing changed, the run outputs nothing and costs only the listing checks.

> This Actor is an independent tool and is not affiliated with or endorsed by Salesforce. It reads only public listing data that anyone can see on AppExchange without logging in.

### What problem does it solve?

If you sell on AppExchange, or advise companies that buy from it, you need to know when:

- a competitor gets new reviews, or its rating drops or rises;
- a competitor changes its price or pricing model;
- a new app shows up in your category or for your keyword;
- a listing changes its name, description, supported editions or products;
- an app disappears from AppExchange.

Checking this by hand every week is slow and easy to forget. A full-directory scraper gives you thousands of rows on every run, and you still have to compare spreadsheets yourself to see what changed.

### How is this different from an AppExchange scraper?

| | Directory scraper | AppExchange Change Monitor |
| --- | --- | --- |
| Output on every run | The whole directory or search, again | Only the differences since the previous run |
| Run with no changes | Same thousands of rows | Empty dataset |
| Comparing runs | You do it in a spreadsheet | Built in, with old and new value per change |
| New apps in a category | You have to diff lists | `NEW_LISTING` rows |
| New reviews | You have to diff review lists | `NEW_REVIEW` rows with the review |
| Best for | One-off research, lead lists | Scheduled competitor and market monitoring |

The Actor keeps the state of each monitor in a named key-value store in your Apify account, so every run is compared with the previous run of the same monitor.

### Who uses it

- **ISVs with an app on AppExchange**: watch competitors' reviews, ratings and prices, and new apps in your category. Typically weekly.
- **Product managers and marketing teams**: get every new review (yours and your competitors') to reply quickly or learn from it. Typically daily.
- **Salesforce consultancies**: spot new apps in a category to recommend or partner with. Typically weekly.
- **Analysts and investors**: follow how a segment of the marketplace evolves. Typically monthly.

### What it detects

| `changeType` | When | `field` / values |
| --- | --- | --- |
| `NEW_LISTING` | An app appears for the first time in one of your searches or categories | `field: "source"`, `newValue`: e.g. `"searchQuery:cpq"` or `"category:sales"` |
| `NEW_REVIEW` | A review was published on a tracked listing | `review`: author display name, date, stars, title, text |
| `RATING_CHANGED` | The average rating changed (1 decimal, as shown on AppExchange) | `field: "rating"`, old and new rating |
| `REVIEW_COUNT_CHANGED` | The number of reviews changed | `field: "reviewCount"` |
| `PRICING_CHANGED` | The pricing model or any active plan (price, currency, unit, frequency) changed | `field: "pricing"`, old and new pricing object |
| `DETAILS_CHANGED` | The app name, short description, supported editions or supported products changed | `field`: `appName`, `shortDescription`, `editions` or `productsSupported` |
| `LISTING_REMOVED` | A tracked listing is no longer on AppExchange in **two consecutive runs** | — |

Texts are normalized before comparing (spacing and letter case), so cosmetic edits do not create false alerts.

### Input

You need a **monitor name** and at least one of: listing URLs, search queries or categories.

| Field | Type | Default | Description |
| --- | --- | --- | --- |
| `monitorName` | string | required | Name of this monitor, e.g. `competitors-cpq`. Runs with the same name are compared with each other; use different names for different watch lists. |
| `listingUrls` | array of strings | – | Listings to track, as URLs (`https://appexchange.salesforce.com/appxListingDetail?listingId=…`) or bare listing IDs. |
| `searchQueries` | array of strings | – | Keyword searches. New apps in the results are reported, and the apps found are tracked. |
| `categories` | array of strings | – | Category slugs as used in AppExchange URLs, e.g. `sales`, `marketing`, `customerService`, `contractManagement`, `documentGeneration`, `salesIntelligence`, `integration`. |
| `trackReviews` | boolean | `true` | Detect new reviews. |
| `maxListingsPerSearch` | integer | `200` | Maximum listings taken from each search or category (in AppExchange order, sponsored results excluded). |
| `emitBaseline` | boolean | `false` | On the first run, also output the full initial state of every listing. |

Example:

```json
{
    "monitorName": "competitors-cpq",
    "listingUrls": [
        "https://appexchange.salesforce.com/appxListingDetail?listingId=a0N30000001f1q1EAA",
        "a0N300000016b7FEAQ"
    ],
    "searchQueries": ["CPQ"],
    "categories": ["contractManagement"],
    "trackReviews": true,
    "maxListingsPerSearch": 200,
    "emitBaseline": false
}
```

#### The first run creates a baseline

The first run of a monitor saves the current state of every listing and outputs **no rows** (unless `emitBaseline` is `true`, in which case you get one `NEW_LISTING` row per listing with `field: "baseline"` and the initial values). From the second run on, you get only changes.

### Output

Each dataset item is one change, with the previous and the new value.

```json
{
    "changeType": "RATING_CHANGED",
    "listingId": "a0N300000016b7FEAQ",
    "appName": "Conga Composer Salesforce Connector | document generation for anyone, anywhere",
    "providerName": "Conga",
    "listingUrl": "https://appexchange.salesforce.com/appxListingDetail?listingId=a0N300000016b7FEAQ",
    "field": "rating",
    "oldValue": 4.7,
    "newValue": 4.6,
    "review": null,
    "detectedAt": "2026-10-17T07:00:00.000Z",
    "monitorName": "competitors-cpq"
}
```

A new review:

```json
{
    "changeType": "NEW_REVIEW",
    "listingId": "a0N30000001f1q1EAA",
    "appName": "RevOps for Salesforce / Native CPQ / Quote to Cash / Subscription Billing / RLM",
    "providerName": "Kugamon",
    "listingUrl": "https://appexchange.salesforce.com/appxListingDetail?listingId=a0N30000001f1q1EAA",
    "field": null,
    "oldValue": null,
    "newValue": null,
    "review": {
        "id": "a6HHu000000XXXXXXX",
        "author": "Jane D.",
        "date": "2026-08-12T14:54:56.000Z",
        "stars": 5,
        "title": "Easy to set up",
        "text": "We had quotes going out the same week. …"
    },
    "detectedAt": "2026-10-17T07:00:00.000Z",
    "monitorName": "competitors-cpq"
}
```

A new app in a search:

```json
{
    "changeType": "NEW_LISTING",
    "listingId": "a0N3A00000XXXXXXXX",
    "appName": "Example CPQ",
    "providerName": "Example Inc.",
    "listingUrl": "https://appexchange.salesforce.com/appxListingDetail?listingId=a0N3A00000XXXXXXXX",
    "field": "source",
    "oldValue": null,
    "newValue": "searchQuery:cpq",
    "review": null,
    "detectedAt": "2026-10-17T07:00:00.000Z",
    "monitorName": "competitors-cpq"
}
```

You can export the dataset as JSON, CSV, Excel or HTML, or read it through the Apify API. The **Changes** view in the Output tab shows the main columns in a table.

At the end of each run the log shows a summary: listings checked, changes by type and any errors.

### How to run it every week

1. Open the Actor in Apify Console, fill in the input and click **Save as a new task**. Give the task a clear name, e.g. "CPQ competitors".
2. Go to **Schedules** → **Create new schedule**.
3. Pick a frequency, for example every Monday at 07:00 (cron `0 7 * * 1`), or daily (`0 7 * * *`).
4. Add your task to the schedule and save.

Keep the same `monitorName` in the task: that name is what links each run to the previous one. To watch several lists separately (for example competitors and a whole category), create one task per list, each with its own monitor name.

### How to get the changes in Slack or by email

Use Apify integrations on the task:

- **Slack**: open the task → **Integrations** → **Slack**, connect your workspace, pick the channel and the "run succeeded" event. The message is a template, so you can include a link to the run's dataset, which contains only the changes.
- **Email**: open the task → **Integrations** → **Gmail**, connect a Google account and send an email after each successful run, with the dataset attached (CSV, JSON, Excel…).
- **Failure alerts**: in the task's monitoring settings, add an email or Slack alert for failed runs, so you know when a run could not check your listings.
- **Other tools**: use a **webhook** on the `ACTOR.RUN.SUCCEEDED` event, or Zapier, Make or n8n, to read the dataset items and post them wherever you want (CRM, Google Sheets, Teams…).

Because the dataset only contains changes, you can send it as it is: an empty dataset means nothing happened.

### Pricing

> **Launch period: free.** While the Actor is new, it is free to use: you only pay the small Apify platform usage of each run (it runs without a browser, on 256 MB of memory). After the launch period it will switch to the pay-per-event prices below. Apify notifies every user at least 14 days before a pricing change, so you will have time to decide.

After launch, this Actor uses pay-per-event pricing. You pay for what it does, not for compute time:

| Event | When | Price |
| --- | --- | --- |
| Actor start | Once per run (charged by Apify) | Minimal |
| `listing-checked` | Each listing checked in a run | $0.003 |
| `change-detected` | Each change row in the dataset | $0.03 |

The first (baseline) run charges only `listing-checked`; it never charges `change-detected`.

**Monthly cost examples (after launch)**

- 100 listings checked **daily** with about 5 changes a day: 100 × $0.003 + 5 × $0.03 = **$0.45 per run**, about **$13.50 per month**.
- 50 competitor listings checked **weekly** with about 10 changes a week: 50 × $0.003 + 10 × $0.03 = **$0.45 per run**, about **$1.95 per month**.

If you set a maximum cost per run, the Actor checks only as many listings as the limit allows, stops cleanly before going over it, and saves its state. Changes it could not deliver are reported in the next run, so nothing is lost.

### FAQ

**Why did my first run return no results?**
The first run of a monitor creates the baseline: it saves the current state so later runs have something to compare with. Run it again (or wait for the schedule) to get changes. Set `emitBaseline` to `true` if you also want the initial state as output.

**How do I start over?**
Use a new `monitorName`, or delete the key-value store named `appexchange-monitor-<your monitor name>` in **Storage**. The next run creates a new baseline.

**Can I add or remove listings later?**
Yes. Listings, searches or categories you add are taken as baseline in their first run (they do not produce `NEW_LISTING` rows), and changes are reported from then on.

**When is a listing reported as removed?**
Only when AppExchange reports it as not found in two consecutive runs. If AppExchange is down, the listing cannot be read, or many listings disappear at once (which usually means the site changed), the previous state is kept and nothing is reported as removed.

**What happens if AppExchange changes its website?**
Every run checks the quality of the data before saving anything. If mandatory fields (name, rating, number of reviews) come back empty for more than 20% of listings, the run fails with a clear message and the saved state is not touched, so you will not get a wave of false changes.

**Does it use a browser? How fast is it?**
No browser. It uses the same public JSON endpoints as the AppExchange website, with low concurrency and pauses between requests. Expect about one to two seconds per listing; 100 listings take a few minutes.

**What review data does it collect?**
Only what the listing shows publicly: reviewer display name, date, stars, title and text. It does not collect reviewer profiles, IDs or contact data, and it never collects publisher emails or phone numbers.

**Are sponsored results included in searches?**
No. Searches and categories are read without sponsored placements, so `NEW_LISTING` reflects organic results.

**Can I get the whole AppExchange directory?**
That is not what this Actor is for. Use a directory scraper for one-off exports, and this Actor to follow what changes over time.

**I found a problem or need another field.**
Open an issue in the **Issues** tab. We answer within 24 hours.

### More monitors by puchus-monitoring

Same approach in every actor: no browser, change-only output, named state per monitor, and pay only for what you get.

- [Google Hotels Price Scraper & Monitor](https://apify.com/puchus-monitoring/google-hotels-price-scraper-monitor) – hotel rates by provider (Booking, Expedia, Agoda, official site) with price-drop alerts.
- [TED Tender Monitor](https://apify.com/puchus-monitoring/ted-tender-monitor) – new EU public tenders, corrigenda, deadline changes and awards by keyword, CPV and country.

Missing a field or a source? Open an issue on any of them – I usually answer within 24 h.

# Actor input Schema

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

Identifier of this monitor, e.g. "competitors-cpq". The saved state is separated by this name: runs with the same name are compared with each other.

## `listingUrls` (type: `array`):

Specific AppExchange listings to track, as full URLs (e.g. https://appexchange.salesforce.com/appxListingDetail?listingId=a0N30000001f1q1EAA) or bare listing IDs (e.g. a0N30000001f1q1EAA).

## `searchQueries` (type: `array`):

AppExchange keyword searches. New apps appearing in these results are reported as NEW_LISTING, and the apps found are tracked for changes.

## `categories` (type: `array`):

AppExchange category slugs as they appear in AppExchange URLs, e.g. "sales", "marketing", "contractManagement", "documentGeneration". New apps appearing in these categories are reported as NEW_LISTING, and the apps found are tracked for changes.

## `trackReviews` (type: `boolean`):

Detect new reviews on the tracked listings (NEW_REVIEW).

## `maxListingsPerSearch` (type: `integer`):

Maximum number of listings taken from each search query or category.

## `emitBaseline` (type: `boolean`):

On the first run of a monitor (no saved state yet), also output the complete initial state of every listing. By default the first run only saves the baseline and outputs nothing.

## Actor input object example

```json
{
  "monitorName": "competitors-cpq",
  "listingUrls": [
    "https://appexchange.salesforce.com/appxListingDetail?listingId=a0N30000001f1q1EAA"
  ],
  "searchQueries": [
    "CPQ"
  ],
  "trackReviews": true,
  "maxListingsPerSearch": 200,
  "emitBaseline": false
}
```

# Actor output Schema

## `results` (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 = {
    "monitorName": "competitors-cpq",
    "listingUrls": [
        "https://appexchange.salesforce.com/appxListingDetail?listingId=a0N30000001f1q1EAA"
    ],
    "searchQueries": [
        "CPQ"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("puchus-monitoring/appexchange-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 = {
    "monitorName": "competitors-cpq",
    "listingUrls": ["https://appexchange.salesforce.com/appxListingDetail?listingId=a0N30000001f1q1EAA"],
    "searchQueries": ["CPQ"],
}

# Run the Actor and wait for it to finish
run = client.actor("puchus-monitoring/appexchange-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 '{
  "monitorName": "competitors-cpq",
  "listingUrls": [
    "https://appexchange.salesforce.com/appxListingDetail?listingId=a0N30000001f1q1EAA"
  ],
  "searchQueries": [
    "CPQ"
  ]
}' |
apify call puchus-monitoring/appexchange-change-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,puchus-monitoring/appexchange-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/VdAMSv9NPZK6beNij/builds/rLFYV09EP9AbXbLCH/openapi.json
