Shopify Scraper avatar

Shopify Scraper

Pricing

from $1.20 / 1,000 products

Go to Apify Store
Shopify Scraper

Shopify Scraper

Scrape any Shopify store: products, variants, collections, prices, images, tags and live stock levels. Crawl a whole catalog, a single collection, one product URL or a keyword search. Optional per-variant inventory counts, subscription plans and videos. Prices in minor units. Pay per result.

Pricing

from $1.20 / 1,000 products

Rating

4.2

(7)

Developer

Richard Feng

Richard Feng

Maintained by Community

Actor stats

72

Bookmarked

2.3K

Total users

47

Monthly active users

11 hours

Issues response

3 days ago

Last modified

Share

Shopify products, every variant: prices, stock, barcodes, images, video, tags and subscriptions, with collections, keyword search, recommendations and multi-currency. Pay per result, not per minute.

Every product a Shopify store publishes, down to the variant. Give it a store, a collection or a product page, or a keyword to search the stores you list. Each product comes back with every variant's price, stock status and SKU, and with its description, images, tags and options. Product pages, and collection crawls with the matching option, add the merchant's barcode, weights and subscription plans. With grabRealInventory, stores that publish their stock numbers give real counts. No API key needed.

🧰 Need the list of stores first? Shopify Store Leads finds Shopify stores by country, category or keyword, ready for this Actor to scrape.

Why choose this Actor?

The whole catalog, not the first page. Point it at a store's home page and it reads the entire catalog, page after page, to the end. Point it at a collection or a single product and it reads just that. A keyword search runs across every store you list. If a store has renamed a product, the scraper follows it to its new page.

More than Shopify's product JSON. A product read from its own data carries more than prices, stock status, images, tags and options. You also get the merchant's barcode (UPC, EAN or GTIN), weight, tax and fulfilment settings, order quantity rules and B2B volume price breaks. Its storefront adds what no JSON endpoint publishes: subscription and pre-order plans with their prices, and product videos. Every product also gets a rolled-up price, a priceRange and a fullyOutOfStock flag. Output below says which crawls return which fields.

Real stock counts, where a store publishes them. With grabRealInventory, each variant carries the number in stock (quantityAvailable), not just in or out of stock. Many stores publish no stock numbers. Those report the count as unknown and are never billed for it.

Prices in the currency the store charged. Prices are integers in minor units, so 2000 means 20.00. source.currency labels them with the currency the store actually charged for the request. To get another currency, add ?currency= to the start URL, or use a Shopify Markets locale path such as /nl-nl/.

Built for stores that push back. A request that a store's bot check refuses is sent again from a fresh proxy address. A rate-limited request waits as long as the store asks, up to a minute, and then tries again. A site that is not a Shopify store fails at once, with a message that says so. A store or product that fails never ends the run.

Pay per result, not per minute. A duplicate is never billed twice, and Max result records caps how much a run saves. A run that reaches its Max total charge stops cleanly and keeps everything it saved. Pricing below lists what each event costs.

Try it on your own store →

🚀 Quick start

  1. Click Try for free. The input opens with one collection already filled in. Start it as it is to see what a record looks like.
  2. Put the pages you want into Shopify site start urls: a store's home page for its whole catalog, a collection, or a single product. To check that a site runs on Shopify, open https://<domain>/admin. If you see a Shopify login page, the site is compatible.
  3. Set Max result records. It caps how many records the run saves, and so the most the run can charge. The default is 100; 0 means no limit.
  4. Optionally, under Extra data per product, set Max recommended products, or turn on Grab real inventory (experimental) or Grab storefront detail (selling plans & video).
  5. Click Start, then download the results from the Storage tab as JSON, CSV, Excel or XML, or pull them from the API.

Start small, check that the records look the way you expect, then raise Max result records.

💡 What people use it for

Research a competitor's range. Get a whole store with no cap on records: prices, variants, stock, and how the store tags and categorises its products.

{ "startUrls": [{ "url": "https://kith.com" }], "maxRequestsPerCrawl": 0 }

Watch one category for launches and stock changes. Crawl one collection on a schedule and compare the runs:

{ "startUrls": [{ "url": "https://kith.com/collections/kith-tops" }], "maxRequestsPerCrawl": 0 }

Build one catalog across brands. Crawl several stores in one run; each record's source.canonicalUrl shows which store it came from:

{ "startUrls": [{ "url": "https://kith.com" }, { "url": "https://www.allbirds.com" }], "maxRequestsPerCrawl": 0 }

Check one product, variant by variant: the price, stock and barcode of every size.

{ "startUrls": [{ "url": "https://www.allbirds.com/products/mens-dasher-nz-anthracite" }] }

See what a store recommends alongside a product:

{ "startUrls": [{ "url": "https://www.allbirds.com/products/mens-dasher-nz-anthracite" }], "maxRecommendationsPerProduct": 10 }

Read real stock counts from a store that publishes them:

{ "startUrls": [{ "url": "https://www.allbirds.com/collections/mens" }], "grabRealInventory": true }

Find products by keyword in the stores you list:

{ "startUrls": [{ "url": "https://kith.com" }], "query": "hoodie" }

Collect prices in another currency. On a store that does not use Shopify Markets, add ?currency=:

{ "startUrls": [{ "url": "https://kith.com/collections/kith-tops?currency=EUR" }] }

For a store on Shopify Markets, use its local market instead, with the locale in the path:

{ "startUrls": [{ "url": "https://flyingtiger.com/nl-nl/collections/shop-all" }] }

📋 Input

The scraper accepts a JSON input defining the target URLs and scraping behavior.

ParameterTypeRequiredDescription
startUrlsArrayYesA list of URLs to scrape (Homepage, Collection, or Product URLs).
proxyObjectNoProxy configuration. Defaults to the Apify proxy (datacenter). Residential proxies are highly recommended to avoid blocks. Pass { "useApifyProxy": false } to scrape without one.
maxRequestsPerCrawlIntegerNoLimit the number of records saved — and therefore billed. Set to 0 for unlimited (default 100).
maxRecommendationsPerProductIntegerNoNumber of recommended products to fetch per product. Billed per recommended product — see Pricing below. Set to 0 to disable (default 0). Max 20.
grabRealInventoryBooleanNoFetch real per-variant inventory counts from each product's JSON. Billed per product that returns a real count; adds one request per product on collection crawls. Only stores that expose inventory in their storefront JSON are supported — others report it as unknown and are not billed for it (default false).
maxInventoryFetchesIntegerNoAdvanced, not shown in the form. Cap on the extra per-product inventory fetches during collection crawls (only when grabRealInventory is enabled). Unlimited unless set; pass a number in the JSON input to cap the run.
grabStorefrontDetailBooleanNoOn collection crawls, also fetch each product's storefront payload for selling plans, video media and the price span. Adds one request per product and is not billed as a separate event. Product URLs fetch this payload regardless — it is where their stock availability comes from (default false).
maxStorefrontFetchesIntegerNoAdvanced, not shown in the form. Cap on those extra per-product storefront fetches during collection crawls (only when grabStorefrontDetail is enabled). Unlimited unless set; pass a number in the JSON input to cap the run.
queryStringNoSearch query to find specific products. Each start URL is searched in turn; a store that cannot be searched is skipped.

📤 Output

Data is stored in the default dataset. The scraper outputs either Product or Collection objects.

Every product record carries a rolled-up price (the cheapest variant), a priceRange (min/max/varies) and a fullyOutOfStock flag, all derived from the variants themselves — no extra request, every store, every crawl path. Variants always carry requiresShipping, taxable and weightGrams.

stockStatus takes one of three values: InStock, OutOfStock, or LowInStock. The last is reported only where a real stock count was read and it is 5 or fewer, so a store that publishes no stock numbers never returns it.

A product URL whose product the store has since moved — usually a rename — is followed to its new page. The record's source.canonicalUrl is that page and source.redirectedFrom holds the URL you gave. A store can also point an old product address at a replacement product, so compare the two where it matters which product you get.

Detail enrichment

When the scraper holds a product's own JSON, variants additionally carry:

FieldWhat it is
barcodeThe merchant's UPC, EAN or GTIN. Shopify stores all of them in this one field.
weight, weightUnitThe weight as the merchant entered it. weightGrams is the same value normalised.
quantityRuleMinimum, maximum and step size for ordering the variant. Shopify's default is {min: 1, increment: 1} with no maximum, and that is what the great majority of storefronts return — read it as no ordering rules configured, not as a measured value.
quantityPriceBreaksB2B volume pricing tiers — buy n or more, pay this unit price. Empty on a typical consumer storefront.
taxCode, fulfillmentServiceThe merchant's tax code and who fulfils the variant.

and the product carries publishedScope and templateSuffix. Image alt text is filled in here too, where the store provides it.

Which crawls get it. A product URL always does — the scraper already holds that product's JSON, so enrichment costs no extra request and needs no option enabled, including with grabRealInventory off. A collection crawl gets it whenever grabRealInventory is on, because the detail fetch that option pays for carries these fields too, at no additional request and no additional charge. A plain collection crawl with grabRealInventory off fetches no product JSON and so returns none of them.

Storefront detail

A product's storefront payload publishes things none of the JSON endpoints do:

FieldWhat it is
sellingPlanGroups, requiresSellingPlanSubscriptions, pre-orders and deposit plans the merchant offers, with each plan's options. requiresSellingPlan is true when the product cannot be bought outright.
sellingPlanAllocations (per variant)What that variant costs under each plan, including the per-delivery price on a recurring one.
Video mediamedias entries typed Video. The other endpoints publish images only, so without this option a product's videos are invisible.

Which crawls get it. A product URL always does, with no option to enable: that payload is where a product URL's stock availability comes from, because the single-product JSON carries no availability flag at all. A collection crawl gets it when grabStorefrontDetail is on — there the availability is already in the listing, so the payload is bought purely for the fields above. It costs one extra request per product, is not billed as a separate event, and is off by default; maxStorefrontFetches caps it, since one start URL can mean hundreds of products.

How availability is resolved on a product URL. Three sources, strongest first: the storefront payload's per-variant flag (joined on variant id), then the product page's embedded schema.org data (joined on SKU), then the JSON API's own flag. The page is only fetched when the payload yields nothing for any variant — a partial answer is kept as-is rather than paying for a rendered page, which is roughly seventy times more data than the payload.

Inventory is narrower than enrichment. quantityAvailable (real stock count), inventoryTracked and inventoryPolicy need grabRealInventory and a store that publishes stock numbers in its storefront JSON — many do not, and those are simply omitted and not billed. The enrichment fields above come from the same request but are published by a wider set of stores, so a store that reveals no stock counts at all can still return complete barcode, weight and quantity-rule data.

💳 Pricing

This actor uses Apify's pay-per-event model: you are charged for the results it produces, not for how long it runs.

EventPriceChargedNotes
product$0.0018Once per product saved to the datasetThe main unit of value. Volume tiers below.
collection$0.0008Once per collection saved to the datasetOnly /collections crawls emit these.
recommends$0.0010Once per recommended productOff by default (maxRecommendationsPerProduct is 0).
real-inventory$0.0012Once per product with a real stock countOff by default (grabRealInventory).

product is tiered by your total monthly Apify spend, so the more you run the less each product costs:

TierFREEBRONZESILVERGOLDPLATINUMDIAMOND
Per product$0.0018$0.0016$0.0014$0.0012$0.0012$0.0012

At the GOLD tier, 100,000 products costs $120.

Controlling your spend

  • maxRequestsPerCrawl caps how many records are saved, so it caps product/collection charges directly. Default 100; 0 means unlimited.
  • Max total charge (set per run or per task in the Apify UI) is a hard ceiling. When it is reached the actor stops of its own accord and the run finishes with everything it had already saved — it is not cut off mid-fetch.
  • Duplicates are never billed twice. A product reached through several start URLs or through overlapping collections, or re-visited after a failed request is retried, is saved and charged once per run.

⚠️ What this Actor does not do

  • It does not merge SKUs. On certain Shopify websites, multiple SKUs may be visually merged into a single product page. This scraper treats each SKU as an individual product entry and does not perform any merging of these SKUs.
  • It does not return 3D or AR models. Shopify also publishes 3D/AR model media. The output spec has no type for it, so those entries are skipped rather than mislabelled as images.
  • It does not guess stock counts. A real count needs grabRealInventory and a store that publishes its stock numbers. A store that does not publish them gets no count and is not billed for one.

🧰 Other Actors by autofacts

Apify only auto-recommends Actors in the same category, so here are the ones that actually pair with this scraper:

ActorWhat it's for
Shopify Store LeadsFind and qualify the stores first — catalog size, apps, theme, contacts — then feed the domains into this Actor
Schema Markup Scraper & SEO AuditorAudit a store's structured data, Open Graph tags and canonical setup
Sephora Product Scraper (Global)Non-Shopify beauty retail, 20 storefronts
Macy's ScraperDepartment-store catalog and pricing
Universal Web PrinterRender any product page to PDF/PNG for archiving or evidence
WooCommerce ScraperThe same job on WooCommerce stores, with a matching record shape

All of them: apify.com/autofacts


🛠️ Troubleshooting

IssuePossible CauseSolution
0 Results FoundThe site may not be Shopify-based or has strong anti-bot protection.Verify with the /admin trick. Try using residential proxies.
Access Denied / 403Your IP has been flagged.Enable useApifyProxy and ensure you have sufficient proxy quota.
Incorrect PriceRaw integer format.Prices are integer minor units — 2000 means 20.00 in source.currency.
Wrong currencyThe store runs Shopify Markets, where ?currency=XYZ is ignored.Use the store's locale-prefixed URL instead (e.g. /nl-nl/collections/...), which selects the market and its currency. Verified on one Markets store; ?currency= still works on stores that are not using Markets.
Run stopped earlyThe run's Max total charge limit was reached.Expected behaviour — the actor stops cleanly and keeps everything it saved. Raise the limit, or lower maxRequestsPerCrawl to fit the budget.

🤖 Use with AI agents

This Actor is callable as a tool by any MCP-capable agent — Claude, Cursor, VS Code — or by your own code, with no wrapper and nothing extra to deploy.

Connect over MCP

https://mcp.apify.com?tools=autofacts/shopify

In a client that reads an mcpServers configuration block:

{
"mcpServers": {
"apify": {
"url": "https://mcp.apify.com?tools=autofacts/shopify",
"headers": { "Authorization": "Bearer YOUR_APIFY_TOKEN" }
}
}
}

The agent reads this Actor's parameters and their descriptions straight from the input schema, and the hosted server infers the result field types from the dataset schema — so a model knows what to send and what comes back before it ever calls anything.

Or call the API directly

curl -X POST "https://api.apify.com/v2/acts/autofacts~shopify/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
-H 'Content-Type: application/json' \
-d '{"startUrls": [{"url": "https://kith.com/collections/kith-tops"}]}'

The response body is the dataset records described above.