# Shopify Variant Price and Availability Monitor (`number_one_scraper/shopify-variant-monitor`) Actor

Track public Shopify variant prices and availability across runs. Export timestamped snapshots and before/after changes for agency reporting. $2 per 1,000 variant observations, including unchanged checks.

- **URL**: https://apify.com/number_one_scraper/shopify-variant-monitor.md
- **Developed by:** [Bhargava Sharabha Pagidimarri](https://apify.com/number_one_scraper) (community)
- **Categories:** E-commerce
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$2.00 / 1,000 variants

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 Variant Price and Availability Monitor

Monitor public Shopify product variants for ecommerce agency reporting. Each successful run writes current prices, currency, options, availability and UTC observation times to an Apify dataset. Reusing a named snapshot store enables comparisons across runs. The first successful observation is a baseline; later runs emit added, changed and removed variant events.

### Quick start

Open this Actor in Apify Console, enter a supported public Shopify product URL, and choose a new unique `snapshotStoreName`. Set a maximum run charge (for example $0.10) in run options, then start the Actor. No Shopify account or API token is needed.

```json
{
  "startUrls": ["https://www.allbirds.com/products/womens-allbirds-flip-flop-dusty-pink"],
  "snapshotStoreName": "my-shopify-monitor-v1",
  "maxProductsPerStore": 3,
  "concurrency": 1
}
```

The sample public product may change or become unavailable. Open Dataset to inspect snapshots and export JSON or CSV; open the `SUMMARY` output for collection counts. Run again sequentially with the same snapshot-store name to compare with the saved baseline. Each run has its own dataset.

Choose a distinct store name for each client or monitor. Do not put secrets, customer IDs or private URLs in input. Public storefronts can change or block access at any time.

### Input

| Field | Default | Limits / behavior |
| --- | --- | --- |
| `startUrls` | Required | 1–100 HTTPS strings; product `/products/handle`, store root, or locale root. Variant query parameters are ignored; all returned variants are collected. |
| `snapshotStoreName` | Required | 3–63 alphanumeric/hyphen characters, beginning with alphanumeric; reuse across runs. |
| `maxProductsPerStore` | 100 | 1–1000 attempted unique products per origin, across all locale paths. Explicit products precede discovery within each locale. |
| `concurrency` | 2 | 1–5 origins at once. One request at a time per origin. |
| `delayMs` | 1000 | 500–60000 ms minimum between request starts per origin. |
| `retries` | 2 | 0–3 retries after the initial attempt, only transient errors, HTTP 429 and 5xx. |
| `timeoutMs` | 20000 | 1000–60000 ms per attempt. Maximum response size 5 MB. |

Store discovery uses the public `products.json` listing with at most 250 products per page. This endpoint is not a guaranteed Shopify discovery contract: use explicit product URLs if it is unavailable. A product cap warning means coverage may be incomplete, including when the catalog happens to equal the cap. No product disappearance is inferred from a partial catalog, missing URL or 404.

### Output contract (schema version 1)

The default dataset mixes four record types. Filter on `recordType` before importing a spreadsheet or processing changes. JSON preserves arrays and before/after objects; CSV exports flatten them according to Apify export options.

- `snapshot`: `productUrl`, string `productId` and `variantId`, `productTitle`, `variantTitle`, ordered `options`, decimal-string `price`, integer `priceRaw`, three-letter `currency`, boolean `available`, UTC `observedAt`.
- `change`: all variant fields plus `changeType` (`added`, `changed`, `removed`), `changedFields`, `previousObservedAt`, `before`, `after`, and deterministic `eventId`. Tracked fields are price, currency, availability and options. Titles alone do not trigger events. Removed rows retain old descriptive fields, use the current observation time and have `after: null`.
- `error` or `warning`: `productUrl`, `observedAt`, `message`. These are not price/stock observations.

`priceRaw` is Shopify Ajax's integer monetary representation; `price` divides it by 100 and formats two decimals, including currencies Shopify represents with appended zeroes. It is **not** a universal ISO minor-unit conversion. Currency comes from the same locale's public `cart.js` and session cookies are retained in memory for the origin. `available` means storefront purchasability; it is not an inventory quantity, stock guarantee or shipping promise.

`SUMMARY` in the run's default key-value store contains counts, request/retry counts, successful response bytes, and start/finish times. Inspect `errors` and `warnings` even for successful runs. Zero successful products fails the Actor after saving error rows and summary. Partial success exits successfully. Storage errors fail the run rather than being treated as storefront errors.

### Paid usage

Each successful variant snapshot costs **$0.002**, including unchanged observations on subsequent runs. There is no start, change, error or warning charge. Platform usage during the run is included. Reading or retaining results after a run may incur standard Apify storage charges. For example, 100 variants checked daily for 30 days produce 3,000 observations and cost $6 in event charges.

A budget too small for the next complete product stops the run before charging or writing that product; earlier completed work remains. Charges are confirmed before dataset output, and the baseline is saved only after all product output succeeds. These operations are not transactional: a charge can succeed followed by a failed or partial dataset write, leaving charged observations without output. Inspect charges and partial results before retrying; a new run can charge repeated observations again.

### Persistence and reliability

Snapshots are keyed by SHA-256 of the normalized product URL, including locale. A successful dataset write precedes replacement of that product's snapshot. Errors never overwrite a baseline. Baselines are saved per product, not atomically for the whole run. Interrupted runs can leave partial results; rerun the same input. A crash between dataset write and snapshot save can repeat change events; consumers must deduplicate by `eventId`. This is not exactly-once delivery.

**Never overlap runs sharing a snapshot store.** Apify key-value writes are not a distributed compare-and-swap lock. Use one saved Task and an external run coordinator if multiple callers are possible. Schedule comfortably beyond observed worst-case duration and skip a launch when a prior run is running. Parallel independent clients need different store names. Deleting the named store resets the baseline; old data is retained until you explicitly delete it. URLs removed from input leave their saved records untouched. Renaming or relocating a product URL establishes a new baseline.

### Coverage and operational limits

Only public HTTPS storefront JSON is supported. No browser execution, proxies, authenticated stores, password gates, CAPTCHAs, checkout/private cart data, B2B pricing, subscriptions, bundles, customer discounts or reliable regional targeting. The Actor only GETs a fresh public cart for currency; it never adds products or uses shopper credentials. Prices reflect the run location and anonymous session. Markets/geolocation changes can cause real currency or price differences across runs; do not treat these as a merchant price change without reviewing context.

Redirects are rejected: supply the final canonical host and locale URL. Private/reserved IP addresses are rejected during connection DNS resolution; custom ports and URL credentials are unsupported. Non-JSON pages, invalid fields, unknown currency, unsafe numeric IDs and products returning 250 or more variants fail explicitly without updating snapshots. This conservative 250 cutoff avoids claiming full coverage for large-variant responses; stores with larger variant sets need a future authenticated Storefront API integration. Empty variant arrays are also rejected. A 404 is not proof of removal. `removed` only means a previously observed variant was absent from a valid, sub-limit product response, not proof it was deleted from Shopify's backend.

Transient retries use bounded exponential backoff and honor Retry-After up to 60 seconds. A longer requested wait stops that request until a later run. Do not use this Actor to bypass access restrictions. Reduce frequency or stop monitoring when a merchant requests it. The worst-case request budget is bounded by 100 input URLs/origins, per-store product caps and pagination, but timeouts/retries can make a large run long. Start with 3–10 products and a 5-minute platform timeout; split large portfolios into separate non-overlapping tasks. Forced timeout leaves partial output and completed-product checkpoints.

### Scheduling

Use one saved Task per monitor and reuse its snapshot-store name. Keep limited permissions. Choose a new unique store name for the first run; the Actor can reuse a store it created in later runs by the same user. A pre-existing store created elsewhere is not automatically accessible. See [Apify's named-storage permission guidance](https://docs.apify.com/actors/development/permissions/migration-guide#the-actor-accesses-named-storages). Prevent overlapping runs sharing a store.

### Support

Use the Actor's Apify Issues tab. Include run ID, public product URL, expected vs actual behavior, and relevant error row. Do not include tokens, private carts or customer data. Shopify endpoint or schema changes can require maintenance; stop downstream alerts if currency or coverage becomes uncertain.

# Actor input Schema

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

1–100 HTTPS product, store root or locale root URLs. Use final canonical domains.

## `snapshotStoreName` (type: `string`):

Unique storage name per monitor/account. Reuse on repeated runs. Never overlap runs sharing this name.

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

Product cap per store. Allowed range 1–1000.

## `concurrency` (type: `integer`):

Concurrent stores. Allowed range 1–5.

## `retries` (type: `integer`):

Retries per request. Allowed range 0–3.

## `delayMs` (type: `integer`):

Minimum request interval (ms). Allowed range 500–60000.

## `timeoutMs` (type: `integer`):

Request timeout (ms). Allowed range 1000–60000.

## Actor input object example

```json
{
  "startUrls": [
    "https://www.allbirds.com/products/womens-allbirds-flip-flop-dusty-pink"
  ],
  "snapshotStoreName": "agency-demo-shopify-v1",
  "maxProductsPerStore": 100,
  "concurrency": 2,
  "retries": 2,
  "delayMs": 1000,
  "timeoutMs": 20000
}
```

# Actor output Schema

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

No description

## `summary` (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 = {
    "startUrls": [
        "https://www.allbirds.com/products/womens-allbirds-flip-flop-dusty-pink"
    ],
    "snapshotStoreName": "agency-demo-shopify-v1"
};

// Run the Actor and wait for it to finish
const run = await client.actor("number_one_scraper/shopify-variant-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.allbirds.com/products/womens-allbirds-flip-flop-dusty-pink"],
    "snapshotStoreName": "agency-demo-shopify-v1",
}

# Run the Actor and wait for it to finish
run = client.actor("number_one_scraper/shopify-variant-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.allbirds.com/products/womens-allbirds-flip-flop-dusty-pink"
  ],
  "snapshotStoreName": "agency-demo-shopify-v1"
}' |
apify call number_one_scraper/shopify-variant-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,number_one_scraper/shopify-variant-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/DfBqbTLAkDVuIULii/builds/4qbfQt8gt4BygjGrT/openapi.json
