# Shopify Price Tracker & Stock Monitor (Price Drops, Restocks) (`upward_enterprises/shopify-store-price-monitor`) Actor

Watch any list of Shopify stores. Get every product's price, compare-at price, and stock by variant, and on every later run only what changed: price drops and rises, stock-outs, restocks, new and removed products. Public product feed, no login. From $0.80 per 1,000 products.

- **URL**: https://apify.com/upward\_enterprises/shopify-store-price-monitor.md
- **Developed by:** [Upward Enterprises](https://apify.com/upward_enterprises) (community)
- **Categories:**
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.80 / 1,000 products

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#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

## Shopify Price Tracker & Stock Monitor

**$1 per 1,000 products ($0.80 on the Gold tier), $0.002 per changed product, $0.005 per store checked. First run of a store and any store that cannot be read are free.**

Give it a list of Shopify stores. It returns every product's current price, compare-at price, and stock, variant by variant, and on every run after the first it tells you exactly what changed: price drops and rises, stock-outs, restocks, new products, removed products. Schedule it daily or hourly and pipe the changes to Slack, Google Sheets, Airtable, or a webhook.

It reads the public product feed every Shopify store publishes. No login, no HTML scraping, no bot tricks: one polite request a second per store, with a named User-Agent. Stores that refuse automated access are reported as blocked, not worked around.

### What you get

All rows land in one dataset with a `kind` field: `product`, `change`, or `store`. Product rows come first for each store, then its change rows, then its store summary. The Console views **Products** and **Changes** pick the useful columns for each kind; filter on `kind` to keep one kind, for example `?clean=true&view=changes` and then keep rows where `kind` is `change`.

**Product rows** (`kind: "product"`), one per product:

```json
{
  "kind": "product",
  "store": "allbirds.com",
  "productId": "6540182913104",
  "handle": "mens-wool-runners",
  "title": "Men's Wool Runners",
  "vendor": "Allbirds",
  "productType": "Shoes",
  "tags": ["men", "wool"],
  "url": "https://allbirds.com/products/mens-wool-runners",
  "price": 98,
  "priceMax": 110,
  "compareAtPrice": 120,
  "onSale": true,
  "available": true,
  "variantCount": 14,
  "availableVariantCount": 11,
  "options": ["Size", "Color"],
  "imageCount": 6,
  "image": "https://cdn.shopify.com/s/files/.../wool-runner.jpg",
  "description": "Our original everyday sneaker, made with soft merino wool ...",
  "createdAt": "2024-03-01T09:00:00-08:00",
  "updatedAt": "2026-09-08T10:12:44-07:00",
  "publishedAt": "2024-03-02T09:00:00-08:00",
  "checkedAt": "2026-09-09T18:00:00.000Z",
  "variants": [
    { "id": "39400", "title": "8 / Natural Grey", "sku": "WR-8-NG", "price": 98, "compareAtPrice": 120, "available": true, "options": ["8", "Natural Grey"], "grams": 620, "updatedAt": "2026-09-08T10:12:44-07:00" }
  ],
  "changes": [ { "change": "price_down", "variantTitle": "8 / Natural Grey", "from": 110, "to": 98, "deltaPct": -10.91 } ]
}
```

`available` is `true` when any variant is in stock, `false` when every variant reports out of stock, and `null` when the store does not publish availability. `description` is the first 500 characters of the product description as plain text.

**Change rows** (`kind: "change"`), one per change since your last run of the same snapshot:

```json
{
  "kind": "change",
  "store": "allbirds.com",
  "productId": "6540182913104",
  "handle": "mens-wool-runners",
  "title": "Men's Wool Runners",
  "url": "https://allbirds.com/products/mens-wool-runners",
  "variantId": "39400",
  "variantTitle": "8 / Natural Grey",
  "sku": "WR-8-NG",
  "change": "price_down",
  "from": 110,
  "to": 98,
  "delta": -12,
  "deltaPct": -10.91,
  "detectedAt": "2026-09-09T18:00:00.000Z"
}
```

| `change` | Meaning |
|---|---|
| `price_down`, `price_up` | A variant's price moved. `from`, `to`, `delta`, `deltaPct` are filled in. |
| `out_of_stock`, `back_in_stock` | A variant's availability flipped. `from` and `to` are booleans. |
| `compare_at_change` | The strike-through price changed (a sale started or ended). |
| `new_product`, `removed_product` | A product appeared in or vanished from the store. Product-level rows have no variant fields. |
| `new_variant`, `removed_variant` | A size or color was added or dropped. |

**Store rows** (`kind: "store"`), one per store, after its other rows:

```json
{
  "kind": "store",
  "store": "allbirds.com",
  "origin": "https://allbirds.com",
  "inputStore": "allbirds.com",
  "status": "ok",
  "message": null,
  "products": 294,
  "variants": 2857,
  "changes": 3,
  "pages": 2,
  "complete": true,
  "firstRun": false,
  "removedProducts": [ { "productId": "6540182913104", "handle": "mens-wool-runners", "title": "Men's Wool Runners" } ],
  "checkedAt": "2026-09-09T18:00:00.000Z",
  "durationMs": 4120
}
```

| `status` | Meaning |
|---|---|
| `ok` | The feed was read. `complete` says whether the whole catalog was seen (false when the product cap stopped paging). |
| `partial` | Some products were read, then a later page failed. Rows for the products read are delivered; removals are not reported and the baseline is not updated. |
| `blocked` | The store refuses automated access (401, 403, 430, a long rate-limit, or a bot challenge page). |
| `not_shopify` | No product feed at this address: not a Shopify storefront, or the feed is disabled. |
| `duplicate` | This address resolves to a store already checked in the same run. |
| `error` | Network failure or a server error after a retry. |

`message` carries the reason for anything other than a clean `ok`. If a store answers from a different host than you typed (a `www.` redirect, a new domain), `store` and `origin` show the host that answered and `inputStore` what you typed; the snapshot is kept under the host that answered.

### Input

| Field | What it does |
|---|---|
| Stores to watch | Domains or URLs, one per line. Any page of the store works. |
| What to deliver | Products and changes (default), products only, or changes only. In products-only mode, removed products are listed on the store row instead of as change rows. |
| Max products per store | Paging stops after this many. Default 2,000. If a store has more products than this, additions and removals cannot be told apart from products beyond the cap, so those are not reported; raise the limit to cover the whole catalog. |
| Include variants | Attach every variant with its own price, SKU, and availability. Default on. |
| Snapshot name | Which memory to compare against; kept on your account as a key-value store named `shopify-price-monitor-<name>`. Give each schedule its own name. Lower-case letters, digits, and hyphens; other characters become hyphens. Do not run two schedules on the same name at the same time. |
| Reset snapshot | Forget the remembered prices for these stores and start a new baseline; no change rows this run. |
| Delay between requests | Default 1,000 ms per page. Faster bursts get rate-limited by many stores. |

The first run for a store is the baseline: product rows (in the two modes that deliver them) and no change rows. Every later run compares against the remembered prices, which live compressed in a key-value store on your own Apify account under the snapshot name. Before reporting a product as removed, the Actor checks its page once more, so a product that merely moved between feed pages is not reported.

### Pricing

| Event | Free and Bronze | Silver | Gold and above |
|---|---|---|---|
| Product | $0.001 | $0.0009 | $0.0008 |
| Changed product | $0.002 | $0.0018 | $0.0016 |
| Store checked | $0.005 | $0.0045 | $0.004 |

A changed product is billed once per run however many of its variants moved; the extra variant rows are delivered free and marked `billed: false`. So a run can never cost more than about twice its catalog: a store-wide sale on a 300-product store is at most $0.60. A store's first, baseline run and any store that could not be read (blocked, not Shopify, duplicate, error) are free. Watching five stores with 300 products each once a day, changes only, costs $0.025 a day plus $0.002 per changed product; on a quiet day that is under a cent. Delivering all 1,500 product rows too adds $1.50 a run. Set a maximum total charge on the run to cap spend; the Actor stops cleanly when it is reached and never delivers rows it could not bill. A run that stops early does not overwrite a store's baseline.

### Use cases

- **Competitor price tracking** for a DTC brand: a daily changes-only run into a Slack channel.
- **Resellers and arbitrage**: watch for restocks and price drops across a set of suppliers.
- **Catalog snapshots**: a weekly products run into a spreadsheet for merchandising reviews.
- **AI agents**: ask "what did competitor X change this week" through the Apify MCP server and get rows, not screenshots.

### Limits and honesty

- Prices come from the store's public feed in the store's own currency; the feed does not include currency codes or customer-specific pricing.
- Stores behind a bot wall are reported as `blocked`. The Actor does not retry with a browser identity.
- Stores that have disabled the product feed, or that are not on Shopify, are reported as `not_shopify`.
- Very large catalogs: memory use is about 1 KB per product, so 50,000 products per store fits the default memory.
- At the end of each run the Actor reports run counts (rows delivered, events charged, duration, status) to its maintainer's dashboard. It never sends your input, your results, or anything about your account.

### About

Built and maintained by **Upward Enterprises · AI & API Services** (Iowa, USA). Public-data tools for people and AI agents, with pay-per-record pricing and no keys to manage.

### Changelog

- **0.1** (2026-09-10): first release.

# Actor input Schema

## `stores` (type: `array`):

Shopify store domains or URLs, one per line, e.g. allbirds.com or https://shop.example.com. Any page of the store works; only the domain is used.

## `mode` (type: `string`):

Products: one row per product with current prices and stock, and that product's changes attached. Changes: only what changed since the last run, one row per change. Both: products and change rows. In Products mode, removed products are listed on the store row instead of as change rows.

## `maxProductsPerStore` (type: `integer`):

Stop paging a store after this many products. If a store has more than this, additions and removals of products cannot be told apart from products beyond the cap, so those are not reported; raise the limit to cover the whole catalog. Each product costs about 1 KB of memory.

## `includeVariants` (type: `boolean`):

Attach every variant (size, color) with its own price, compare-at price, SKU, and availability to each product row.

## `snapshotName` (type: `string`):

Which memory to compare against; stored on your account as a key-value store named shopify-price-monitor-<this name>. Lower-case letters, digits, and hyphens; other characters become hyphens. Give each schedule its own name. Do not run two schedules on the same name at the same time.

## `resetSnapshot` (type: `boolean`):

Forget the remembered prices for these stores and start a new baseline on this run (no change rows this time).

## `requestDelayMs` (type: `integer`):

Pause between page requests to the same store. One request a second is polite and reliable; faster bursts get rate-limited by many stores.

## Actor input object example

```json
{
  "stores": [
    "allbirds.com"
  ],
  "mode": "both",
  "maxProductsPerStore": 2000,
  "includeVariants": true,
  "snapshotName": "default",
  "resetSnapshot": false,
  "requestDelayMs": 1000
}
```

# Actor output Schema

## `all` (type: `string`):

No description

## `productColumns` (type: `string`):

No description

## `changeColumns` (type: `string`):

No description

## `csv` (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 = {
    "stores": [
        "allbirds.com"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("upward_enterprises/shopify-store-price-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 = { "stores": ["allbirds.com"] }

# Run the Actor and wait for it to finish
run = client.actor("upward_enterprises/shopify-store-price-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 '{
  "stores": [
    "allbirds.com"
  ]
}' |
apify call upward_enterprises/shopify-store-price-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,upward_enterprises/shopify-store-price-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/IsmMJKqnbSkc3imYE/builds/JwiFyyjjtmj5gLU0a/openapi.json
