# Shopify Store Scraper (`thenetaji/shopify-store-scraper`) Actor

Export every product and variant from any Shopify-powered store by domain, read straight from the store's own JSON API rather than scraped HTML.

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

## Pricing

from $0.60 / 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.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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 Store Scraper

The Actor exports every product and variant from any Shopify-powered store by domain. It reads the store's own `/collections.json` and `/collections/<handle>/products.json` endpoints directly, so no HTML is parsed and no product page is rendered.

This scrapes product catalogs from stores built on Shopify. It does not read the Shopify App Store (apps.shopify.com); an app listing and a store's product catalog are different documents, and this Actor only reads the latter.

### Accepted input

| Field | Type | Default | Description |
|---|---|---|---|
| `storeUrl` | string | — | Required. A domain or URL of a Shopify-powered store. |
| `maxItems` | integer | `0` | Maximum product variants saved. `0` removes the limit. |
| `proxyConfig` | object | Residential Apify Proxy | Proxy used for every request. Residential by default — most storefronts block or rate-limit datacenter IPs outright. |
| `includeRawData` | boolean | `false` | Adds `handle`, every product image, option definitions, and per-variant `grams`/`taxable`/`requiresShipping`/`publishedAt`. Already present in the same response, so this costs nothing extra to turn on. |

```json
{
  "storeUrl": "allbirds.com",
  "maxItems": 500
}
```

### Response fields

One record per product variant.

| Field | Contents |
|---|---|
| `name` | Product title, with the variant title appended when it isn't the default one |
| `price` | Variant price, exactly as the store's JSON returns it |
| `category` | Collection the product was found in, title-cased |
| `brand` | Product vendor |
| `identifier` | Variant id |
| `sku` | Variant SKU, when the store sets one |
| `stock` | `0` when the variant is out of stock; `null` when it's available |
| `image_url` | First product image |
| `url` | The product's page inside the collection it was found in |
| `description` | Product description with HTML tags stripped |
| `old_price` | Pre-sale price, when the store sets a real compare-at price |
| `barcode` | Variant barcode, when the store's product JSON exposes it |
| `product_type` | The store's product type for this product |
| `tags` | The store's tags for this product |
| `handle` | The product's URL slug. Only present when `includeRawData` is on |
| `images` | Every product image, not just the first. Only present when `includeRawData` is on |
| `options` | The product's option definitions (e.g. Size, Color) and their values. Only present when `includeRawData` is on |
| `grams` | Variant weight in grams. Only present when `includeRawData` is on |
| `taxable` | Whether the variant is taxable. Only present when `includeRawData` is on |
| `requires_shipping` | Whether the variant requires shipping. Only present when `includeRawData` is on |
| `published_at` | When the product was published to the store. Only present when `includeRawData` is on |

```json
{
  "name": "Wool Runner - Natural Black / Dark Grey",
  "price": "98.00",
  "category": "Mens Shoes",
  "brand": "Allbirds",
  "sku": "",
  "stock": null,
  "url": "https://www.allbirds.com/collections/mens-shoes/products/mens-wool-runner-natural-black"
}
```

### How collection is scoped

The store's collections are paged first, then each collection that reports at least one product is paged for its products. A product's variants are yielded once each; a variant that appears in more than one collection is written only the first time it's seen, not once per collection.

A collection's advertised product count can be higher than what actually comes back — the endpoint only exposes products published to the store's online-store sales channel, and a nonzero count is read as "this collection has something to fetch," not as a promise of that many results.

### Behaviour on partial results and limits

`maxItems` stops the run once at least that many variants have been saved, not the instant the limit is reached — a request already in flight when the limit is hit is still allowed to finish, so the final count can land a little past `maxItems` rather than cutting off mid-request.

Stores behind extra bot protection commonly block datacenter IPs outright and rate-limit everything else, which is why Residential Apify Proxy is on by default. Password-protected storefronts still don't work regardless of proxy: the Actor reads the storefront's public JSON API directly, and a store that requires a login has no public surface for it to read.

### Frequently asked questions

**Does this scrape the Shopify App Store?**
No. apps.shopify.com lists apps for merchants to install; this Actor reads a store's product catalog through its storefront JSON API. The two are unrelated documents on unrelated domains.

**Why did I get fewer results than the store's collections suggest?**
A collection's `products_count` includes products not published to the online store, which the JSON endpoints this Actor reads don't expose. The count is a ceiling, not a guarantee.

**Are the same product variants returned more than once if they sit in several collections?**
No. Each variant is saved once, on the first collection it's found in, even if the store lists it under several.

**Does `maxItems` cut the run off at exactly that number?**
Not to the item. The run stops once the count reaches or slightly passes `maxItems`, because a request already in flight when the limit is hit is allowed to complete rather than being discarded mid-request.

**Will this work on a password-protected store?**
No. The Actor reads public JSON endpoints with no browser session and no login; a store that requires one has nothing public for it to read.

**Why does this need a proxy, and why residential specifically?**
Most Shopify storefronts block or rate-limit plain JSON API requests, and many block known datacenter IP ranges outright regardless of request pattern. Residential Apify Proxy is enabled by default for that reason; switching it to datacenter or turning it off is likely to get blocked on stores with any real bot protection.

**The store's own `products.json` has more fields than this Actor returns — where did they go?**
Nowhere; they're not fetched selectively, they're just not included in the default row shape. The store's JSON response carries the full product object — every image, all option definitions, per-variant shipping/tax flags, timestamps, and more — and this Actor reads that same response but writes out a fixed, commonly-needed set of fields per variant. Turn on `includeRawData` to get the rest; it's already part of the same response, so nothing extra is fetched or charged for it.

# Actor input Schema

## `storeUrl` (type: `string`):

Domain or URL of any Shopify-powered store to scrape, e.g. allbirds.com.

## `maxItems` (type: `integer`):

Maximum number of product variants to save. Set 0 for no limit.

## `proxyConfig` (type: `object`):

Most Shopify stores block or rate-limit datacenter IPs; Residential proxy is used by default.

## `includeRawData` (type: `boolean`):

Add the product's handle, all images (not just the first), option definitions, and per-variant grams/taxable/requires shipping/publish date. All of this already comes back in the same request, so turning it on costs nothing extra.

## Actor input object example

```json
{
  "storeUrl": "allbirds.com",
  "maxItems": 100,
  "proxyConfig": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  },
  "includeRawData": false
}
```

# Actor output Schema

## `dataset` (type: `string`):

Dataset containing all scraped data

# 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 = {
    "storeUrl": "allbirds.com",
    "maxItems": 100
};

// Run the Actor and wait for it to finish
const run = await client.actor("thenetaji/shopify-store-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 = {
    "storeUrl": "allbirds.com",
    "maxItems": 100,
}

# Run the Actor and wait for it to finish
run = client.actor("thenetaji/shopify-store-scraper").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 '{
  "storeUrl": "allbirds.com",
  "maxItems": 100
}' |
apify call thenetaji/shopify-store-scraper --silent --output-dataset

```

## MCP server setup

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

```

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/Y615yjXzcd50h3CZc/builds/9MZXXVWXePkPJeLen/openapi.json
