Shopify Store Scraper · Products, Prices & Stock From Any Store avatar

Shopify Store Scraper · Products, Prices & Stock From Any Store

Pricing

from $2.30 / 1,000 product returneds

Go to Apify Store
Shopify Store Scraper · Products, Prices & Stock From Any Store

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

Tarek Etman

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

an hour ago

Last modified

Share

reapX — public sources in, addressable records out

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:

FieldWhat it gives you
title, handle, productUrl, productIdThe product and a permanent link to it
price, maxPrice, currencyCheapest and dearest variant, in the store's own currency
compareAtPrice, onSale, discountPercentThe store's own "was" price and how deep the discount runs
inStock, variantsCount, availableVariantsCountLive inventory: what is buyable right now
vendor, productType, tags, optionsMerchandising and catalog structure
publishedAt, daysSincePublished, updatedAtNew-arrival windows and change detection
sku, imageUrl, imagesCountIdentifiers and creative
variants[], descriptionOptional - 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

  1. 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.
  2. Optionally narrow to collections - paste sale, new-arrivals, or the full https://store.com/collections/sale URL.
  3. Set Max products per store. You are charged once per product returned, so this is your price cap.
  4. 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 price by productId.
  • Discount and promo monitoring - onSaleOnly + discountDesc surfaces the deepest markdowns in a catalog.
  • Stock-out alerting - watch availableVariantsCount fall to zero on the SKUs you care about.
  • New-product feeds - newerThanDays: 7 plus sortBy: newest gives 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.

FieldTypeAcceptsWhat it does
storeUrlslistone value per lineDomains 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"].
collectionHandleslistone value per lineLimit 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.

FieldTypeAcceptsWhat it does
maxProductsPerStoreinteger1 to 20000Hard cap on how many products each store returns. This is also your cost cap: you are charged once per product returned. Prefilled with 100.
sortBystringone 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.

FieldTypeAcceptsWhat it does
minPriceinteger0 to anyKeep only products whose lowest variant price is at least this much, in the store's own currency.
maxPriceinteger0 to anyKeep only products whose lowest variant price is at most this much, in the store's own currency.
onSaleOnlytrue/falsecheckboxKeep only products where a compare-at price is higher than the current price - the store's own definition of "on sale".
minDiscountPercentinteger1 to 99Keep only products discounted by at least this percentage off the compare-at price.
inStockOnlytrue/falsecheckboxKeep only products with at least one variant the store currently marks as available to buy.
newerThanDaysinteger1 to 3650Keep only products published to the storefront within this many days - the new-arrivals window.
vendorslistone value per lineKeep only products from these vendors (exact, case-insensitive match on the store's vendor field).
productTypeslistone value per lineKeep only products of these types (exact, case-insensitive match on the store's product type field).
requiredTagslistone value per lineKeep only products carrying at least one of these storefront tags.
titleKeywordslistone value per lineKeep 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.

FieldTypeAcceptsWhat it does
includeVariantstrue/falsecheckboxAdd 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.
includeDescriptiontrue/falsecheckboxAdd 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.

FieldTypeAcceptsWhat it does
proxyConfigurationobjectfree textOptional. 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

FieldTypeWhat it is
storeDomainstringHost of the Shopify storefront this product was read from.
storeNamestringShop name as published by the storefront. Null when the store hides its meta endpoint.
currencystringISO 4217 currency the storefront prices in. All price fields on the row use it.
collectionHandlestringCollection the product was read from. Present only when the run was scoped to collections.
productIdstringShopify product ID, unique within the store. Stable across runs.
titlestringProduct name exactly as shown on the storefront.
handlestringURL slug of the product within the store.
productUrlstringCanonical storefront URL for the product page.
vendorstringBrand or vendor the store assigns to the product.
productTypestringStore's own product type / category label.
tagslistStorefront tags on the product. Often carries merchandising signals such as new-releases or sale.
pricenumberLowest current variant price, in the store's currency.
maxPricenumberHighest current variant price. Equal to price when every variant costs the same.
compareAtPricenumberHighest compare-at (was) price across variants. Null when the store publishes none.
onSaletrue/falseTrue when the compare-at price is above the current price.
discountPercentnumberPercent off the compare-at price. Null when the product is not discounted.
inStocktrue/falseTrue when at least one variant is currently available to buy.
variantsCountintegerNumber of variants published for the product.
availableVariantsCountintegerHow many of those variants the store currently marks as available.
skustringSKU of the first variant that publishes one. Null when the store hides SKUs.
optionslistOption axes and their values, e.g. Size and Colour.
imageUrlstringFirst product image published by the store.
imagesCountintegerNumber of images on the product.
publishedAtstringUTC ISO 8601 timestamp the product went live on the storefront.
createdAtstringUTC ISO 8601 timestamp the product record was created.
updatedAtstringUTC ISO 8601 timestamp of the store's last edit to the product.
daysSincePublishedintegerWhole days between publication and this scrape. Use it to size a new-arrivals window.
descriptionstringPlain-text product description, HTML stripped, capped at 5,000 characters. Present only when "Include the product description" is on.
variantslistPer-variant id, title, SKU, price, compare-at price, availability, grams and option values. Present only when "Include the full variant list" is on.
sourceUrlstringExact public JSON endpoint this row came from.
scrapedAtstringUTC 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

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

  1. You set the filters below, or none at all.
  2. The Actor calls the source's own public endpoint and pages through the results.
  3. 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.
  4. 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.