# Shopify Store Scraper & Price Monitor (`asgardlabs/shopify-store-api`) Actor

Every product, variant, price and stock status from any Shopify store, and exactly what changed since your last run: price drops, back in stock, sold out, new and removed products. Half a cent per store, a tenth of a cent per product. Stores that block the public feed are reported free.

- **URL**: https://apify.com/asgardlabs/shopify-store-api.md
- **Developed by:** [Asgard Labs](https://apify.com/asgardlabs) (community)
- **Stats:** 2 total users, 1 monthly users, 33.3% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 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.

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 Store Scraper & Price Monitor – products, variants, stock, price changes

Give it Shopify shop addresses. Get the complete catalog of each shop: every product and variant with price, compare-at price, stock status, images, tags and options. Run it again with **only changes** on and you get exactly what moved since last time: new products, price changes with old and new value, back in stock, out of stock, removed products.

No proxies, no browser, no login. It reads the public product feed every Shopify shop publishes, 250 products per page, so a 10,000-product shop takes under a minute.

### Shopify product data per store

- **title, handle, vendor, productType, tags, url, store, currency**
- **minPrice, maxPrice, onSale, maxDiscountPct, available, variantCount, availableVariantCount**
- **variants** – id, title, sku, price, compareAtPrice, available, options, weight, dates
- **images, featuredImage, options, createdAt, updatedAt, publishedAt**
- **status** – `new`, `changed`, `unchanged` or `removed`; **changes** – `price` (from, to), `back-in-stock`, `out-of-stock`, `variant-added`, `variant-removed`; **firstSeenAt**
- **descriptionText / descriptionHtml** when `includeBody` is on

Per shop, the run summary also records the shop's name, currency, country and its myshopify address.

### Use cases

- Watch competitor prices daily.
- Track when a sold-out item returns.
- Build a product list for a price-comparison site.
- Audit a client's catalog.

### Input

| Field | Default | Meaning |
|---|---|---|
| `stores` | – | Shop domains or URLs. Custom domains work. A pasted collection link (`…/collections/sale`) reads that collection only. |
| `maxProductsPerStore` | 0 | 0 = whole catalog. |
| `collection` | – | Collection handle, e.g. `sale`. A misspelled handle is reported as an error, not as an empty shop. |
| `onlyChanges` | false | Output only new / changed / removed products since the last run. History is kept per shop in a key-value store. |
| `onSaleOnly`, `availableOnly`, `vendor`, `productType`, `tags`, `minPrice`, `maxPrice` | – | Filters. |
| `includeBody`, `includeCollections` | false | Product description; list of collections per shop. |
| `timeoutSecs` | 300 | Time budget per shop. |
| `concurrency` | 5 | Shops read in parallel. |
| `stateStoreName` | shopify-monitor-state | Where the change history lives. Use a different name to start fresh. |

### Output example

```json
{ "id": 6970463658064, "handle": "mens-tree-runners", "title": "Men's Tree Runners", "vendor": "Allbirds", "productType": "Shoes", "tags": ["men", "runner"],
  "url": "https://allbirds.com/products/mens-tree-runners", "store": "allbirds.com", "currency": "USD",
  "minPrice": 98, "maxPrice": 98, "onSale": false, "maxDiscountPct": 0, "available": true, "variantCount": 14, "availableVariantCount": 11,
  "images": ["https://cdn.shopify.com/…"], "featuredImage": "…", "options": [{ "name": "Size", "values": ["8", "9"] }],
  "variants": [{ "id": 4046…, "title": "8", "sku": "TR1M-…", "price": 98, "compareAtPrice": null, "available": true, "option1": "8" }],
  "status": "changed", "changes": [{ "variantId": 4046…, "type": "price", "from": 110, "to": 98 }], "firstSeenAt": "2026-08-01T…" }
```

### How to use

1. Paste your store domains, one per line into the input form. The example input is ready to run as is.
2. Click **Start**. Rows appear in the **Output** tab as they are found.
3. Download the table as CSV, JSON or Excel, or read it through the API.
4. For monitoring, create a **Schedule** and switch on `onlyChanges`: later runs return only what changed, and only that is charged.

### Pricing

Pay per event: **`store-scan` $0.005** per shop that was read, **`product` $0.001** per product in the output. A 2,000-product catalog costs about $2. A daily `onlyChanges` run on 50 shops usually costs well under $0.50, because unchanged products are not output and not charged. Shops that are blocked, dead, not Shopify, or misspelled are free.

### Good to know

- **"On sale" depends on the shop.** A product counts as on sale only when the shop publishes a compare-at price higher than the price. Shops that show discounts in other ways (Gymshark and Allbirds among them) will show `onSale: false`.
- **Removed products are only reported on a full scan.** With a product cap, a collection or a filter, the run sees part of the catalog, so it does not guess what was removed. The history of unseen products is kept for the next full scan.
- **Stock is yes/no.** The public feed shows in stock or sold out per variant, never quantities.
- **Shops that switch off the public feed** (password-protected or some enterprise setups) are reported as `Shopify store but products.json is disabled/blocked` and not charged.
- **Errors are in plain words**: `ENOTFOUND (domain not found)`, `timeout`, `collection not found: xyz`, `not a Shopify store`.

### Use from code or an AI agent

Every run can be started and read through the Apify API, the JavaScript and Python clients, or an AI agent with tool access. Open the **API** tab on this page for ready-made snippets. Pricing is per event, so an agent pays only for what it receives.

### FAQ

**Does it work on custom domains, not only myshopify.com?**
Yes. Give it the shop's public address, e.g. `allbirds.com`. The shop's own currency, name and country come back with every product.

**Can it see inventory quantities?**
No. Shopify's public feed shows in stock or sold out per variant, not how many. That is what `available` and `availableVariantCount` report.

**What happens when a store has switched off the public product feed?**
The store is reported as `Shopify store but products.json is disabled/blocked` and is not charged. Nothing is guessed.

**How does "only changes" billing work?**
The first full read charges every product once. Later runs with `onlyChanges` on charge only products that are new, changed or removed since the last run, so daily monitoring of a 1,000-product shop usually costs a few cents.

**Can I read one collection only?**
Yes. Paste the collection link, e.g. `shop.com/collections/sale`, or set the `collection` handle. `/collections/all` means the whole catalogue. A misspelled handle is reported as an error, not as zero products.

# Actor input Schema

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

Store domains or URLs (custom domains work). A pasted collection link reads that collection only; /collections/all means the whole catalogue.

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

0 = whole catalog. With a cap, removed products cannot be detected. The example input uses 250 so a first try is quick and cheap.

## `collection` (type: `string`):

e.g. "sale" or "new-arrivals". A misspelled handle is reported as an error.

## `includeBody` (type: `boolean`):

Adds descriptionHtml and descriptionText to every product. Rows get larger.

## `includeCollections` (type: `boolean`):

Adds one free row per store of type "collections" with every collection's handle, title and product count.

## `onlyChanges` (type: `boolean`):

Price changes, back-in-stock, out-of-stock, new and removed products. State is kept per store in a key-value store. Removed products are only reported on a full scan (no cap, collection or filter).

## `onSaleOnly` (type: `boolean`):

Keep only products where at least one variant has a compare-at price above its price.

## `availableOnly` (type: `boolean`):

Keep only products with at least one variant marked available.

## `vendor` (type: `array`):

Keep only these vendors (brand names as the store spells them, case-insensitive).

## `productType` (type: `array`):

Keep only these product types, case-insensitive.

## `tags` (type: `array`):

Keep products carrying at least one of these tags.

## `minPrice` (type: `integer`):

Keep products whose highest variant price is at least this, in the store's currency.

## `maxPrice` (type: `integer`):

Keep products whose lowest variant price is at most this, in the store's currency.

## `timeoutSecs` (type: `integer`):

A store that takes longer is reported as timed out and not charged.

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

How many stores to read at the same time.

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

Key-value store in your account that keeps the last snapshot per store for "only changes". Letters, digits and dashes.

## Actor input object example

```json
{
  "stores": [
    "allbirds.com",
    "gymshark.com"
  ],
  "maxProductsPerStore": 250,
  "includeBody": false,
  "includeCollections": false,
  "onlyChanges": false,
  "onSaleOnly": false,
  "availableOnly": false,
  "vendor": [],
  "productType": [],
  "tags": [],
  "timeoutSecs": 300,
  "concurrency": 5,
  "stateStoreName": "shopify-monitor-state"
}
```

# Actor output Schema

## `products` (type: `string`):

One row per product: title, vendor, type, tags, min/max price, currency, onSale, availability, variants, images, status (new / changed / unchanged / removed) and changes. Collections rows (type: collections) when includeCollections is on.

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

Counts per run and per store (storeResults with name, currency, country), errors with plain reasons, charge-limit state.

# 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",
        "gymshark.com"
    ],
    "maxProductsPerStore": 250
};

// Run the Actor and wait for it to finish
const run = await client.actor("asgardlabs/shopify-store-api").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",
        "gymshark.com",
    ],
    "maxProductsPerStore": 250,
}

# Run the Actor and wait for it to finish
run = client.actor("asgardlabs/shopify-store-api").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",
    "gymshark.com"
  ],
  "maxProductsPerStore": 250
}' |
apify call asgardlabs/shopify-store-api --silent --output-dataset

```

## MCP server setup

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

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/pBHU9XqMZNnfCVNtD/builds/LYEYQPh1myLpBHLO9/openapi.json
