Product catalogue of any Shopify store from its public products feed, up to 200 stores a run and 5,000 products each: title, handle, URL, vendor, type, tags, dates, price range, sale and stock flags, every variant with SKU, price and options, images and description. Robots-aware. Pay per product.
One product delivered with its variants. Stores that are not on Shopify, closed by robots.txt or without products are never charged.
ACTOR=steadydata~shopify-products-catalog URL="https://api.apify.com/v2/acts/$ACTOR/run-sync-get-dataset-items" curl -X POST "$URL?token=$APIFY_TOKEN" \ -H "Content-Type: application/json" \ -d '{"stores": ["allbirds.com", "gymshark.com"], "maxProductsPerStore": 500, "includeDescription": false}'
import os, requests ACTOR = "steadydata~shopify-products-catalog" URL = f"https://api.apify.com/v2/acts/{ACTOR}/run-sync-get-dataset-items" rows = requests.post( URL, params={"token": os.environ["APIFY_TOKEN"]}, json={'stores': ['allbirds.com', 'gymshark.com'], 'maxProductsPerStore': 500, 'includeDescription': False}, timeout=900, ).json() # every row carries a status; failures are records, not exceptions ok = [r for r in rows if r.get("status") == "ok"] print(len(ok), "rows delivered")
const ACTOR = "steadydata~shopify-products-catalog"; const url = `https://api.apify.com/v2/acts/${ACTOR}/run-sync-get-dataset-items` + `?token=${process.env.APIFY_TOKEN}`; const rows = await fetch(url, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({"stores": ["allbirds.com", "gymshark.com"], "maxProductsPerStore": 500, "includeDescription": false}), }).then((r) => r.json()); // one object per delivered row, same shape every time console.log(rows.filter((r) => r.status === "ok").length, "rows");
Point an MCP client at Apify's hosted server with this dataset in the tools list, or run our own server locally. The field explanations in the table below travel with the schema, so your agent reads them straight from the platform instead of from this page.
https://mcp.apify.com?tools=steadydata/shopify-products-catalog # the tool arrives in your agent as steadydata--shopify-products-catalog
| Field | Type | Name | What it does |
|---|---|---|---|
storesrequired | array of string | Shopify stores | One per row, up to 200: the store domain or any URL on it (allbirds.com, https://gymshark.com/collections/all). |
maxProductsPerStore | integer | Max products per store | Cost ceiling per store, in the feed's order (newest first). Pages of 250. |
includeDescription | boolean | Include the product description | On: the description as plain text. Off keeps rows small. |
onlyAvailable | boolean | Only products with stock | On: products where no variant is available are skipped and not charged. |
{
"store": string | null,
"productId": string | null,
"title": string | null,
"handle": string | null,
"url": string | null,
"vendor": string | null,
"productType": string | null,
"tags": array | null,
"createdAt": string | null,
"updatedAt": string | null,
"publishedAt": string | null,
"currency": string | null,
"priceMin": number | null,
"priceMax": number | null,
"compareAtPriceMax": number | null,
"onSale": boolean | null,
"available": boolean | null,
"variantCount": integer | null,
"availableVariantCount": integer | null,
"variants": array | null,
"optionNames": array | null,
"imageCount": integer | null,
"imageUrl": string | null,
"images": array | null,
"description": string | null,
"status": string | null,
"input": string | null,
"errorCode": string | null,
"error": string | null
}| Field | Type | When it is filled |
|---|---|---|
store | string, null | |
productId | string, null | |
title | string, null | |
handle | string, null | The shop's own URL slug for the product, stable across renames. |
url | string, null | |
vendor | string, null | Filled when the shop fills in a vendor; it is an optional field in Shopify. |
productType | string, null | Filled when the shop fills in a product type; it is an optional field in Shopify. |
tags | array, null | Filled when the shop tags its products; many shops use collections only. |
createdAt | string, null | When the product was created in the shop, in UTC. |
updatedAt | string, null | When the product was last changed in the shop, in UTC. |
publishedAt | string, null | When the product became visible in the shop; empty for a product that is not published. |
currency | string, null | The shop currency. Shopify publishes one currency per shop in this feed. |
priceMin | number, null | The lowest variant price; for a product with one variant the same as priceMax. |
priceMax | number, null | The highest variant price; for a product with one variant the same as priceMin. |
compareAtPriceMax | number, null | The crossed-out price, filled when the shop set one; empty when the product is not discounted. |
onSale | boolean, null | True when the crossed-out price is higher than the asking price. |
available | boolean, null | |
variantCount | integer, null | How many variants the product has, for example sizes or colours. |
availableVariantCount | integer, null | How many of those variants are in stock right now. |
variants | array, null | |
optionNames | array, null | What the variants vary on, for example Size and Colour. |
imageCount | integer, null | |
imageUrl | string, null | The first image; images holds all of them. |
images | array, null | |
description | string, null | Filled when includeDescription is on; off by default because it makes rows much larger. |
status | string, null | Either 'ok' or 'error'. An error row carries errorCode and error, and leaves the data fields empty; it is never charged. |
input | string, null | The input this row was built from, so a row can always be traced back. |
errorCode | string, null | Filled on an error row only; a delivered row leaves it empty. |
error | string, null | Filled on an error row only; a delivered row leaves it empty. |
One report per Shopify store, up to 200 stores per run: catalogue size, price range and average, currency, share in stock, share on sale, product types, brands, newest and oldest product, plus ranked signals such as a stale catalogue or heavy discounting. Pay per delivered report.
$3.50 per 1,000 · Store reportedProduct catalogue of any WooCommerce store from its public Store API, up to 200 stores a run and 5,000 products each: name, URL, SKU, price, sale price, currency, stock and on-sale flags, rating, categories, tags, brands, attributes, images and description. Robots-aware. Pay per product.
$1.00 per 1,000 · Product listedOne report per WooCommerce store, up to 200 stores per run: the exact product count, price range and average, currency, share in stock, share on sale, categories, brands, average review rating, and ranked signals such as an empty catalogue or heavy discounting. Company data only, no personal data.
$3.50 per 1,000 · Store reportedAlready using this one? Ratings are the first thing other buyers look at, and this dataset has none yet. If it does a job for you, a rating on its Apify page is the one thing that helps. It takes a minute and it is the only thing we ask for.