Shopify Products Catalog Scraper: prices, variants, stock, bulk
Pricing
from $0.65 / 1,000 product listeds
Shopify Products Catalog Scraper: prices, variants, stock, bulk
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.
Pricing
from $0.65 / 1,000 product listeds
Rating
0.0
(0)
Developer
Steadydata Team
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
2 days ago
Last modified
Categories
Share
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.
Why this scraper
- Only delivered results are charged. Inputs that fail come back as clear error records at no cost.
- Straight from the store's own public products feed, 250 products per call, no browser, no start fee. Measured on the platform: 60 products with 502 variants from two stores in 10 seconds for a twentieth of a cent. Priced at a fraction of the alternatives in this niche.
- One complete row per product: title, handle and product URL, vendor, type, tags, created, updated and published dates, currency from the shop profile, price range across variants, compare-at price with an on-sale flag, stock flag with the number of available variants, every variant as an object (id, title, SKU, price, compare-at price, availability, weight, option values), option names, image count, first image and all image URLs, and the description as plain text when asked.
- Robots-aware and merchant-only: each store's robots.txt is read first and a store that closes its feed is skipped for free; the merchant's address and contact data in the shop profile are never delivered.
Who this is for
Paste store domains or any URL on the store in stores (up to 200 per run), set maxProductsPerStore (default 500, ceiling 5,000, newest first), switch includeDescription on when you want the text, and onlyAvailable on to skip sold-out products without paying for them. Built for competitor price and assortment monitoring, catalogue exports into feeds and marketplaces, and stock or discount tracking across many Shopify stores.
Who this is not for
Only stores that publish the standard products feed are covered; a store on another platform, or a Shopify store that has closed the feed, comes back as a free NOT_SHOPIFY row. Some stores rate-limit datacenter traffic (measured: bombas.com answers 429), which is a free BLOCKED row. Availability is what the feed says per variant, not a stock count; Shopify does not publish quantities there. Prices are in the store's own currency, without conversion. Collections, reviews and sales figures are not in the feed and not on the row.
Input fields
| Field | Type | Required or default | What it does |
|---|---|---|---|
stores | list of text | required | One per row, up to 200: the store domain or any URL on it (allbirds.com, https://gymshark.com/collections/all). |
maxProductsPerStore | number | 500 | Cost ceiling per store, in the feed's order (newest first). Pages of 250. |
includeDescription | true/false | false | On: the description as plain text. Off keeps rows small. |
onlyAvailable | true/false | false | On: products where no variant is available are skipped and not charged. |
Input example
{"stores": ["allbirds.com","gymshark.com"],"maxProductsPerStore": 500,"includeDescription": false,"onlyAvailable": false}
Output example
| Field | Type | What it holds | Example |
|---|---|---|---|
store | text | The shop this row was read from. | allbirds.com |
productId | text | Shopify's id for the product, the key to follow it over time. | 7340901859408 |
title | text | The product name as the shop shows it. | Women's Allbirds Flip Flop - Dusty Pink |
handle | text | The shop's own URL slug for the product, stable across renames. | womens-allbirds-flip-flop-dusty-pink |
url | text | The page of this item on the source itself. | https://allbirds.com/products/womens-allbirds-flip-flop-d... |
vendor | text | Filled when the shop fills in a vendor; it is an optional field in Shopify. | Allbirds |
productType | text | Filled when the shop fills in a product type; it is an optional field in Shopify. | Shoes |
tags | list | Filled when the shop tags its products; many shops use collections only. | ["allbirds::carbon-score => undefined", "allbirds::cfId =... |
createdAt | text | When the product was created in the shop, in UTC. | 2026-03-27T21:03:50Z |
updatedAt | text | When the product was last changed in the shop, in UTC. | 2026-09-30T09:08:28Z |
publishedAt | text | When the product became visible in the shop; empty for a product that is not published. | 2026-09-25T23:58:13Z |
currency | text | The shop currency. Shopify publishes one currency per shop in this feed. | USD |
priceMin | number | The lowest variant price; for a product with one variant the same as priceMax. | 25 |
priceMax | number | The highest variant price; for a product with one variant the same as priceMin. | 25 |
compareAtPriceMax | number | The crossed-out price, filled when the shop set one; empty when the product is not discounted. | 50 |
onSale | true/false | True when the crossed-out price is higher than the asking price. | True |
available | true/false | True when at least one variant can be ordered. | True |
variantCount | number | How many variants the product has, for example sizes or colours. | 7 |
availableVariantCount | number | How many of those variants are in stock right now. | 1 |
variants | list | The variants of the product, each with its own name, price and availability. | [{"id": "42146889039952", "title": "5", "sku": "A12513W05... |
optionNames | list | What the variants vary on, for example Size and Colour. | ["Size"] |
imageCount | number | How many images the product carries; one or none usually means a thin listing. | 5 |
imageUrl | text | The first image; images holds all of them. | https://cdn.shopify.com/s/files/1/1104/4168/files/A12513_... |
images | list | The product images, in the order the shop shows them. | ["https://cdn.shopify.com/s/files/1/1104/4168/files/A1251... |
description | text | Filled when includeDescription is on; off by default because it makes rows much larger. |
Error codes: INVALID_STORE, NOT_SHOPIFY, NO_PRODUCTS, ROBOTS_DISALLOWED, FETCH_FAILED, BLOCKED.
One delivered row looks like this:
{"store": "allbirds.com","productId": "7369944137808","title": "Free Returns Coverage","handle": "free-returns-coverage","url": "https://allbirds.com/products/free-returns-coverage","vendor": "re:do","productType": "return,package_protection","tags": ["ATTV_GLOBAL_EXCLUSION_TAG","exclude_rebuy","klaviyo_reviews_exclude","NOREVIEWS","redo-package-protection"],"createdAt": "2026-06-05T14:14:57Z","updatedAt": "2026-09-17T01:45:17Z","publishedAt": "2026-09-02T18:33:11Z","currency": "USD","priceMin": 0.8,"priceMax": 2.98,"compareAtPriceMax": 2.98,"onSale": false,"available": true,"variantCount": 2,"availableVariantCount": 2,"variants": [{"id": "42436493115472","title": "$0.80","sku": "x-redo","price": 0.8,"compareAtPrice": 0.8,"available": true,"grams": 0,"options": ["$0.80"],"requiresShipping": false,"taxable": true},{"id": "42436493148240","title": "$2.98","sku": "x-redo","price": 2.98,"compareAtPrice": 2.98,"available": true,"grams": 0,"options": ["$2.98"],"requiresShipping": false,"taxable": true}],"optionNames": ["Price"],"imageCount": 1,"imageUrl": "https://cdn.shopify.com/s/files/1/1104/4168/files/Screenshot_2026-06-11_at_10.12.01_AM.png?v=1781197934","images": ["https://cdn.shopify.com/s/files/1/1104/4168/files/Screenshot_2026-06-11_at_10.12.01_AM.png?v=1781197934"],"description": null,"status": "ok"}
Related actors from steadydata
- shopify-store-report: one summary row per store instead of every product
- woocommerce-products-catalog: the same catalogue for WooCommerce stores
- google-shopping: what the same products cost at other merchants
Pricing
Pay per event: one product-listed event per delivered result. No charge for inputs
that fail, no separate platform-usage surcharge.
Free Apify plan: this actor delivers up to 25 rows per run for accounts on the Apify free plan, and then stops with a message. That limit is set by us, not by Apify. It exists so the actor keeps paying for itself for the people who do pay. Any paid Apify plan runs it at full size, billed per delivered row, with failed rows never charged.
Reviews: if this actor saves you time, a short review on this page is the one thing that helps most. Ratings are what other buyers look at first, and we have no other way to ask.
FAQ
Is personal data collected? No. Products, prices, stock flags and images of a store are delivered; the merchant's city, province and contact data that the shop profile carries are left out on purpose.
How do I get only discounted products?
Filter on onSale true; compareAtPriceMax against priceMax gives the discount. Combine with onlyAvailable to see what is both discounted and in stock.
Does it get the whole catalogue?
Up to maxProductsPerStore (ceiling 5,000), newest first, in pages of 250. For a larger store run again with a higher ceiling or split by store.
How does this differ from the Shopify store report actor? The report gives one row per store (size, price range, stock share, signals); this actor gives every product with its variants. Use the report to pick stores, this one to read them.
What does a run cost when a store fails?
Nothing. NOT_SHOPIFY, NO_PRODUCTS, ROBOTS_DISALLOWED, INVALID_STORE, FETCH_FAILED and BLOCKED rows are free; only delivered products are charged.
What happens when the source changes? Sources change from time to time; that is the nature of this work. The actor is monitored daily and fixed fast, and while it is broken you are not charged, because only delivered results cost anything.
Can I call it from my own code, like an API? Yes. Every run is available through the Apify API: start it with one HTTP request, then read the results as JSON, CSV or Excel from the dataset endpoint, or use the official Python and JavaScript clients. The input is the same JSON as in the input example above, so a scheduled script, a Make or Zapier step or an AI agent can call it without any changes.