# Shopify Scraper & Price Tracker (`suicidalsquirrel/shopify-product-inventory-monitor`) Actor

Scrape products from public Shopify stores: prices, variants, SKUs, images, and availability. Compare with a previous run to flag price changes, restocks, and new products. Blocked or non-Shopify sites get a clear status, and you pay only for saved products.

- **URL**: https://apify.com/suicidalsquirrel/shopify-product-inventory-monitor.md
- **Developed by:** [SuicidalSquirrel](https://apify.com/suicidalsquirrel) (community)
- **Categories:** E-commerce, Automation, Developer tools
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.35 / 1,000 product snapshots

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?

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

### Monitor Shopify products and availability

Create normalized snapshots of public Shopify products, variants, prices, and availability for monitoring, research, data pipelines, and AI agents.

The Actor uses direct HTTP requests—never a browser—to read public storefront pages and Shopify product data. It returns one dataset item per product and an explicit diagnostic when a source is unavailable, blocked, unsupported, or cannot be parsed.

#### What you get

- Product title, URL, handle, vendor, type, description, and tags.
- Minimum, maximum, and compare-at prices in the publicly displayed currency.
- Product and variant availability, normalized options, SKUs, and prices.
- Exact inventory quantity only when a public storefront response explicitly provides it; otherwise `null`.
- Public image URLs and alt text without downloading image files.
- Optional `new`, `updated`, or `unchanged` comparison against a previous Apify dataset.
- Explicit, uncharged diagnostic items instead of silent empty results.
- Run telemetry covering requests, transferred bytes, retries, duration, response statuses, proxy tier, and concurrency.

This is an independent community Actor and is not affiliated with or endorsed by Shopify.

### How to monitor Shopify products

1. Add one or more public store homepages, collection URLs, or product URLs.
2. Choose the product limits and fields to include, then run the Actor.
3. View or export the dataset as JSON, CSV, Excel, XML, or through the Apify API.

Start with one product URL for the quickest test:

```json
{
    "startUrls": ["https://www.beardbrand.com/products/alliance-membership"],
    "maxProductsPerStore": 10,
    "maxProducts": 10,
    "includeVariants": true,
    "includeDescription": true,
    "includeImages": true,
    "includeInventory": true,
    "maxConcurrency": 10,
    "proxyConfiguration": { "useApifyProxy": false }
}
```

Direct requests are the default. A proxy is used only when you explicitly configure one; the Actor never silently enables a paid residential proxy group.

#### Input reference

| Field                 | Purpose                                                          |             Default |
| --------------------- | ---------------------------------------------------------------- | ------------------: |
| `startUrls`           | Public Shopify store, collection, or product URLs                | One example product |
| `maxProductsPerStore` | Maximum successful product snapshots per store                   |               `100` |
| `maxProducts`         | Hard product cap across the run                                  |              `1000` |
| `includeVariants`     | Include normalized public variant data                           |              `true` |
| `includeDescription`  | Include cleaned plain-text descriptions                          |              `true` |
| `includeImages`       | Include image URLs and alt text                                  |              `true` |
| `includeInventory`    | Include public availability and explicitly exposed quantities    |              `true` |
| `previousDatasetId`   | Readable earlier dataset to compare against                      |             Not set |
| `maxConcurrency`      | Maximum concurrent product requests; decreases after rate limits |                `10` |
| `proxyConfiguration`  | Optional Apify or custom proxy settings                          |   Direct connection |

`maxProducts` is the run-wide cost guard. The Actor accepts at most 1,000 input URLs, 5,000 products per store, 10,000 products per run, and concurrency from 1 to 50.

### Shopify product data output

This trimmed example omits some fields for readability; the Full dataset view contains every schema field:

```json
{
    "status": "ok",
    "errorCode": null,
    "errorMessage": null,
    "storeDomain": "beardbrand.com",
    "productId": "1353927262291",
    "handle": "alliance-membership",
    "productUrl": "https://www.beardbrand.com/products/alliance-membership",
    "title": "Alliance Membership",
    "currency": "USD",
    "priceMin": 90,
    "priceMax": 90,
    "availableForSale": true,
    "variants": [
        {
            "id": "27845715066963",
            "title": "Default Title",
            "sku": "ALLIANCE",
            "price": 90,
            "compareAtPrice": null,
            "availableForSale": true,
            "inventoryQuantity": null,
            "selectedOptions": [{ "name": "Title", "value": "Default Title" }]
        }
    ],
    "change": null,
    "source": {
        "type": "product",
        "input": "https://www.beardbrand.com/products/alliance-membership"
    },
    "fetchedAt": "2026-10-08T12:00:00.000Z"
}
```

The **Overview** dataset view emphasizes status, diagnostics, prices, availability, change classification, and URL. The **Full data** view exposes every normalized field.

#### Diagnostic items

When no product can be produced for an input source, the Actor saves one item whose `status` is `unavailable`, `blocked`, `unsupported`, or `error`. Check `errorCode` and `errorMessage` for the reason. Diagnostic items are free.

### Detect price and availability changes

1. Run a baseline snapshot and copy its default dataset ID.
2. Run the same sources again and select that dataset in `previousDatasetId`.
3. Read the `change` object on each successful product.

`change.type` is `new`, `updated`, or `unchanged`. `priceChanged` and `availabilityChanged` also inspect matching variants, so a variant-level update can be detected even when the product-level minimum and maximum prices stay the same. `changedFields` lists the normalized fields that differ.

The Actor does not automatically retain a shared baseline between runs. Your schedule or workflow must pass the previous dataset ID that should be compared.

### Use cases

- **Competitive pricing:** collect normalized current and compare-at prices from public catalogs.
- **Stock alerts:** schedule snapshots and route availability changes to your own alert workflow.
- **Catalog research:** compare public assortments, variants, vendors, types, and tags.
- **Data pipelines:** export structured results or consume them from an API, warehouse workflow, or spreadsheet.
- **AI agents:** expose a predictable input/output contract through Apify's API or MCP server.

### Integrations and API

The examples below use the Actor's stable ID. Keep API tokens in environment variables or secret managers, never in source code or URLs.

#### REST API

For a small synchronous run that returns dataset items directly:

```bash
curl --request POST \
  --url 'https://api.apify.com/v2/acts/1wirUifsUferqbRnC/run-sync-get-dataset-items?clean=true' \
  --header "Authorization: Bearer $APIFY_TOKEN" \
  --header 'Content-Type: application/json' \
  --data '{
    "startUrls": ["https://www.beardbrand.com/products/alliance-membership"],
    "maxProducts": 1
  }'
```

For longer jobs, start the Actor asynchronously and retrieve the resulting `defaultDatasetId` rather than holding one HTTP connection open.

#### JavaScript or TypeScript

```js
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('1wirUifsUferqbRnC').call({
    startUrls: ['https://www.beardbrand.com/products/alliance-membership'],
    maxProducts: 10,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();

console.log(items);
```

#### Python

```python
import os
from apify_client import ApifyClient

client = ApifyClient(os.environ['APIFY_TOKEN'])
run = client.actor('1wirUifsUferqbRnC').call(
    run_input={
        'startUrls': ['https://www.beardbrand.com/products/alliance-membership'],
        'maxProducts': 10,
    }
)
items = client.dataset(run['defaultDatasetId']).list_items().items

print(items)
```

#### MCP and AI agents

Connect an MCP-compatible agent to `https://mcp.apify.com`, enable the Actor as a tool, and ask the agent to run it with the same JSON input. The Actor's schemas describe the input and its product and diagnostic outputs so an agent can retrieve structured results.

#### Make

Use the official Apify app in Make:

1. Add **Run an Actor** and select this Actor.
2. Map the JSON input fields from your scenario.
3. Add **Get Dataset Items** and map the run's default dataset ID into it.

For larger catalogs, use **Watch Actor Runs** followed by **Get Dataset Items** instead of waiting synchronously.

#### Zapier

Use Apify's **Run Actor** action for short jobs. Zapier's synchronous Actor action has a 30-second limit, so prefer an asynchronous run for larger or recurring catalogs. Use **Finished Actor Run** and **Fetch Dataset Items** to continue the Zap after processing completes.

### Pricing

You pay only for each successfully saved product snapshot. Discovery, comparison, change classification, and diagnostic items do not add separate charges. Platform usage is included in the event price. The price depends on your Apify plan:

| Apify plan tier | Per product snapshot | Per 1,000 products |
| --------------- | -------------------- | ------------------ |
| Free            | $0.0005              | $0.50              |
| Bronze          | $0.00045             | $0.45              |
| Silver          | $0.0004              | $0.40              |
| Gold            | $0.00035             | $0.35              |

For example, on the Free plan 100 successful products cost $0.05 and 1,000 cost $0.50. To estimate a run's maximum charge, multiply your plan's per-snapshot price by `maxProducts`. The actual charge can be lower when fewer products are returned. You can also set a maximum total charge when starting a run.

### Limits and responsible use

- Only public storefront data is supported. Shopify themes, localization, Markets configuration, headless implementations, and installed apps vary.
- Password protection, login walls, CAPTCHAs, access controls, and geographic restrictions are not bypassed.
- Some stores rate-limit or block direct HTTP traffic. These cases return explicit diagnostics, but a user-selected proxy is not a guarantee of access.
- Collection discovery reads public product links from the requested page and does not currently paginate every theme-specific collection layout.
- Currency depends on a public storefront marker because Shopify's public product JSON does not include a currency code.
- Exact inventory is not inferred from availability. It stays `null` unless a fetched public payload explicitly exposes an integer quantity.
- Images and scripts are not downloaded. Only public image URLs are returned.
- The Actor does not collect customers, accounts, carts, checkouts, orders, reviews, emails, phone numbers, or merchant-admin data.

Use the Actor only where you have a lawful basis to do so. You are responsible for complying with applicable laws, website terms, robots directives, and appropriate request rates.

### FAQ

#### Which URLs can I use?

Use a public Shopify store homepage, collection page, or direct product page. URLs containing credentials, fragments, non-HTTP protocols, localhost, private IPs, or link-local targets are rejected.

#### Why did I receive a diagnostic instead of products?

Open the Overview or Full data view and inspect `status`, `errorCode`, and `errorMessage`. Common reasons include a non-Shopify site, an unavailable page, rate limiting, a recognized block, unsupported public markup, or a product cap reached before that source began.

#### Does `availableForSale` show exact stock?

No. Availability is a public yes/no signal. `inventoryQuantity` remains `null` unless the storefront explicitly publishes an integer quantity.

#### Can it monitor changes automatically?

The Actor compares with the dataset you supply in `previousDatasetId`. Use an Apify schedule or external workflow to run snapshots and pass the desired previous dataset ID. Automatic Actor-owned cross-run state and notifications are not included.

#### Can I use a proxy?

Yes. Direct requests are the default. You can explicitly configure Apify Proxy or your own proxy URL, but no paid residential group is selected automatically.

#### Does it support password-protected or checkout data?

No. The Actor does not bypass authentication or access carts, checkouts, orders, customer records, or Shopify Admin APIs.

#### Can I export the results?

Yes. Apify datasets can be downloaded in formats including JSON, JSONL, CSV, Excel, XML, and RSS, or read with the REST, JavaScript, and Python APIs.

### Changelog

#### October 2026

- Added normalized public product, variant, price, availability, image, and description snapshots.
- Added comparison with a caller-selected previous dataset for price and availability monitoring.
- Added explicit diagnostics for unavailable, blocked, unsupported, and failed sources.
- Added optional user-configured proxy routing, structured dataset views, and integration examples.
- Verified 99.39% discovery completeness on a fixed five-store, 329-product public-sitemap benchmark and a 10.191-second median across three 100-product direct-HTTP runs. Results apply to the recorded benchmark stores and date, not every Shopify storefront.

# Actor input Schema

## `startUrls` (type: `array`):

Public storefront homepages, collection pages, or product pages. Domains without https:// are accepted. Example: https://example.com/collections/shoes

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

Maximum successful product snapshots saved from each store. Example: 100

## `maxProducts` (type: `integer`):

Hard cap across all stores to limit run cost. Example: 1000

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

Include public variant titles, options, prices, SKUs, and availability. Example: true

## `includeDescription` (type: `boolean`):

Include clean plain-text product descriptions when publicly available. Example: true

## `includeImages` (type: `boolean`):

Include public product image URLs and alt text without downloading image files. Example: true

## `includeInventory` (type: `boolean`):

Include availability and exact quantity only when the public storefront explicitly exposes it. Example: true

## `previousDatasetId` (type: `string`):

Optional readable Apify dataset ID or name from an earlier snapshot. Leave empty for no comparison. Example: aBcDeFg1234567890

## `maxConcurrency` (type: `integer`):

Maximum parallel HTTP requests; the Actor can reduce this after rate limits. Example: 10

## `proxyConfiguration` (type: `object`):

Optional proxy configuration for storefront requests. Direct requests are used by default. Example: {"useApifyProxy":false}

## Actor input object example

```json
{
  "startUrls": [
    "https://example.com/collections/shoes"
  ],
  "maxProductsPerStore": 100,
  "maxProducts": 1000,
  "includeVariants": true,
  "includeDescription": true,
  "includeImages": true,
  "includeInventory": true,
  "previousDatasetId": "aBcDeFg1234567890",
  "maxConcurrency": 10,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

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

Normalized public product snapshots plus explicit diagnostic items. Opens in the Overview view; switch to Full data for every field.

## `telemetry` (type: `string`):

Summarized request, byte, duration, retry, and direct/proxy measurements for this run.

# 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 = {
    "startUrls": [
        "https://www.beardbrand.com/products/beard-combs"
    ],
    "maxProductsPerStore": 100,
    "maxProducts": 1000,
    "includeVariants": true,
    "includeDescription": true,
    "includeImages": true,
    "includeInventory": true,
    "maxConcurrency": 10,
    "proxyConfiguration": {
        "useApifyProxy": false
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("suicidalsquirrel/shopify-product-inventory-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 = {
    "startUrls": ["https://www.beardbrand.com/products/beard-combs"],
    "maxProductsPerStore": 100,
    "maxProducts": 1000,
    "includeVariants": True,
    "includeDescription": True,
    "includeImages": True,
    "includeInventory": True,
    "maxConcurrency": 10,
    "proxyConfiguration": { "useApifyProxy": False },
}

# Run the Actor and wait for it to finish
run = client.actor("suicidalsquirrel/shopify-product-inventory-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 '{
  "startUrls": [
    "https://www.beardbrand.com/products/beard-combs"
  ],
  "maxProductsPerStore": 100,
  "maxProducts": 1000,
  "includeVariants": true,
  "includeDescription": true,
  "includeImages": true,
  "includeInventory": true,
  "maxConcurrency": 10,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}' |
apify call suicidalsquirrel/shopify-product-inventory-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,suicidalsquirrel/shopify-product-inventory-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/1wirUifsUferqbRnC/builds/eJVaXxLULZPB9e79s/openapi.json
