Shopify Products Data, Shopify Collection Scraper, Shopify CSV
Pricing
from $0.85 / 1,000 products
Shopify Products Data, Shopify Collection Scraper, Shopify CSV
Every product from any Shopify store or collection: prices, variants, SKUs, barcodes, stock counts, availability, images, tags. Filter by price, sale, brand, type or stock. Overlapping collections deduped: one row, one charge per product. Watch a store: drops, restocks, new, delisted. No browser.
Pricing
from $0.85 / 1,000 products
Rating
0.0
(0)
Developer
Fetch Smith
Maintained by CommunityActor stats
0
Bookmarked
3
Total users
0
Monthly active users
2 days ago
Last modified
Categories
Share
Shopify Products Data Scraper
Extract the full product catalog of any Shopify store (or a single collection or product) into JSON, CSV or Excel: title, vendor, type, tags, prices, compare-at prices, availability, images, options, variants with SKUs, and description. Works on any Shopify storefront, no login, no browser. Pay only per product returned.
It doubles as a Shopify catalog API and a general product feed API: query any storefront's public JSON feed and get back a clean, per-product priced dataset. Pull Shopify inventory data (stock counts, barcodes, quantities) or set up ongoing Shopify competitor monitoring for price drops, restocks and new launches.
Use cases
- Competitor price and assortment monitoring
- Building product feeds, catalogs and dropshipping research datasets
- Tracking new launches and stock changes on a schedule
- Feeding product data into AI agents and recommendation systems
Input
| Field | Type | Description |
|---|---|---|
storeUrls | array | Store homepages, /collections/<handle> URLs, or /products/<handle> URLs |
searchQuery | string | Only keep products whose title, vendor, product type or tags contain every word in the query (case-insensitive). Applied client-side after fetching — Shopify's public feed has no server-side search — so it still scans the whole store at the same request cost as an unfiltered run, but needs no collection URL guessing. Ignored on direct product URLs. |
vendors | array | Only keep products whose vendor/brand contains one of these (case-insensitive OR — e.g. ["Allbirds","Rothy's"]). Combines with every other filter via AND. Ignored on direct product URLs. |
productTypes | array | Only keep products whose product type contains one of these (case-insensitive OR), e.g. ["Shoes","Sandals"]. Combines with every other filter via AND. Ignored on direct product URLs. |
maxProductsPerStore | integer | Cap per store (default 500) |
includeDescription | boolean | Plain-text description (default true) |
includeVariants | boolean | Full variants array with SKU, price, options, availability (default true) |
onlyAvailable | boolean | Skip sold-out products |
minPrice | integer | Only keep products with a variant at or above this price (matches when the product's own price range overlaps the window — a $40-$120 hoodie matches minPrice: 100). Store's own currency, no FX conversion. Filtered-out products are never charged. |
maxPrice | integer | Only keep products with a variant at or below this price (same range-overlap rule). Store's own currency, no FX conversion. Filtered-out products are never charged. |
onSaleOnly | boolean | Only keep products currently discounted — a compare-at price set above the current price, the same condition as the isOnSale output field. Filtered-out products are never charged. |
minDiscountPercent | integer | Only keep products discounted by at least this percentage (checks the discountPercent output field). Stricter than onSaleOnly, which accepts any discount; products with no discount at all (discountPercent: null) are excluded, not treated as 0%. Filtered-out products are never charged. |
maxResults | integer | Total cap |
proxyConfiguration | object | Route storefront requests through Apify Proxy (default on). Use a country-specific residential group to read that country's prices and currency, or to get past IP rate-limiting. Accounts without proxy access fall back to a direct connection instead of failing. |
detailLevel | string | "basic" (default) reads only the products feed. "full" also fetches each product's live page and its per-product .js route for barcode, inventoryQuantity, inventoryManagement, inventoryPolicy, quantityRule, totalInventory, seoTitle, seoDescription, ratingValue, reviewCount, hasSubscriptionOption and subscriptionPlans — none of which the bulk products.json feed carries — priced as one extra event per product (see Pricing). Both requests run in parallel, so it costs no extra wall-clock. |
watchLabel | string | Turn the run into a catalog watch. The first run under a label records every product's price, availability and sale status and returns nothing (charges nothing); later runs under the same label return only products that are new, changed price, started/ended a sale, changed stock status, or have been removed from the catalog. See Watch mode below. |
watchEvents | array | Restrict a watch to some change types only: new, priceDrop, priceIncrease, wentOnSale, saleEnded, backInStock, outOfStock, delisted. Empty (default) reports all eight. wentOnSale/saleEnded fire only when a compare-at price is added/removed with the current price unchanged — a price move that also flips the sale flag is reported as priceDrop/priceIncrease instead (the row's previousIsOnSale still shows the sale-state change). Ignored without watchLabel. |
webhookUrl | string | Optional http(s) URL that receives a small JSON POST when the run finishes (products pushed, per-change-type counts, dataset id, and a per-URL sources outcome list — see the FAQ). Best-effort — a failing webhook never fails the run or changes the bill. |
Output (one item per product)
{"id": 6543210987,"title": "Men's Wool Runner","handle": "mens-wool-runners","url": "https://www.allbirds.com/products/mens-wool-runners","vendor": "Allbirds","productType": "Shoes","tags": ["men", "runners"],"currency": "USD","priceMin": 98,"priceMax": 98,"compareAtPriceMin": 125,"compareAtPriceMax": 125,"isOnSale": true,"discountPercent": 21.6,"available": true,"availableVariantCount": 9,"variantCount": 12,"totalInventory": 149,"images": [{ "src": "https://cdn.shopify.com/s/files/....jpg", "alt": "Men's Wool Runner in Natural Grey" }],"imageUrl": "https://cdn.shopify.com/s/files/....jpg","imageCount": 6,"variants": [{ "id": 1, "title": "8 / Natural Grey", "sku": "WR-8-NG", "price": 98, "compareAtPrice": 125, "available": true, "grams": 340, "requiresShipping": true, "taxable": true, "position": 1, "featuredImage": "https://cdn.shopify.com/s/files/....jpg", "barcode": "196942208243", "inventoryQuantity": 15, "inventoryManagement": "shopify", "inventoryTracked": true, "inventoryPolicy": "deny", "quantityRule": null }],"description": "Our classic everyday sneaker ...","descriptionHtml": "<p>Our classic everyday sneaker ...</p>","createdAt": "2025-01-10T12:00:00Z","updatedAt": "2026-08-01T09:30:00Z","store": "https://www.allbirds.com","sourceUrl": "https://www.allbirds.com/collections/mens","seoTitle": "Men's Wool Runner Shoes | Allbirds","seoDescription": "Our best-selling sneaker, made from sustainably sourced merino wool.","ratingValue": 4.6,"reviewCount": 51,"hasSubscriptionOption": true,"subscriptionPlans": [{ "name": "Subscribe & Save 20%", "plans": [{ "name": "Delivered every 30 days", "recurringDeliveries": true, "discountPercent": 20 }] }]}
barcode/inventoryQuantity/inventoryManagement/inventoryTracked/inventoryPolicy/quantityRule (per variant), totalInventory/hasSubscriptionOption/subscriptionPlans (per product), seoTitle, seoDescription, ratingValue and reviewCount are only present when detailLevel is "full" (see Input) — Shopify's bulk products.json feed carries none of them.
quantityRule ({min, max, increment}) is only populated for a real, non-default rule — a minimum order quantity, a case-pack increment, or a per-order cap — Shopify's own implicit default (min:1, max:null, increment:1) is reported as null instead so it doesn't look like every product has a "rule". hasSubscriptionOption/subscriptionPlans cover both Shopify's native subscriptions and app-backed ones like Recharge (verified live on both); subscriptionPlans[].plans[].discountPercent is null when a plan doesn't use a flat percentage discount (e.g. a fixed-price override).
How complete the stock fields are is a per-store setting, not something any scraper controls. Measured live: allbirds.com publishes real per-variant quantities and UPC barcodes; brooklinen.com publishes a barcode but no quantity; rothys.com publishes barcodes but no quantities. Missing values come back as null rather than a guess. barcode is whatever the merchant typed into that field — a GTIN/UPC on most stores, an internal SKU on some. totalInventory counts only variants Shopify actually tracks stock for, so untracked items (inventoryManagement: null, which report a 999999 sentinel) can't inflate it; it is null — not 0 — when a store tracks none, keeping "sold out" distinguishable from "this store doesn't publish stock". Each variant also carries inventoryTracked as an explicit boolean (true/false, or null on a detailLevel:"basic" row that has no inventory data at all), so that same guardrail survives flattening variants[] into CSV rows, where an implicit inventoryManagement: null is easy to drop and a 999999 sentinel is then easy to sum by mistake.
watchLabel, watchChange, watchChanges, previousPriceMin, previousAvailable, previousIsOnSale and priceChange are only present on rows returned by a run with watchLabel set (see Watch mode below); a normal one-off scrape never emits them.
Watch mode — pay only for what changed
Scraping a competitor's catalog every morning normally means re-buying the same 500 products every morning. Set watchLabel and you buy the diff instead:
{ "storeUrls": ["https://www.allbirds.com"], "maxProductsPerStore": 5000,"watchLabel": "allbirds-prices", "watchEvents": ["priceDrop", "backInStock"] }
- Run 1 records each product's price, availability and sale status and returns 0 products, charged $0 — it's a baseline.
- Every run after that returns only the products that moved. Each row is a normal product row plus
watchChange(new/priceDrop/priceIncrease/wentOnSale/saleEnded/backInStock/outOfStock/delisted),watchChanges(all changes on that product),previousPriceMin,previousAvailable,previousIsOnSaleandpriceChange(the signed difference). - Unchanged products are never pushed and never charged, so a daily watch on a stable catalog costs nothing on quiet days.
- Adding a store URL to an existing label baselines just that store, so you aren't billed for its whole catalog as "new". Changing a filter (
onlyAvailable,searchQuery,vendors,productTypes,minPrice,maxPrice,onSaleOnly,minDiscountPercent) starts a fresh baseline, since it's a different watched set. - In watch mode
maxProductsPerStorecaps how many products are scanned per store (the diff has to walk the feed), so set it above the store's catalog size; the log warns when the sweep was cut short.
delisted — products that are gone
A delisted row means a product that was in the baseline is no longer in the store's catalog at all: pulled, discontinued or sold out permanently. It is the one watch event with a coverage precondition, because "absent" and "we didn't look" are the same observation from the outside:
- It is only produced for a store whose feed this run walked end to end. If
maxProductsPerStorecut the sweep short,maxResultsor your PPE budget stopped it mid-flight, the run ran out of time, or the store errored, nodelistedrows are produced for that store — the run never guesses, so you are never charged for a catalog that is still there. - A watched single product URL (
/products/<handle>) has no "page 2" to still hold it, so it works differently: a clean 404 on a URL this label already successfully watched before is treated as delisted immediately, in the same run that first sees it 404 (not the password/frozen/blocked/rate-limited cases, which report their own reason and are not treated as delisted). - Products that are still on the store but no longer match your filters (e.g. a price rose above
maxPrice) are not delisted. Coverage is measured on the raw feed, before any filter runs. - If more than half a store's baseline (and more than 25 products) goes missing at once, the run reports none of it and logs why — a collection being emptied or re-scoped looks identical to a mass delist from the outside. They stay in the baseline, so a genuine shutdown still surfaces as the catalog shrinks past the guard.
- A delisted row carries
id,handle,url,store,previousPriceMin,previousAvailableandpreviousIsOnSale; the product-detail fields arenull, because there is no longer a product to read them from. Theurlis the product's old URL — it 404s by definition, and is there to identify the product. - Once reported, the product leaves the baseline, so you are charged for it once. If it comes back, it returns as
new. - Pair it with
webhookUrland Apify's scheduler to get a ping only when something actually changes.
Pricing
result — charged per product returned, $0.001/product on the Free plan, $0.00095 on Bronze, $0.0009 on Silver, tapering to $0.00085/product on Gold and above (full live ladder, verified against the live Actor record 2026-10-04; the comparisons below quote the "$0.001 → $0.00085" ends of it for brevity). No per-run "Actor Start" fee at all — ours is genuinely zero (re-verified live 2026-10-03). Many rivals in this niche do charge one, from $0.00005 up to $0.05 per run before any data is delivered, but we no longer claim most of them do: live-pricing the whole unnamed cohort on 2026-10-07 found 31 of 64 with no start fee at all, so a start fee is roughly a coin flip in this niche rather than the norm. Among the rivals named here the sweeps have found nine that also charge no start fee — pintostudio/shopify-product-search (41 users, $0.002/result), igview-owner/shopify-scraper ($0.015 Free → $0.005 Gold+), rl1987/shopify-api-scraper ($0.001 per variant rather than per product), lergassy/shopify-store-intel, scrapesage/shopify-store-scraper, kalirobot/shopify-scraper, dami_studio/shopify-products-scraper, technicaldost/shopify-products-exporter ($0.002/product) and scrapebench/shopify-change-tracker (58 users, $0.01 per change) — plus three Actors on Apify's FREE model that charge nothing at all (see below). Zero start fee is a real advantage over most of the niche, not over all of it. Stores that block the public catalog return nothing and cost nothing.
productDetail — charged once per product (not once per extra request) only when detailLevel is "full" and the extra fetches actually found stock, SEO or rating data, at the same tiered rate as result ($0.001 Free, $0.00095 Bronze, $0.0009 Silver, $0.00085 Gold+). A product that yields none of them — no rating app installed, no meta description, store doesn't publish stock — costs nothing.
The biggest rival in this niche is autofacts/shopify (2,304 users — more than three times the next listing). It is a genuine head-on competitor: whole catalogs, single collections, single product URLs or a keyword search, with variants, prices, images, tags and optional per-variant live stock counts. Its pricing is tiered $0.0012/product on Free down to $0.0008/product on Gold and above, plus a $0.00005 Actor Start fee and separate $0.0008 collection / $0.001 recommended-product / $0.001 "Real inventory" events. So we are cheaper on the Free plan ($0.001 vs $0.0012) and it is cheaper on Gold and above ($0.0008 vs our $0.00085) — the per-product gap either way is under two hundredths of a cent, so pick on features, not price (verified live 2026-10-02).
The next-largest rival, trovevault (685 users, trovevault/shopify-products-scraper), matches our per-product rate ($0.001 Free → $0.00085 Diamond, re-verified live 2026-10-03 — 0 price drift since 2026-09-23) but adds two charges we don't have: a one-time Actor Start fee every run tiered the same $0.001 → $0.00085 (1 GB default memory), and — since 2026-09-23, after a promotional free period ended — a second $0.001-tier "Inventory Enrichment" event on top of the base product charge for stock counts, barcodes and SEO fields (the equivalent of our productDetail, which stays a single combined charge). At Free-tier pricing, their full-detail product now costs $0.002 + a per-run start fee versus our $0.002 with no start fee. Their entire live input schema is six properties — domains, maxProducts, includeInventoryDetails, proxyConfiguration and two internal plumbing fields — so it has no equivalent of our vendors/productTypes/searchQuery/minPrice/maxPrice/onSaleOnly/minDiscountPercent filtering, watch mode, or webhooks (checked against their 2026-09-18 build's input schema, not their Store description, verified live 2026-10-02).
webdatalabs/shopify-product-scraper (400 users) charges roughly 10x our rate — $0.01/product on Free tapering to $0.007/product on Gold+, plus a $0.00005 Actor Start fee — versus our $0.001 → $0.00085 with no start fee (verified live 2026-10-03). The same account also runs webdatalabs/shopify-store-intelligence (186 users, confusingly titled "Shopify Store Scraper" in the Store), which is priced per store, not per product — $0.01 → $0.004 per store analysed plus a $0.006 → $0.003 Actor Start fee — so it is not comparable to us per row and is a store-profile tool rather than a catalog export (verified live 2026-10-03). nexgendata/shopify-store-analyzer is the same shape at $0.05 per store-analysis record plus $0.01/$0.02 competitor-pricing and tech-stack add-ons.
What we do not claim: we are not the cheapest in this niche, at any tier. A 12-term Store sweep on 2026-10-03 (re-run full-list 2026-10-05, 131 matched, 362 seen) saw hundreds of Shopify-mentioning listings — the large majority are Shopify App Store, lead/email-finder or product-review scrapers that do not export a store's catalog at all. Ruled out by scope, not priced: lurkapi/shopify-product-reviews-scraper-api (68 users, re-verified live 2026-10-16), lurkapi/shopify-loox-reviews-scraper-api (verified live 2026-10-05) and cleanscrape/shopify-reviews-scraper all sell shopper review text/ratings pulled from a store's review app, not the product catalog itself — a different product entirely, not a substitute at any price. Priced all 52 genuine product-catalog, store-profile and price-watch rivals live. Several cost less than we do:
codescraper/fast-shopify-products-scraper(the most-used free rival found so far),novus/shopify-scraper,bercikgroup/shopify-store-products-scraperandstreaked_patty/mercadolibre-scraper(mislabeled slug — its title and input schema are plain Shopify, nothing to do with MercadoLibre) are all on Apify's FREE pricing model — $0 per product at any volume, with no start fee either. All four are real product scrapers readingproducts.json. No price we could set beats free; what we offer over them is the filtering (vendors/productTypes/searchQuery/price/discount), watch mode, webhooks,detailLevel: "full"enrichment and the per-sourcecomplete/incompleteReasoncompleteness contract — none of which any of the four advertises (codescraper's entire input isstartUrls/maxItems/proxyConfiguration).streaked_pattyis also the narrowest of the four: a singleshop_domainper run (no multi-store, no collection or product-URL targeting) hard-capped at 250 products total, so it cannot export a catalog bigger than that at any price. (bercikgroup/novusre-verified live 2026-10-03,streaked_pattynewly found and verified live 2026-10-05,codescrapernewly found and verified live 2026-10-06:bercikgroupcarries no pricing record at all,novuswas auto-converted off Apify's sunsetting rental model on 2026-10-02 and landed on FREE,streaked_pattyhas run a single build on the FREE model since 2026-07-18, andcodescraperwas a flat $3/month rental until it was auto-migrated off the sunsetting rental model on 2026-10-06 and landed on FREE too — none of the four has a per-product charge today.)fetch_cat/shopify-products-scrapercharges $0.0000281/product on Free (~35x under our rate) behind a $0.005 start fee, so it is cheaper than us on any run past roughly 6 products.sleek_waveform/universal-shopify-scrapercharges $0.0005/product plus $0.00001/result and a $0.00005 start fee — about half our rate at every tier.shahidirfan/Shopify-Product-Scraper(41 users) charges a flat $0.0009/product regardless of plan, beating our $0.001 Free-tier rate (it charges a $0.00005 start fee we don't, and loses to us on Gold+ at $0.0009 vs $0.00085).lergassy/shopify-store-intelprices products at $0.0005 → $0.00035, under our rate, but bills $0.005 per store profile on top and is scoped as store intelligence (identity, email, apps, catalogue size) rather than a full catalog export.kalirobot/shopify-scrapercharges a flat $0.00049/product with no start fee at all — about half our Free rate and still under our $0.00085 Gold+ rate, so it is cheaper than us at every plan tier with no crossover volume. It is a real catalog extractor; what we add over it is thevendors/productTypes/searchQuery/price/discount filtering, watch mode, webhooks,detailLevel: "full"enrichment and thecomplete/incompleteReasoncontract (newly named 2026-10-03).rover-omniscraper/shopify-scraper("Ultra Fast Shopify Scraper") charges a flat $0.0009/product plus a $0.0003 start fee, so it beats our $0.001 Free rate on any run past roughly 3 products, while our $0.00085 Gold+ rate beats it at any volume. It is the closest new rival on features as well as price — multi-store auto-detection, price/tag/sale filtering and new-arrival flags, pure HTTP, no browser — though it advertises no watch mode, webhooks or per-source completeness contract (newly named 2026-10-03).scrapesage/shopify-store-scraperis tiered the wrong way round from us: $0.002/result on Free (double our rate) falling to $0.00076 on Platinum and $0.0005 on Diamond, with no start fee. We are cheaper up to and including Gold; it undercuts our flat $0.00085 on the top two plans only (newly named 2026-10-03).autofacts/shopifyundercuts us on Gold and above, as above.
We also checked these rival Actors, none of which undercut us — clearpath/shop-by-shopify-product-scraper (106 users, user count re-verified 2026-10-18, $0.00249 → $0.00179/product plus a separate $0.00001/result event and an Actor Start fee — pricier than us at every tier), khadinakbar/shopify-all-in-one-scraper (35 users, $0.003/product flat plus separate collection/review events), and pintostudio/shopify-products-scraper (34 users, $0.0037/result flat plus start fee). A 2026-10-05 pass found four more flat-rate catalog scrapers, all dearer than us at every tier: cg_nguyen/shopify-store-scraper ($0.002/product + $0.00005 start), hichemdev/shopify-product-scraper ($0.002/product + $0.002 start), mighty_monk/shopify-product-scraper ($0.003/product + $0.00005 start) and hello.datawizards/shopify-product-script ($0.008/result + a $0.05 start fee, the dearest start fee found in this niche). The same sweep priced ten more Shopify catalog rivals live, all dearer than us: khadinakbar/ecommerce-store-scraper (122 users, $0.003/product + $0.005 start), automation-lab/shopify-scraper (101 users, $0.003 Free → $0.00084 Diamond + $0.005 start — its Diamond per-product rate shades ours by $0.00001, but the start fee puts it above us on any real run), thirdwatch/shopify-store-scraper (55 users, $0.002 → $0.0012 + start), benthepythondev/shopify-store-scraper (23 users, $0.004 → $0.0028 + start), crawlerbros/shopify-scraper ($0.002 → $0.001 + a $0.01 start fee), makework36/shopify-products-scraper ($0.001 flat + start), parseforge/shopify-stores-scraper ($0.01599 → $0.01199 + $0.005 start), prodiger/shopify-scraper ($0.003 + $0.005 start), apivault_labs/shopify-product-scraper (verified live 2026-10-05, $0.003 flat + start) and automly/shopify-products-scraper ($0.00155 + a $0.05 start fee) (all re-verified live 2026-10-03 with zero price drift since 2026-10-02).
The 2026-10-03 sweep priced thirteen further catalog rivals that had never been named here, all of them dearer than us — memo23/dtc-product-scraper (37 users, verified live 2026-10-16, $0.001/product, a tie with our Free rate, but behind a $0.005 start fee and a second $0.001 "Additional data" event), agenscrape/shopify-intelligence-scraper ($0.002 + a $0.01 start fee), flash_scraper/shopify-store-scraper ($0.003 + start), cloud9_ai/shopify-scraper ($0.0025 + start), crawlerbros/shopify-scraper-pro ($0.002 Free → $0.001 Gold+ plus a $0.005 start fee — a second, dearer listing from the same account as crawlerbros/shopify-scraper above), piotrv1001/shopify-product-scraper ($0.0015 + start), fortuitous_pirate/shopify-store-scraper ($0.00465 Free → $0.00186 Gold+ on both the result and the start event), happitap/shopify-product-scraper ($0.002 + start), aiscraperdev/shopify-product-inventory-scraper ($0.004 + start), muhammadafzal/shopify-product-scraper ($0.01 Free → $0.007 Gold+ plus a $0.005 start fee), straightforward_hydra/shopify-product-feed-scraper ($0.002 + start), technicaldost/shopify-products-exporter ($0.002 flat, no start fee) and gauzy_penguin/Shopify-Scraper ($0.001 flat + a $0.00005 start fee — a tie with our Free rate before its start fee, and dearer than our $0.00085 on Gold+) (all verified live 2026-10-03).
A fleet-oldest full-cohort sweep on 2026-10-05 (niche grown to 374 seen, 136 matched — up from 131/362 on 2026-10-03) found 102 matched listings never named here, almost all sitting at 1-2 users (only one, f0rty7even/shopify-products-scraper, which is itself dearer at $0.00099 → $0.00079 — see below). Every one was live-priced via its own Actor record, reading isPrimaryEvent rather than the cheapest-looking event (the same trap as trovevault/memo23 above): four listings' real headline price turned out to be a dearer flat $0.002-$0.01/row event while a non-primary "enrichment"/"change-alert" sub-event sat at a misleadingly cheap $0.0001-$0.00001 (zentrafoundry/shopify-woocommerce-dtc-catalog-intelligence, zentrafoundry/sku-price-drop-alert-api, zentrafoundry/supplier-stock-price-monitor, darknezz/shopify-products-scraper, cancap/shopify-products-monitor — the last two land as a tie or dearer-than-Gold+ once the real $0.001 primary event is read, not an undercutter). Four more are a different product entirely, not priced: charlieakan/shopify-product-image-marketplace-photo-audit, charlieakan/shopify-product-seo-listing-quality-audit and charlieakan/shopify-google-shopping-feed-readiness-audit are listing-quality audit tools (flag products with weak images/SEO/feed issues), and overbifrost/shopify-csv-import-checker-catalog-diff, mathe_carv/shopify-csv-change-review, chartreuse_oast/catalogops-shopify-csv-preflight and biztoolsus/shopify-catalog-import-check bill per CSV comparison/report ($0.05-$0.5 one-time), not per product — none of the eight scrapes a live catalog. tidytools/shopify-store-products-scraper prices its product event at $0.001 Free → $0.0008 Gold+, a tie on Free and dearer than us from Bronze up, so it is not an undercutter either (all verified live 2026-10-05).
Of the rest, 19 genuinely undercut us at every tier, all flat or near-flat per-product rates with no crossover volume: scrapesignal_labs/shopify-product-catalog-scraper ($0.0003, the new niche floor among full catalog scrapers), eiv/shopify-product-scraper ($0.0004), ahmed_jasarevic/shopify-product-scraper, steadyscrape/shopify-products-scraper, highbrow_qualification_z7w/shopify-product-price-monitor, springlike_meadowland/shopify-products-scraper (all $0.0005), itzcloud/shopify-products ($0.0005 → $0.00035), scrape_pad/shopify-products-scraper ($0.0006), tinyrex/shopify-products-scraper ($0.0007), and at $0.0008 flat (or tapering a little below it): ghostgrid/shopify-competitor-product-monitor, datamule/shopify-product-catalog-scraper, tenfoldfleet/shopify-products-scraper, coughins/shopify-multi-store-catalog-exporter, nexo_data/shopify-sku-price-stock-changes, datalantern/shopify-products, beike_r/shopify-products, ceddl/shopify-catalog-delta ($0.0008 → $0.00068), luminar/shopify-products-price-stock-monitor ($0.0008 → $0.00072) and getascraper/shopify-store-product-monitor ($0.00088 → $0.00066). 11 more cross under only from a paid tier up (dearer or tied on Free): hodayfaell/shopify-product-scraper and bovi/shopify-products-scraper (both ~$0.0009, cross under at Bronze/Silver), lightmoon/shopify-product-scraper and parsebird/shopify-products-scraper (both $0.00099 → $0.00079, cross under from Silver up), dododata/shopify-products-scraper ($0.001 → $0.0007), steadydata/shopify-products-catalog ($0.001 → $0.00065), accountable_eel/shopify-product-monitor ($0.001 → $0.0005), eliai/shopify-products-scraper and datascraperes/shopify-products-scraper (both $0.001 → ~$0.0008/$0.00075) all cross under from Bronze up, tacps126/shopify-products ($0.001 → $0.0008, Gold+ only) and montyburrows/shopify-prices ($0.001 → $0.0007/variant, not product — a different unit, caveat'd). All 30 are 1-2 user listings, all read via the Actor's own pricingInfos/isPrimaryEvent record on 2026-10-05, not inferred from Store titles (verified live 2026-10-05).
On watch mode specifically, the sweep found three dedicated Shopify change-trackers, which is the feature set our watchLabel mode competes with directly rather than on catalog export: scrapebench/shopify-change-tracker (58 users — price increases/decreases, back-in-stock and out-of-stock flips, products added or removed) charges a flat $0.01 per change detected, with no start fee; technicaldost/shopify-price-delta-monitor charges $0.005/product; and redfoxxie/shopify-product-price-stock-monitor (newly named 2026-10-05) charges a flat $0.005 per delivered price/stock/availability/variant change, no start fee. We charge our ordinary $0.001 → $0.00085 result rate per changed product and nothing for products that did not change, so we are roughly 10x, 5x and 5x cheaper per reported change respectively; what they offer over us is nothing we lack, but none of the three is a catalog exporter, so a buyer who wants both a one-off export and a watch feed needs two Actors there and one here (verified live 2026-10-03/2026-10-05).
A fleet-oldest re-sweep on 2026-10-06 (niche grown to 385 seen, 142 matched — up from 374/136 on 2026-10-05) found only two genuinely unnamed listings worth pricing, both well above the usual 1-2-user noise floor: codescraper/fast-shopify-products-scraper (added to the FREE-pricing list above) and vulnv/shopify-products-scraper, newly moved today from a flat $5/month rental to pay-per-event — a flat $0.0015/product, no start fee, dearer than our $0.001 Free rate and our $0.00085 Gold+ rate at every tier, so not an undercutter. The remaining 62 unnamed matches were judged by title and shape rather than priced one by one — a shortcut the 2026-10-07 sweep below overturned, so read that paragraph and not this sentence for the small-listing cohort. check-pricing/check-charges re-confirm our own rate unchanged ($0.001 → $0.00085, no start fee).
A fleet-oldest re-sweep on 2026-10-07 (388 seen, 143 matched, 64 unnamed — essentially flat against 385/142/64 on 2026-10-06) found no unnamed listing above the 1–2-user noise floor for the first time in this niche's audit history, so on the 2026-10-06 reasoning there would have been nothing to price. Every one of the 64 was live-priced anyway, individually, off its own Actor record — and four of them do undercut us, which is the direct refutation of the "no tiered pricing record suggesting anything below our rate" shortcut above: a 2-user listing's price is not predictable from its title, and three of these four publish a full tiered ladder:
glidepath/shopify-products-scraperis the real find — a tiered $0.0008312/product on Free and Bronze, $0.00075 on Silver, $0.00065 on Gold and above, behind only a $0.00005 start fee. That undercuts us at every tier from the first product onwards (no crossover volume), and at ~24% below our Gold+ rate it is the deepest full-ladder undercut in the niche outside the flat-rate floor listings named further up.funny_ground/shopify-products-scrapercharges a flat $0.0008/product — under our rate at every tier — but hides a $0.005 Actor Start fee behind it, which is the whole story: we are cheaper on any run up to 25 products on Free, or up to 100 products on Gold+, and it only wins above those volumes. A single-store catalog pull usually clears 100 products, a watch-mode delta run usually does not.sourcing-data-studio/shopify-products-apiprices at $0.001 Free/Bronze → $0.0009 Silver → $0.0008 Gold+, plus a $0.00005 start fee. We are cheaper on Bronze ($0.00095 vs $0.001) and tie on Free and Silver; it undercuts us by $0.00005/product on Gold and above, which its start fee cancels out only for the first product of each run.snow_leo_data/shopify-inventory-scraperis dearer than us on Free, Bronze and Silver ($0.0012 → $0.00098) and undercuts our $0.00085 by a single hundredth of a cent at Gold+ ($0.00084), with a $0.00005 → $0.00004 start fee that keeps us cheaper on any run of four products or fewer.
Also worth naming from the same sweep: titan_coder/shopify-products-delta-tracker publishes no pricing record at all, so it is free to run today — the cheapest it can possibly be — but an Actor with no pricing record is one the author has not finished monetizing rather than a committed free tier, and it can acquire one without notice. None of the other 59 listings priced below our ladder at any tier.
FAQ
Does it work on custom domains, not just *.myshopify.com? Yes — pass any storefront domain that runs Shopify; no need to resolve it to the myshopify.com backend first.
What if the same store ends up in storeUrls twice? Deduped automatically by the resolved endpoint (so https://store.com and https://store.com/ count as one) — you're never charged twice for the same store.
What if I pass several collections that share products? Overlapping collections are normal — /collections/all plus a category, or two categories with the same product in both. Each product is returned, and charged, once per run, under the first URL that returned it; the repeats are skipped before any filter, before watch mode and before billing, and the log says how many there were. Every row carries sourceUrl, the input URL it came from, so several collections of one store stay distinguishable (store is the same for all of them). In watch mode this also means a product that merely left one collection but is still in another of your URLs is not reported as delisted.
Can I tell which products are on sale? Yes, each item includes isOnSale (true when some single variant has its own compare-at price above its own price) and discountPercent — the percentage off the cheapest variant's own list price, so it never mixes two different variants — plus store currency. Both are deliberately per-variant: comparing compareAtPriceMin against priceMin reads two numbers that can belong to different variants, so a stale compare-at left below a raised price can report a sale no variant is actually running.
Does searchQuery handle accented words correctly? Yes, as of v0.1.29 — the query and the product text it's matched against are Unicode-normalized before comparing, so an accented word (e.g. "café") matches regardless of which of Unicode's two equivalent representations (composed vs. decomposed) you typed it in.
Do I get the description as HTML? Both: description is plain text and descriptionHtml is the store's raw body_html, so you can keep the formatting when re-publishing a catalog.
Can I get the product's SEO title/description and star rating? Yes, set detailLevel: "full". Those aren't in Shopify's products.json feed at all — they only live on the rendered product page (<title>/meta description tags and, when the store runs a review app like Judge.me or Yotpo, a ratingValue/reviewCount in the page's structured data), so getting them costs one extra HTTP request per product and is priced as its own event.
Can I get real inventory counts and barcodes? Yes, set detailLevel: "full". Shopify's bulk products.json feed strips barcode, inventory_quantity, inventory_management and inventory_policy out of every variant, but the per-product JSON route still serves them on the same variant ids, so they merge back in exactly. How much a given store exposes is that store's own setting (see the note under Output) — you get real values where they are published and null where they are not, never a guess. No login or Admin API token is involved; this is the same data a logged-out shopper's browser receives. Every variant also gets inventoryTracked (true/false) so "this quantity is real" is a named field rather than something you infer from inventoryManagement being non-null — untracked variants report a 999999 sentinel quantity, and that trap is easy to walk into once you flatten variants[] into a CSV and drop a column. It is null, not false, on rows that carry no inventory data at all (detailLevel: "basic"), because "not tracked" and "not asked" are different answers.
Can I pass one product URL instead of a whole store? Yes — a /products/<handle> URL returns exactly that one product, with the same fields (and the same per-result price) as it would have from a full-store run. Fixed in v0.1.33: Shopify serves single products through a different, older serializer than the bulk feed, and two of its quirks used to corrupt that path — a comma-joined tags string instead of a list, and no per-variant availability flag at all.
When is available null instead of true/false? Only when the store publishes nothing to decide it from: stock tracking is on for the variant but quantities are withheld. Untracked variants and stores that allow overselling are reported true, tracked ones as inventoryQuantity > 0, and detailLevel: "full" resolves the remaining unknowns from the storefront's own .js route at no extra request. null always means "this store doesn't say", never "sold out".
Can I tell if a product is sold as a subscription, and how big the discount is? Yes, set detailLevel: "full" — hasSubscriptionOption and subscriptionPlans cover both Shopify's own native subscriptions and app-backed ones (e.g. Recharge), including each plan's delivery frequency and percentage discount, read straight from the storefront's own .js route.
Can I tell if a product has a minimum order quantity or case-pack size (wholesale/B2B)? Yes, set detailLevel: "full" — each variant's quantityRule reports a real {min, max, increment} only when the store has actually configured one; Shopify's meaningless universal default (order 1 at a time, no cap) comes back as null, not a fake "rule".
Does it bypass password-protected or dev stores? No — only publicly reachable catalogs are read, same as a logged-out shopper would see.
What happens if a store's storefront has a transient connection blip mid-scan? Every request is retried up to 3 times on a connection-level failure (measured at roughly 1 fresh request in 4 for HTTP/2 faults, 2026-09-21) before that store is marked errored in the status message — a single blip no longer silently truncates a multi-page store to whichever page it reached.
A store returned nothing — how do I tell "empty catalog" from "wrong URL"? The run log says which, by name. Shopify answers products.json with a status code that identifies the problem, and as of v0.1.43 each one gets its own message: 401 the storefront is password-protected ("Opening soon" page up), 402 the store is frozen or closed (lapsed plan), 404 there is no Shopify store at that domain (usually a typo), 403 a bot check or geo/IP block — re-run with proxyConfiguration, 429 rate-limited. Only a genuine 200 carrying {"products":[]} is reported as an empty store or collection, and that one is re-checked on three separate attempts before we believe it. Nothing is charged in any of these cases.
I automate this — how do I tell those cases apart in code, not by reading the log? Every run writes a RUN_SUMMARY record to its own key-value store (GET https://api.apify.com/v2/actor-runs/<runId>/key-value-store/records/RUN_SUMMARY), and the same object is included in the webhookUrl POST as sources. It holds one entry per URL you passed, with status — ok, empty, filteredOut, duplicate, error, badUrl, watchBaselined, watchNoChanges or notReached — plus scanned, delivered, duplicates, complete and a reason string. scanned and delivered are separate fields on purpose: an empty dataset stops being ambiguous, and a store that errors today and succeeds tomorrow does not look like a catalog that suddenly appeared. notReached specifically means the run stopped (time limit, maxResults, charge budget) before that URL was fetched at all — never confuse it with an empty store.
Just tell me if the run got everything — one field. The same RUN_SUMMARY record carries run-level complete, incompleteReason, incompleteDetail and sourcesIncomplete alongside sources. complete is true only when every URL you passed was read to the end and the run did not stop on the clock; false otherwise; null if no URL was processed at all. incompleteReason names the first shortfall — max-products-per-store, max-results, time-budget, charge-limit, source-request-failed, bad-url, not-reached, empty-source, filtered-out, duplicate-source, watch-baselined or watch-no-changes — and incompleteDetail says which URL and how many others. Note that complete is separate from status on purpose: a URL with status: "ok" is still incomplete if maxProductsPerStore (default 500) cut its sweep short, which is the most common reason a large catalog comes back partial. This is the same complete / incompleteReason / incompleteDetail contract every FetchSmith Actor's RUN_SUMMARY carries, so one pipeline can check all of them the same way.
Can I search a store for a keyword instead of scraping its whole catalog? Yes, set searchQuery (e.g. "wool" or "merino sweater") — it matches against title, vendor, product type and tags (all words must appear). It's a client-side filter, not a separate search API call, since Shopify's public feed has no server-side keyword search; a store with a large catalog still takes the same time to scan as an unfiltered run of the same store.
How does a price window handle products whose variants have different prices? A product is a price range, not a single price, so minPrice/maxPrice keep a product when its range overlaps your window: a $40-$120 hoodie matches maxPrice: 50 (there is a buyable $40 variant) and also minPrice: 100 (there is a $120 one). Prices are compared in the store's own currency — the currency output field tells you which — and no FX conversion is done, so the same window means different things on a USD and a EUR store. Products whose price can't be read at all are dropped rather than passed through, so a price-filtered run never returns a row the filter couldn't be evaluated against. Both bounds are applied before billing, so you are never charged for a product the filter removed.
Can I get only the discounted products? Yes, set onSaleOnly: true — it keeps products where the store set a compare-at price above the current price (identical to the isOnSale output field), with discountPercent on every row computed from the cheapest variant's own compare-at price. Products the store never put a compare-at price on are excluded, since there is no evidence of a discount to report.
Can I set a minimum discount instead of "any discount"? Yes, set minDiscountPercent (e.g. 30 for "30% off or more") — it reads the same discountPercent field onSaleOnly checks, just with a floor. A product with no compare-at price at all is excluded, not treated as a 0% discount that happens to fail the floor.
Can I filter to specific brands or product types without a keyword search? Yes — vendors and productTypes are OR-matched lists (e.g. vendors: ["Allbirds","Rothy's"] returns products from either brand), unlike searchQuery, which is AND-of-words across a single combined haystack and can't express "brand A or brand B". Both read fields already on every row, so there's no extra request cost, and both combine with every other filter (including each other) via AND — only the entries within one list are OR'd together.
Can I monitor a store for price drops instead of re-scraping the whole catalog? Yes — set watchLabel (optionally narrowed with watchEvents). The first run under that label is a free baseline that returns nothing; every later run returns only new products, price changes, sale starts/ends, stock flips and products that have disappeared from the catalog (delisted), each row carrying watchChange, previousPriceMin, previousAvailable, previousIsOnSale and priceChange. Products that didn't move are never pushed and never charged, so a scheduled daily watch costs nothing on days when the store doesn't change. See Watch mode above.
Does the watch catch a markdown that doesn't change the listed price? Yes — wentOnSale/saleEnded fire when a store adds or removes a compare-at ("was") price while the current price itself stays put, which priceDrop/priceIncrease alone would miss entirely. If the current price changes too, that's reported as priceDrop/priceIncrease instead (one event per row, not two) and the row's previousIsOnSale still shows whether the sale flag itself flipped.
Related guides
Engineering write-ups behind this Actor:
- Every Shopify store's catalog is public JSON — no login, no browser
- Shopify's bulk products.json hides stock counts and barcodes — the per-product endpoint doesn't
- HTTP-only vs headless browser scraping: a timed benchmark
- Four ways an invisible character makes a scraper return zero rows
- We nearly charged our own buyers twice for rows they'd already paid for — a capped watch-mode baseline can silently evict old-but-current ids on a high-volume run, re-delivering (and re-billing) rows already paid for.
- Eight ways an "only new since last run" watch mode silently stops working — the fleet-wide survey of watch-mode failure shapes across all nineteen incremental Actors, this one included.
Only publicly available data is collected. Support: support@fetchsmith.com · Hosted API: https://fetchsmith.com/tools/shopify-products-scraper
Source code: https://github.com/Fetchsmith/fetchsmith/tree/main/actors/shopify-products-scraper