Shopify Store Scraper · Products, Prices & Stock From Any Store
Pricing
from $2.30 / 1,000 product returneds
Shopify Store Scraper · Products, Prices & Stock From Any Store
Shopify store products scraper to extract full product catalogs, prices, variants, discounts and stock from any Shopify store via public /products.json. Scrape product titles, SKUs, vendors, images, pricing and inventory. Ideal for dropshipping research, price monitoring and e-commerce intelligence.
Pricing
from $2.30 / 1,000 product returneds
Rating
0.0
(0)
Developer
Tarek Etman
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
15 hours ago
Last modified
Categories
Share

Shopify Store Products Scraper · Products, Prices & Stock From Any Store
A Shopify store products scraper and Shopify store scraper to extract any Shopify store's full product catalog — every product, price, discount, variant, SKU and stock state — straight from the storefront's public /products.json. Paste any Shopify store domain or collection URL to get structured product data. No login, no cookies, no API key, no headless browser, and no store owner permission needed.
Maintained by reapX. Every row cites the Apify run that produced it — nothing is inferred, modelled or filled in, and a field absent from the source is absent from the row. The extracted archive for this source is browsable at reapx.dev/data/shopify-store-products-scraper/ and mirrored as an open dataset on Hugging Face and Kaggle. Questions: reapxdev@proton.me
Perfect for dropshipping product research, e-commerce competitor analysis, price monitoring, market intelligence, and product catalog synchronization.
What this Shopify store scraper extracts
What this Shopify scraper returns
One row per product, with:
| Field | What it gives you |
|---|---|
title, handle, productUrl, productId | The product and a permanent link to it |
price, maxPrice, currency | Cheapest and dearest variant, in the store's own currency |
compareAtPrice, onSale, discountPercent | The store's own "was" price and how deep the discount runs |
inStock, variantsCount, availableVariantsCount | Live inventory: what is buyable right now |
vendor, productType, tags, options | Merchandising and catalog structure |
publishedAt, daysSincePublished, updatedAt | New-arrival windows and change detection |
sku, imageUrl, imagesCount | Identifiers and creative |
variants[], description | Optional - per-variant price/SKU/stock, and plain-text copy |
Five named dataset views ship with it: Products, Pricing & discounts, Inventory, New arrivals and Full catalog.
How to scrape a Shopify store
- Put one or more domains into Shopify store URLs -
gymshark.com,https://glossier.com, whatever you have. A full URL works just as well as a bare domain. - Optionally narrow to collections - paste
sale,new-arrivals, or the fullhttps://store.com/collections/saleURL. - Set Max products per store. You are charged once per product returned, so this is your price cap.
- Run it. The default configuration returns 100 real products from a live store in under 5 seconds.
Filters that decide what you pay for
Every filter runs before charging, so products you filter out are never billed:
- Price band - minimum and maximum price in the store's currency
- Discounted products only and minimum discount % - sale and markdown monitoring
- In-stock only - drop sold-out products
- Published in the last N days - a new-arrivals feed for any store
- Vendors, product types, tags, title keywords - cohort and category slicing
- Sort by newest, oldest, cheapest, most expensive or biggest discount, then take the top N
What you can build with it
- Competitor price tracking - run daily across a set of rival stores and diff
pricebyproductId. - Discount and promo monitoring -
onSaleOnly+discountDescsurfaces the deepest markdowns in a catalog. - Stock-out alerting - watch
availableVariantsCountfall to zero on the SKUs you care about. - New-product feeds -
newerThanDays: 7plussortBy: newestgives you a weekly launch feed. - Ecommerce lead generation - catalog size, price band and vendor mix qualify a store before you pitch it.
- Market and assortment research - price distribution and product-type mix across a whole vertical.
- Product-feed building - image, title, price and SKU are enough to seed a comparison site or affiliate feed.
Input configuration
Required: storeUrls. Everything else is optional.
Stores & collections
Which storefronts to read. Everything here comes from the store's own public product feed - no login, no cookies, no API key.
| Field | Type | Accepts | What it does |
|---|---|---|---|
storeUrls | list | one value per line | Domains of the Shopify stores to scrape. A bare domain or a full URL both work - https://gymshark.com, gymshark.com and https://gymshark.com/collections/all are all accepted. Prefilled with ["https://gymshark.com"]. |
collectionHandles | list | one value per line | Limit the scrape to specific collections instead of the whole catalogue. Use the handle from the URL or paste the URL itself - "sale", "new-arrivals" or https://gymshark.com/collections/sale all work. Leave empty to scrape every product in the store. Prefilled with []. |
How much to fetch
Volume and ordering. You pay per product returned, so this section decides the price of the run.
| Field | Type | Accepts | What it does |
|---|---|---|---|
maxProductsPerStore | integer | 1 to 20000 | Hard cap on how many products each store returns. This is also your cost cap: you are charged once per product returned. Prefilled with 100. |
sortBy | string | one of 6: none, newest, oldest, priceAsc, ... | How to pick which products you get when the cap is smaller than the catalogue. "Feed order" is the fastest and stops as soon as the cap is met; every other option scans the catalogue first, so it is slower on large stores. Prefilled with "none". |
Filters
All filters are applied before charging - filtered-out products are never billed. Leave a filter empty to ignore it.
| Field | Type | Accepts | What it does |
|---|---|---|---|
minPrice | integer | 0 to any | Keep only products whose lowest variant price is at least this much, in the store's own currency. |
maxPrice | integer | 0 to any | Keep only products whose lowest variant price is at most this much, in the store's own currency. |
onSaleOnly | true/false | checkbox | Keep only products where a compare-at price is higher than the current price - the store's own definition of "on sale". |
minDiscountPercent | integer | 1 to 99 | Keep only products discounted by at least this percentage off the compare-at price. |
inStockOnly | true/false | checkbox | Keep only products with at least one variant the store currently marks as available to buy. |
newerThanDays | integer | 1 to 3650 | Keep only products published to the storefront within this many days - the new-arrivals window. |
vendors | list | one value per line | Keep only products from these vendors (exact, case-insensitive match on the store's vendor field). |
productTypes | list | one value per line | Keep only products of these types (exact, case-insensitive match on the store's product type field). |
requiredTags | list | one value per line | Keep only products carrying at least one of these storefront tags. |
titleKeywords | list | one value per line | Keep only products whose title contains at least one of these words or phrases (case-insensitive). |
Output detail
Extra columns. These change the row size, not the price.
| Field | Type | Accepts | What it does |
|---|---|---|---|
includeVariants | true/false | checkbox | Add a variants array to each row with per-variant id, SKU, price, compare-at price, availability and option values. Off by default to keep rows small. |
includeDescription | true/false | checkbox | Add the product description as plain text, stripped of HTML and capped at 5,000 characters. |
Network
Only touch this if a store blocks the default direct requests.
| Field | Type | Accepts | What it does |
|---|---|---|---|
proxyConfiguration | object | free text | Optional. Most Shopify storefronts answer fine without a proxy, so this is off by default. Turn it on if a specific store returns 403 or 429 for the datacenter that runs the actor. Prefilled with {"useApifyProxy": false}. |
Pricing
Pay per event. The primary event is Product returned (product-returned), charged at $0.0035 per event on the free plan, falling to $0.0014 on the highest tier.
Charged once per complete product row written to the default dataset. Blocked stores, non-Shopify hosts, empty feeds and products removed by your filters are never charged.
Minimum charge cap per run: $0.0035.
Tiered discounts apply automatically on every paid Apify plan. Platform usage is absorbed by the Actor, so the per-event price is the whole price.
Usage examples
The prefilled run, which is what Start does with nothing changed
{"storeUrls": ["https://gymshark.com"],"maxProductsPerStore": 100,"sortBy": "none","onSaleOnly": false,"inStockOnly": false,"includeVariants": false,"includeDescription": false,"proxyConfiguration": {"useApifyProxy": false}}
Filter by collections only (optional)
{"storeUrls": ["https://gymshark.com"],"maxProductsPerStore": 100,"sortBy": "none","onSaleOnly": false,"inStockOnly": false,"includeVariants": false,"includeDescription": false,"proxyConfiguration": {"useApifyProxy": false},"collectionHandles": ["sale","new-arrivals"]}
Filter by sort products by
{"storeUrls": ["https://gymshark.com"],"maxProductsPerStore": 100,"sortBy": "discountDesc","onSaleOnly": false,"inStockOnly": false,"includeVariants": false,"includeDescription": false,"proxyConfiguration": {"useApifyProxy": false}}
Filter by minimum price
{"storeUrls": ["https://gymshark.com"],"maxProductsPerStore": 100,"sortBy": "none","onSaleOnly": false,"inStockOnly": false,"includeVariants": false,"includeDescription": false,"proxyConfiguration": {"useApifyProxy": false},"minPrice": 25}
Filter by maximum price
{"storeUrls": ["https://gymshark.com"],"maxProductsPerStore": 100,"sortBy": "none","onSaleOnly": false,"inStockOnly": false,"includeVariants": false,"includeDescription": false,"proxyConfiguration": {"useApifyProxy": false},"maxPrice": 100}
Output example
One row, exactly as the actor wrote it to the dataset:
{"storeDomain": "gymshark.com","storeName": "Gymshark US","currency": "USD","productId": "6806953033930","title": "Gymshark Everyday Seamless Zip Tracktop - Onyx Grey","handle": "gymshark-everyday-seamless-zip-tracktop-jackets-outerwear-grey-aw26","productUrl": "https://gymshark.com/products/gymshark-everyday-seamless-zip-tracktop-jackets-outerwear-grey-aw26","vendor": "Gymshark | Be a visionary.","productType": "Womens Jackets / Outerwear","tags": ["06/08/26","30/7/26-everyday-seamless","activity:Conditioning","akeneo_updated_at:1785528453","all-products","build-your-wishlist","category:Jackets / Outerwear","conditioning","created_by:product-service","division:Apparel","everyday","everyday-seamless","feature: seamless","feature:lightweight","filter-colour:Grey","garmentLength:Midi","get-the-look:B1B7P-GB7N","get-the-look:B4B8H-GB7N","get-the-look:B6B1R-WB57","get-the-look:B7A3L-GB7N","get-the-look:B7C2L-YBZ5","get-the-look:I2C5D-WB57","INTL-06/08/26","lower-bucket","most-loved","must-have","NAM-06/08/26","new-releases","outerwear","outlet","over-40-off","over-50-off","over-60-off","over-70-off","percentageInStockV2","range:Everyday Seamless","seamtype:Seamless","season:AW26","segmentation:Conditioning","sizeguide:top","subcategory:Track Top","test","tier:3","top-rated","tracksuits","trading","winback-codes","Womens","zip-jackets"],"price": 42,"maxPrice": 42,"compareAtPrice": null,"onSale": false,"discountPercent": null,"inStock": true,"variantsCount": 7,"availableVariantsCount": 7}
That row carries 29 fields in total; 18 are shown.
Fields on every row
| Field | Type | What it is |
|---|---|---|
storeDomain | string | Host of the Shopify storefront this product was read from. |
storeName | string | Shop name as published by the storefront. Null when the store hides its meta endpoint. |
currency | string | ISO 4217 currency the storefront prices in. All price fields on the row use it. |
collectionHandle | string | Collection the product was read from. Present only when the run was scoped to collections. |
productId | string | Shopify product ID, unique within the store. Stable across runs. |
title | string | Product name exactly as shown on the storefront. |
handle | string | URL slug of the product within the store. |
productUrl | string | Canonical storefront URL for the product page. |
vendor | string | Brand or vendor the store assigns to the product. |
productType | string | Store's own product type / category label. |
tags | list | Storefront tags on the product. Often carries merchandising signals such as new-releases or sale. |
price | number | Lowest current variant price, in the store's currency. |
maxPrice | number | Highest current variant price. Equal to price when every variant costs the same. |
compareAtPrice | number | Highest compare-at (was) price across variants. Null when the store publishes none. |
onSale | true/false | True when the compare-at price is above the current price. |
discountPercent | number | Percent off the compare-at price. Null when the product is not discounted. |
inStock | true/false | True when at least one variant is currently available to buy. |
variantsCount | integer | Number of variants published for the product. |
availableVariantsCount | integer | How many of those variants the store currently marks as available. |
sku | string | SKU of the first variant that publishes one. Null when the store hides SKUs. |
options | list | Option axes and their values, e.g. Size and Colour. |
imageUrl | string | First product image published by the store. |
imagesCount | integer | Number of images on the product. |
publishedAt | string | UTC ISO 8601 timestamp the product went live on the storefront. |
createdAt | string | UTC ISO 8601 timestamp the product record was created. |
updatedAt | string | UTC ISO 8601 timestamp of the store's last edit to the product. |
daysSincePublished | integer | Whole days between publication and this scrape. Use it to size a new-arrivals window. |
description | string | Plain-text product description, HTML stripped, capped at 5,000 characters. Present only when "Include the product description" is on. |
variants | list | Per-variant id, title, SKU, price, compare-at price, availability, grams and option values. Present only when "Include the full variant list" is on. |
sourceUrl | string | Exact public JSON endpoint this row came from. |
scrapedAt | string | UTC ISO 8601 timestamp of this run. |
5 named dataset views ship with it: Products, Pricing & discounts, Inventory, New arrivals, Full catalog. They drive the Output tab in Console and the Output block on the Actor's .md page.
FAQ
How do I scrape products from a Shopify store?
Put the store domain into Shopify store URLs and run. The actor reads
https://{store}/products.json, the public product feed Shopify serves on every storefront, and pages
through it until it hits your product cap.
Do I need an API key, a login or the store owner's permission?
No. This reads only the public storefront JSON that Shopify publishes for every store - the same data your browser can fetch. There is no authentication step and no private data involved.
How do I know if a site is a Shopify store?
Open https://thesite.com/products.json in a browser. If you get JSON with a products array, this
actor will scrape it. If you get a 404 or an HTML page, the site is not a public Shopify storefront and
the run reports it and charges you nothing for it.
Can I scrape only one collection or category?
Yes. Add the collection handle - sale, new-arrivals, mens-tops - or paste the full
/collections/... URL into Collections only. Multiple collections per run are supported.
Can I scrape several Shopify stores in one run?
Yes. Shopify store URLs takes a list, and Max products per store applies to each store independently, so a 10-store run at 200 products each returns up to 2,000 rows.
How do I track Shopify price changes over time?
Schedule the actor daily with the same input and compare price and compareAtPrice on matching
productId values between dataset runs. updatedAt tells you when the store itself last touched the
product.
How do I find products that are on sale?
Turn on Discounted products only, optionally set Minimum discount %, and sort by
Biggest discount first. Rows come back ranked by discountPercent.
Does it return stock levels?
It returns availability, not counts. Shopify's public feed publishes a per-variant available flag but
not exact inventory numbers, so you get inStock, variantsCount and availableVariantsCount - which
is enough to detect sell-outs and size-run gaps. Exact quantities exist only in the private admin API.
How fast is it and what does it cost?
The feed returns 250 products per request, so 1,000 products is four HTTP requests. A 100-product default run finishes in a few seconds and costs $0.35 at the Free-tier rate, less on every paid plan.
What happens if a store blocks the request?
The run backs off and retries on 429 and 5xx responses, respecting Shopify's storefront rate limit. If the store still refuses, that store is reported in the status message and charged at zero. Turn on Proxy if one specific store blocks the datacenter the actor runs in.
Can an AI agent call this actor?
Yes. Every input field and every output field carries a title, description and example, so an agent can fill the input and read the dataset without extra prompting.
Unofficial - not affiliated with Shopify. Collects public data only. reapx. Contact reapxdev@proton.me.
The full published archive
- shopify store products scraper archive — every entity this Actor has observed
- All reapx datasets — 20 sources, tens of thousands of pages
- llms.txt · feed.json · feed.xml — the machine-readable index, for agents
Each archive page carries Dataset JSON-LD, a canonical URL and the identifiers used, so an agent can resolve an entity without running anything. Nothing on those pages is estimated or modelled.
🧪 Example input
A real, runnable configuration — this is an actual input this Actor has run with.
{"storeUrls": ["https://gymshark.com","https://www.taylorstitch.com","https://outdoorvoices.com"],"includeVariants": true,"maxProductsPerStore": 40,"sortBy": "none","onSaleOnly": false,"inStockOnly": false,"includeDescription": false,"proxyConfiguration": {"useApifyProxy": false}}
📄 Sample output
One real row from a real run of this Actor, unedited.
{"storeDomain": "gymshark.com","storeName": "Gymshark US","currency": "USD","productId": "6806953033930","title": "Gymshark Everyday Seamless Zip Tracktop - Onyx Grey","handle": "gymshark-everyday-seamless-zip-tracktop-jackets-outerwear-grey-aw26","productUrl": "https://gymshark.com/products/gymshark-everyday-seamless-zip-tracktop-jackets-outerwear-grey-aw26","vendor": "Gymshark | Be a visionary.","productType": "Womens Jackets / Outerwear","tags": ["06/08/26","30/7/26-everyday-seamless","activity:Conditioning","akeneo_updated_at:1785528453","all-products","build-your-wishlist","category:Jackets / Outerwear","conditioning","created_by:product-service","division:Apparel","everyday","everyday-seamless","feature: seamless","feature:lightweight","filter-colour:Grey","garmentLength:Midi","get-the-look:B1B7P-GB7N","get-the-look:B4B8H-GB7N","get-the-look:B6B1R-WB57","get-the-look:B7A3L-GB7N","get-the-look:B7C2L-YBZ5","get-the-look:I2C5D-WB57","INTL-06/08/26","lower-bucket","most-loved","must-have","NAM-06/08/26","new-releases","outerwear","outlet","over-40-off","over-50-off","over-60-off","over-70-off","percentageInStockV2","range:Everyday Seamless","seamtype:Seamless","season:AW26","segmentation:Conditioning","sizeguide:top","subcategory:Track Top","test","tier:3","top-rated","tracksuits","trading","winback-codes","Womens","zip-jackets"],"price": 42,"maxPrice": 42,"compareAtPrice": null,"onSale": false,"discountPercent": null,"inStock": true,"variantsCount": 7,"availableVariantsCount": 7,"sku": "B1B7P-GB7N-S","options": [{"name": "Size","values": ["Small","Extra Small","Extra Large","Large","Extra Extra Small","Medium","Extra Extra Large"]}],"imageUrl": "https://cdn.shopify.com/s/files/1/0156/6146/files/EverydaySeamlessZipTracktopGSOnyxGreyB1B7P_GB7N_4180.jpg?v=1784724702","imagesCount": 6,"publishedAt": "2026-07-30T17:50:00Z","createdAt": "2026-05-13T09:46:05Z","updatedAt": "2026-08-02T23:14:07Z","daysSincePublished": 3,"sourceUrl": "https://gymshark.com/products.json?limit=250&page=1","scrapedAt": "2026-08-02T23:14:08.011644Z","variants": [{"variantId": "39799577903306","title": "Small","sku": "B1B7P-GB7N-S","price": 42,"compareAtPrice": null,"available": true,"grams": 0,"option1": "Small","option2": null,"option3": null},{"variantId": "39799577968842","title": "Extra Small","sku": "B1B7P-GB7N-XS","price": 42,"compareAtPrice": null,"available": true,"grams": 0,"option1": "Extra Small","option2": null,"option3": null},{"variantId": "39799582097610","title": "Extra Large","sku": "B1B7P-GB7N-XL","price": 42,"compareAtPrice": null
How it works
- You set the filters below, or none at all.
- The Actor calls the source's own public endpoint and pages through the results.
- Every row is pushed to the dataset as it is built, not buffered to the end — so a run that hits its time limit still returns everything it collected up to that point.
- You are charged per row returned, so the maximum-results field is also your cost cap.
💬 Your feedback
Found a bug, or need a field this does not return yet? Open an issue on the Actor's Issues tab, or write to reapxdev@proton.me. Bugs get fixed and reasonable field requests get added.