# Price & Competitor Monitor — Track Price Changes (`darknezz/price-competitor-monitor`) Actor

Track product prices and competitor offers on Shopify and AliExpress. Persistent per-URL price history, price/availability change detection and webhook alerts — clean JSON, no API key required.

- **URL**: https://apify.com/darknezz/price-competitor-monitor.md
- **Developed by:** [Oaida Adrian](https://apify.com/darknezz) (community)
- **Categories:** E-commerce, Automation, Business
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.00 / 1,000 product checkeds

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

## Price & Competitor Monitor — Track Price Changes

Monitor **any Shopify or AliExpress product's price over time** and get alerted the moment it changes. Give the Actor a list of product URLs and it keeps a persistent per-URL price history, detects price and availability changes between runs, and flags (or webhooks) every change — clean JSON, no API key, no login, no proxy required.

Built for price-watchers, dropshippers and competitor-research teams who track prices in a spreadsheet and want a scheduled, diffing alternative.

### Why this Actor

- **Per-URL persistence** — history lives in the Actor's own durable key-value store (30 days by default, up to 365), so scheduled runs build a real price timeline instead of throwaway snapshots.
- **Change detection, not just scraping** — every run diffs against the previous one and reports `diffType` (`NEW` / `PRICE_CHANGED` / `AVAILABILITY_CHANGED` / `UNCHANGED`) with absolute and percentage change.
- **Alert output** — changed rows carry `alert: true`; set a webhook URL and the Actor POSTs a compact JSON alert per change (Zapier, Make, Slack hooks, Mailgun…).
- **One schema for both marketplaces** — Shopify product pages and AliExpress item pages produce the same normalized record, so your downstream table stays uniform.
- **Browser-grade TLS** — requests impersonate the newest Chrome/Safari/Firefox fingerprints with per-request rotation, the same hardening that keeps the house AliExpress scraper alive against its anti-bot wall.
- **USD-pinned AliExpress prices** — the storefront is pinned to the global site in USD/English so prices are comparable wherever the run executes.

### How it works

For each URL the Actor: (1) fetches the product — Shopify via the store's public `/products/<handle>.json` plus `/cart.js` for currency; AliExpress via the item page with the house-proven search-page selector as a price fallback, matched by product id; (2) reads the previous snapshot from the durable key-value store; (3) computes the diff (price change, availability change, `diffType`); (4) appends the new point to history and prunes points older than `historyDays`; (5) emits one dataset row per URL with snapshot + diff + recent history, and posts to the webhook if the row changed.

The first run of a URL is the baseline (`NEW`, no alert). Alerts fire only on real changes afterwards.

### Input

```json
{
  "urls": ["https://allbirds.com/products/mens-strider-medium-grey",
           "https://colourpop.com/products/the-daily-face-eye-cream-moisturizer-set"],
  "notifyOnChange": true,
  "historyDays": 30,
  "webhookUrl": "https://hooks.example.com/price-alerts"
}
```

| Field | Type | Description |
|---|---|---|
| `urls` | array | Shopify product URLs (`/products/<handle>`) or AliExpress item URLs (`/item/<id>.html`) — **required** |
| `notifyOnChange` | boolean | Emit `alert: true` rows and fire the webhook on changes (default `true`) |
| `historyDays` | integer | History window to keep per URL (default 30, max 365) |
| `webhookUrl` | string | Optional JSON POST target for change alerts |

### Output (one item per URL per run)

```json
{
  "url": "https://colourpop.com/products/the-daily-face-eye-cream-moisturizer-set",
  "status": "ok",
  "sourceType": "shopify",
  "productId": "7397683134546",
  "title": "The Daily Face + Eye Cream",
  "currency": "USD",
  "price": 29.0,
  "diffType": "PRICE_CHANGED",
  "previousPrice": 36.25,
  "priceChange": -7.25,
  "priceChangePercent": -20.0,
  "priceDirection": "down",
  "availabilityChanged": false,
  "alert": true,
  "priceHistory": [
    {"price": 36.25, "currency": "USD", "available": false, "checkedAt": "2026-08-13T09:00:00Z"},
    {"price": 29.0, "currency": "USD", "available": false, "checkedAt": "2026-08-14T09:00:00Z"}
  ],
  "checkedAt": "2026-08-14T09:00:00Z"
}
```

A fetch that fails cleanly (blocked page, deleted product) emits a `status: "error"` row instead — so your pipeline sees outages, not just price moves.

### Use cases

- 📉 **Price-drop alerts** — schedule a daily run per competitor, get pinged the day a price dips.
- 🧾 **Price history** — build a defensible "what it cost" timeline for listings and buying decisions.
- 🏷️ **Competitor monitoring** — track the same product across Shopify stores and AliExpress sellers in one table.
- 🔔 **Availability watch** — `AVAILABILITY_CHANGED` fires when an item goes out of stock or comes back.

### Run it from the API

**curl:**

```bash
curl -X POST "https://api.apify.com/v2/acts/darknezz~price-competitor-monitor/run-sync-get-dataset-items?token=YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"urls":["https://allbirds.com/products/mens-strider-medium-grey"],"notifyOnChange":true,"historyDays":30}'
```

**Python (Apify SDK):**

```python
from apify_client import ApifyClient

client = ApifyClient("YOUR-APIFY-TOKEN")
run = client.actor("darknezz/price-competitor-monitor").call(
    run_input={"urls": ["https://colourpop.com/products/the-daily-face-eye-cream-moisturizer-set"]},
)
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item["title"], "|", item["price"], item["currency"], "|", item["diffType"], "| alert:", item["alert"])
```

**Scheduling:** attach an Apify Schedule (daily or hourly) — the durable key-value store is what turns repeat runs into a history.

### Pricing

Pay per event: a small fee for each product checked, plus Apify's standard platform events. No subscription — you pay only for what you run.

### FAQ

**How long is history kept?** `historyDays` (default 30, max 365); older points are pruned each run, and the output row always carries the recent history.

**Do I need a proxy or account?** No — Shopify's public product JSON and AliExpress's server-rendered search pages are read directly with browser-grade TLS.

**Can I get email alerts?** The Actor posts JSON to any webhook URL; point it at a mail bridge (Mailgun, SendGrid, Zapier Email) and you have email alerts without SMTP credentials in the input.

**What if a page is blocked or a product deleted?** The run records a `status: "error"` row and leaves the previous snapshot untouched — a transient block never looks like a price crash.

**Are AliExpress prices stable across runs?** Yes — the storefront is pinned to USD/English, so the same product reads the same price regardless of egress region.

**Is scraping this legal?** Product pages and search results are public and unauthenticated. You are responsible for complying with each store's terms and applicable laws.

### Limitations

- Shopify stores that disabled `/products.json` are not supported (rare).
- AliExpress item pages are client-rendered; the price comes from the search-page fallback matched by product id, so an item absent from search is reported without a price until it reappears.
- Availability on AliExpress is not currently reported (`available: null`).
- History is capped at 500 points per URL as a safety limit.

# Actor input Schema

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

Shopify product URLs (https://store.com/products/handle) or AliExpress item URLs (https://www.aliexpress.com/item/ID.html). One per line. Each URL is fetched on every run; the price history is kept per URL in the Actor's key-value store.

## `notifyOnChange` (type: `boolean`):

When true, rows whose price or availability changed since the previous run are flagged alert=true and, if a webhookUrl is set, posted to it. The first run of a new URL is a baseline (no alert).

## `historyDays` (type: `integer`):

How many days of price history to keep per URL. Older points are pruned on every run.

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

When a change is detected (and notifyOnChange is true), a JSON payload with the alert details is POSTed here. Works with Zapier, Make, Slack-compatible webhooks, Mailgun, etc.

## Actor input object example

```json
{
  "urls": [
    "https://allbirds.com/products/mens-strider-medium-grey",
    "https://colourpop.com/products/the-daily-face-eye-cream-moisturizer-set",
    "https://allbirds.com/products/mens-tree-dashers"
  ],
  "notifyOnChange": true,
  "historyDays": 30
}
```

# Actor output Schema

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

No description

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

No description

## `title` (type: `string`):

No description

## `price` (type: `string`):

No description

## `currency` (type: `string`):

No description

## `diffType` (type: `string`):

No description

## `alert` (type: `string`):

No description

## `productUrl` (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": [
        "https://allbirds.com/products/mens-strider-medium-grey",
        "https://colourpop.com/products/the-daily-face-eye-cream-moisturizer-set",
        "https://allbirds.com/products/mens-tree-dashers"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("darknezz/price-competitor-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://allbirds.com/products/mens-strider-medium-grey",
        "https://colourpop.com/products/the-daily-face-eye-cream-moisturizer-set",
        "https://allbirds.com/products/mens-tree-dashers",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("darknezz/price-competitor-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://allbirds.com/products/mens-strider-medium-grey",
    "https://colourpop.com/products/the-daily-face-eye-cream-moisturizer-set",
    "https://allbirds.com/products/mens-tree-dashers"
  ]
}' |
apify call darknezz/price-competitor-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,darknezz/price-competitor-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/0NmaxdT3cfcUOyZ2q/builds/18GwAEogjMk2TUvk4/openapi.json
