# Shopify Product Change Monitor (`xgt999/my-actor`) Actor

Monitor up to 20 Shopify product URLs for variant price and availability changes. Compare with the last successful run and export JSON. Source errors are kept separate from stock changes. No proxy or AI key required. Not a full-store crawler; compatibility limits apply.

- **URL**: https://apify.com/xgt999/my-actor.md
- **Developed by:** [John Z STUDIO](https://apify.com/xgt999) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$0.01 / actor start

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

## Shopify Product Change Monitor

Compare a watchlist of public Shopify product pages with their last successful
observations. Output includes variant price changes, sellable/unavailable
transitions, added/removed variants, explicit observations and source errors.
The initial successful run establishes a baseline and emits no change events.

### Run on Apify

1. Enter 1-20 canonical product URLs and a dedicated baseline storage name.
2. Keep the same storage name and monitor ID for subsequent runs.
3. Start the Actor with 512 MB memory. The first successful check establishes
   the baseline; the next checks compare with that stored observation.
4. Open Results for observations, changes and errors, or export the dataset as
   JSON. Run summary provides totals. This Actor does not send email alerts.

### Pricing

The configured launch price is **$0.01 per run start**, including up to 20 input
URLs. There is no per-result or per-change charge and platform execution usage
is included. The start fee applies even if there are no changes, a source is
unavailable, or the run fails. The Actor supports 256-512 MB memory, so there is
only one automatically billed start event per run. Apify's displayed pricing is
authoritative; taxes and optional services outside this Actor are not included.

The minimum configurable maximum charge is $0.02. This is a spending cap, not a
second charge: a normal run uses the single $0.01 event. No paid proxy, AI key,
or external subscription is required. Results and store compatibility are not
guaranteed. Begin with one product before scheduling a larger watchlist.

### Input

```json
{
  "productUrls": ["https://theme-dawn-demo.myshopify.com/products/art-deco-cyclamen"],
  "monitorId": "my-watchlist",
  "stateStoreName": "product-change-baselines"
}
```

Use 1-20 canonical HTTPS product URLs with no query strings, fragment, credentials
or nonstandard port. Optional locale paths are supported. The example is a public
demonstration store, not a customer or endorsement. Change `monitorId` to begin a
new baseline; unlisted URLs are not monitored and are not reported as removed.

### Output and failure handling

- `observation`: title, currency, variant values, baseline flag and change count.
- `change`: `price_changed`, `restocked`, `out_of_stock`, `variant_added`, or
  `variant_removed`. Stock event names refer only to storefront availability,
  not physical inventory counts. Prices are integer minor units for supported
  two-decimal currencies; CAD 16500 means CAD 165.00.
- `error`: a product could not be checked. HTTP 404 is an error, never an inferred
  deletion. Failures, currency changes and replaced product identities preserve
  the last good baseline. Storage/output failures stop the run.

### Example change records

This is a **simulated fixture example**, produced by the actual comparison code,
not a live store change or a customer result. The first check records a baseline;
the next check changes a variant from USD 100.00 to USD 85.00 and from unavailable
to available. The resulting records include:

```json
[
  { "recordType": "change", "type": "price_changed", "id": "demo-variant", "previous": 10000, "current": 8500, "currency": "USD" },
  { "recordType": "change", "type": "restocked", "id": "demo-variant" }
]
```

These excerpts omit the `url` and `observedAt` fields for clarity. A source failure
instead produces an `error` row, with no stock-change event and no replacement of
the last successful baseline. An unchanged successful check emits an observation
with `changeCount: 0`; a healthy run can legitimately report no changes.

### Limits and responsible use

Only pages you are permitted to access should be monitored. Public accessibility
is not a license to reuse data. The reader observes robots.txt, checks public DNS
addresses at connection time, rejects redirects and private addresses, and stops
requesting a host after authentication rejection or rate limiting. It performs no
login, bypass, purchase or cart mutation. No source page script is executed.

Limits: 20 URLs, 60 HTTP attempts per run, 2 MiB decompressed response size, a
16 MiB cumulative response budget (counting failed responses too), 20-second
request timeout, sequential requests and at least one second between same-host
requests. Longer declared crawl delays are honored up to 30 seconds, otherwise
the product is skipped. Robots failures fail closed except a missing 404 policy.

Currency comes from unambiguous public product-page metadata, not a guessed
symbol. Supported: USD, EUR, GBP, CAD, AUD, NZD, CHF, SGD, HKD, CNY. Other currencies
or missing metadata fail closed. Observations reflect the runner's anonymous
regional storefront; keep runner region stable. Personalized prices, pages with
inconsistent HTML/API currency, headless stores and exact inventory counts are not
supported. Separate HTML and JSON requests are not an atomic snapshot. Products
returning 250 or more variants are rejected because the Ajax API may truncate them.

### Persistent monitoring

This is a hosted Apify Actor, not a downloadable code package. There is no local
installation step for buyers. Never put private tokens in product URLs or input.
`stateStoreName` selects a dedicated
persistent key-value store in the running user's account. Keep the name and
`monitorId` stable between scheduled runs. Do not overlap runs for the same monitor:
cloud key-value writes are not transactional. Interrupted output can be repeated
on retry; consumers should not assume exactly-once delivery.

Cloud runs have an internal 120-second deadline. Slow or blocked sources can
prevent completing the whole watchlist. On timeout the run fails; previously
saved results remain available, while the summary may be missing and an
interrupted product may be repeated on retry. Use smaller watchlists for slow
stores. Scheduling and external alerts are not enabled automatically.
All-source failure fails the cloud run; partial failure is recorded
in the dataset and `SUMMARY` alongside successful results.

### Verification

The local suite passed 29 deterministic checks, covering comparison, validation,
source failures, baseline preservation and resource limits. The fixture example
above additionally checks a baseline, price drop, availability recovery, source
failure and unchanged recovery through the production comparison pipeline.

Real Apify smoke tests passed against Shopify's Dawn demo product, including a
repeat run of the bounded-runtime build with one product, zero errors and no
changes. This is narrow test coverage, not a promise of universal storefront
compatibility, customer adoption or financial returns. This tool is independent
and is not affiliated with or endorsed by Shopify.

### References

- https://shopify.dev/docs/api/ajax/reference/product
- https://docs.apify.com/api/client/js/reference/class/ApifyClient
- https://docs.apify.com/actors/development/actor-definition/actor-json

# Actor input Schema

## `productUrls` (type: `array`):

1-20 public Shopify product URLs. Use canonical URLs without query strings. Only monitor pages you are permitted to access.

## `stateStoreName` (type: `string`):

A dedicated persistent key-value store in your account. Reuse it for subsequent runs. Do not overlap runs using the same monitor ID.

## `monitorId` (type: `string`):

Reuse to compare with the last successful observation. Change this value to reset a baseline.

## Actor input object example

```json
{
  "productUrls": [
    "https://theme-dawn-demo.myshopify.com/products/art-deco-cyclamen"
  ],
  "stateStoreName": "product-change-baselines",
  "monitorId": "default"
}
```

# Actor output Schema

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

Observation rows contain product title, currency, variants, baseline flag and change count. Change rows describe variant price or availability changes. Error rows do not imply a stock change.

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

Counts of checked products, failures, changes, baselines and requests. May be absent after a timeout or storage failure.

# 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 = {
    "productUrls": [
        "https://theme-dawn-demo.myshopify.com/products/art-deco-cyclamen"
    ],
    "stateStoreName": "product-change-baselines"
};

// Run the Actor and wait for it to finish
const run = await client.actor("xgt999/my-actor").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 = {
    "productUrls": ["https://theme-dawn-demo.myshopify.com/products/art-deco-cyclamen"],
    "stateStoreName": "product-change-baselines",
}

# Run the Actor and wait for it to finish
run = client.actor("xgt999/my-actor").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 '{
  "productUrls": [
    "https://theme-dawn-demo.myshopify.com/products/art-deco-cyclamen"
  ],
  "stateStoreName": "product-change-baselines"
}' |
apify call xgt999/my-actor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,xgt999/my-actor"
        }
    }
}
```

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/BgpILr34eVM1w9KBf/builds/CLRv5uIVivUD3albc/openapi.json
