# Shopify Scraper — Store Products, Prices & Catalog Intel (`mochiboo/shopify-store-products-price-scraper`) Actor

Scrape any Shopify store's full product catalog from its public products.json: titles, vendors, variants, SKUs, prices, real discounts and stock status. Adds a store-intelligence row — price bands, % on sale, out-of-stock rate, new products. No API key. Company-level data only.

- **URL**: https://apify.com/mochiboo/shopify-store-products-price-scraper.md
- **Developed by:** [mochi](https://apify.com/mochiboo) (community)
- **Categories:** E-commerce, Developer tools, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 1,000 results

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/platform/actors/running/actors-in-store#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 Scraper — Store Products, Prices & Catalog Intelligence

Scrape any Shopify store's full product catalog straight from its own public `products.json`
endpoint. Get every product with variants, SKUs, prices, real discounts and stock status — plus a
**store-intelligence rollup** that tells you how a competitor is priced, how much of the range is
actually on sale, how much is out of stock, and how fast they ship new product.

No API key. No login. No browser. Works on any Shopify store that leaves its storefront JSON public.

**Keywords:** shopify scraper, shopify store products, shopify product scraper, competitor price
tracking, ecommerce price monitoring, shopify catalog export, product feed, dropshipping research.

***

### Three things this actor gets right

**1. It only counts discounts that are real.**
Shopify merchants routinely leave `compare_at_price` populated on items that are not on sale. In the
catalogs this actor was built against, one store had **603 variants with `compare_at_price` set and
not a single one higher than the actual price** — 570 were *lower*. A scraper that treats "compare-at
is present" as a discount reports that store as heavily discounted while nothing is on sale. This
actor only counts a discount when compare-at is strictly greater than price.

**2. It tells you when it only saw part of the catalog.**
Every rollup carries `catalogComplete` and `pagesFetched`. If a store rate-limits mid-walk, or you
capped the run, the percentages describe a slice — and the row says so instead of quietly presenting
slice statistics as store statistics. `filtersApplied` likewise flags a rollup computed over filtered
rows rather than the whole store.

**3. It reads each store's robots.txt first, and gets the parsing right.**
Most Shopify robots.txt files contain a literal `Disallow: /` — scoped to `User-agent: Nutch`.
A naive check would skip nearly every store on the platform; a missing check would crawl stores that
genuinely opt out. This actor resolves the correct user-agent group and applies longest-match
precedence, then skips stores that disallow the endpoint (`shop.bulletproof.com` is a real example).

***

### Input

Everything is optional — running with `{}` scrapes a demo store.

| Field | Type | Default | Description |
|---|---|---|---|
| `storeDomains` | array | `["allbirds.com"]` | Store domains. Scheme/path stripped automatically. |
| `outputMode` | enum | `products` | `products`, `storeSummary`, or `both`. |
| `maxProductsPerStore` | int | `20` | Cap on product rows per store. |
| `keyword` | string | `""` | Match against title, description and tags. |
| `productType` | string | `""` | Exact match on Shopify `product_type`. |
| `vendor` | string | `""` | Exact match on Shopify `vendor`. |
| `minPrice` / `maxPrice` | int | `0` | Price band in the store's own currency. `0` = unbounded. |
| `onlyDiscounted` | bool | `false` | Keep only genuinely discounted products. |
| `onlyInStock` | bool | `false` | Drop products with no available variant. |
| `publishedAfter` | string | `""` | `YYYY-MM-DD`; find new arrivals. |
| `respectRobots` | bool | `true` | Honour each store's robots.txt. |
| `requestDelayMs` | int | `500` | Delay between catalog pages. |
| `useApifyProxy` | bool | `false` | Rotating residential IPs (rarely needed). |

```json
{
  "storeDomains": ["www.deathwishcoffee.com"],
  "outputMode": "both",
  "onlyDiscounted": true,
  "maxProductsPerStore": 50
}
```

### Output

**Product row** — flat, stable keys, safe for agents and spreadsheets:

```json
{
  "storeDomain": "www.deathwishcoffee.com",
  "storeName": "Death Wish Coffee",
  "productId": 7218414092343,
  "handle": "rebellious-by-nature-trucker",
  "title": "Rebellious By Nature Trucker",
  "vendor": "Sportiqe",
  "productType": "Accessories",
  "tags": ["Accessories", "Apparel", "Gifting", "Merch", "Website Exclusive Sale"],
  "currency": "USD",
  "minPrice": 17.5, "maxPrice": 17.5,
  "maxDiscountPercent": 30, "isDiscounted": true,
  "availability": "in_stock",
  "variantCount": 1, "inStockVariantCount": 1,
  "skus": ["UHCLBD25"],
  "imageCount": 5,
  "createdAt": "2025-01-23",
  "publishedAt": "2026-06-15", "daysSincePublished": 45,
  "productUrl": "https://www.deathwishcoffee.com/products/rebellious-by-nature-trucker"
}
```

**Store summary row** — one per store:

```json
{
  "storeDomain": "www.deathwishcoffee.com",
  "storeName": "Death Wish Coffee", "country": "US", "currency": "USD",
  "myshopifyDomain": "deathwishcoffee.myshopify.com",
  "catalogComplete": true, "filtersApplied": false, "pagesFetched": 1,
  "productCount": 141, "variantCount": 404,
  "vendorCount": 15, "productTypeCount": 10,
  "medianPrice": 29.98,
  "discountedProductPercent": 15.6,
  "averageDiscountPercent": 34.5,
  "outOfStockVariantPercent": 11.6,
  "newProducts30d": 18,
  "topVendors": [
    { "name": "Death Wish Coffee Company", "count": 36 },
    { "name": "Sportiqe", "count": 27 }
  ],
  "topProductTypes": [
    { "name": "Apparel", "count": 39 },
    { "name": "Coffee", "count": 27 }
  ]
}
```

*(Both examples are verbatim from live runs on 2026-07-31, trimmed for length.)*

### What you can do with it

- **Competitor price monitoring** — schedule daily, watch `medianPrice` and `discountedProductPercent` move.
- **Promo detection** — `onlyDiscounted` plus a schedule tells you the moment a rival runs a sale, and how deep.
- **New-arrival tracking** — `publishedAfter` or `newProducts30d` shows how fast a brand ships product.
- **Stock-out intelligence** — `outOfStockVariantPercent` is a proxy for demand and supply trouble.
- **Catalog export / migration** — full product and variant dump with SKUs.

### Data coverage and limits — read this

- **Only stores that leave `products.json` public.** Most do; some disable it or sit behind a bot
  wall. Those return a clear `error` on the summary row (`HTTP 403`, `HTTP 404`, or
  `not a Shopify store`) rather than failing the run.
- **Not every field exists.** `products.json` exposes no inventory *quantity* (only available
  yes/no), no cost, and no per-product sales data. Currency and store identity come from
  `/meta.json`; if a store blocks that, `currency` is `null`.
- **Prices are the storefront's default-market prices.** Shopify Markets stores may show different
  prices to shoppers in other countries.
- **Rate limits are real.** The default 500 ms delay keeps large catalog walks healthy; raise
  `requestDelayMs` for stores that still return 429.
- **Company-level data only.** Product catalogs are business data. This actor requests no customer,
  reviewer or account data and never touches checkout, cart or account endpoints.

### Legal

Reads only `robots.txt`, `meta.json` and `products.json` — the same public storefront endpoints a
Shopify theme calls, shipped by default on every Shopify store. The actor fetches and honours each
store's robots.txt before reading anything, identifies itself honestly in its User-Agent, and skips
any store that disallows the endpoint or blanket-bans AI crawlers by name.

# Actor input Schema

## `storeDomains` (type: `array`):

Store domains to scrape, e.g. allbirds.com. Scheme and paths are stripped automatically. Each store is read from its own public /products.json endpoint.

## `outputMode` (type: `string`):

products = one row per product. storeSummary = one rollup row per store (price bands, % discounted, stock rate). both = product rows plus a summary row.

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

Upper bound on product rows returned per store. Store-summary mode always reads the whole catalog.

## `keyword` (type: `string`):

Keep only products whose title, description or tags contain this text. Case-insensitive. Empty = no keyword filter.

## `productType` (type: `string`):

Exact-match filter on the Shopify product\_type field, e.g. Shoes. Empty = all types.

## `vendor` (type: `string`):

Exact-match filter on the Shopify vendor field. Empty = all vendors.

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

Keep products with at least one variant priced at or above this value, in the store's own currency. 0 = no minimum.

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

Keep products with at least one variant priced at or below this value, in the store's own currency. 0 = no maximum.

## `onlyDiscounted` (type: `boolean`):

Keep only products genuinely on sale — a variant whose compare-at price is strictly higher than its price.

## `onlyInStock` (type: `boolean`):

Drop products whose every variant is unavailable.

## `publishedAfter` (type: `string`):

Keep only products published on or after this date, format YYYY-MM-DD. Useful for spotting new arrivals. Empty = no date filter.

## `respectRobots` (type: `boolean`):

Fetch and honour every store's robots.txt before reading its catalog, and skip stores that disallow it. Leave on unless you own the store.

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

Pause between catalog pages of the same store. The 500 ms default keeps large catalog walks from tripping the store's rate limiter; raise it further for stores that still return HTTP 429.

## `useApifyProxy` (type: `boolean`):

Route requests through rotating residential IPs. Off by default — these are public endpoints and usually need no proxy.

## Actor input object example

```json
{
  "storeDomains": [
    "allbirds.com"
  ],
  "outputMode": "products",
  "maxProductsPerStore": 20,
  "keyword": "",
  "productType": "",
  "vendor": "",
  "minPrice": 0,
  "maxPrice": 0,
  "onlyDiscounted": false,
  "onlyInStock": false,
  "publishedAfter": "",
  "respectRobots": true,
  "requestDelayMs": 500,
  "useApifyProxy": false
}
```

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

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

# Run the Actor and wait for it to finish
run = client.actor("mochiboo/shopify-store-products-price-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "storeDomains": [
    "allbirds.com"
  ]
}' |
apify call mochiboo/shopify-store-products-price-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=mochiboo/shopify-store-products-price-scraper",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/Lor6QMOlhouWz7OJg/builds/vIh1P6lUkRs445jF9/openapi.json
