Shopify Scraper avatar

Shopify Scraper

Pricing

from $3.00 / 1,000 product results

Go to Apify Store
Shopify Scraper

Shopify Scraper

Shopify products scraper and Shopify store scraper: export any store's products, prices, SKUs, stock and theme. Shopify catalog scraper, Shopify product data scraper, Shopify store catalog, Shopify products JSON. Shopify price monitor, Shopify price tracker, Shopify stock monitor: get only changes.

Pricing

from $3.00 / 1,000 product results

Rating

5.0

(2)

Developer

Flash Scrape

Flash Scrape

Maintained by Community

Actor stats

0

Bookmarked

17

Total users

2

Monthly active users

9 days ago

Last modified

Features

Limited permissions

Share

Shopify Store Scraper exports any Shopify store's full catalog and watches its prices and stock, in one Actor for $0.003 per result ($3 per 1,000 rows), the same flat price on every Apify plan. Paste a store's URL (any Shopify website, not just your own) and this Shopify scraper reads its public Shopify products JSON feed (/products.json), paginated up to the 25,000 products Shopify's public feed serves per store: every product row carries 17 columns (20 in one-row-per-variant mode, which adds per-variant sku, price, compare_at_price, available and grams), plus an optional store-intelligence row with theme, currency, country, catalogue size and shipping reach. Turn on Only changed products and schedule it: this Shopify price monitor then delivers only new products and price or stock changes, and unchanged products are not delivered and not billed. A Shopify catalog scraper with no login, no API key, no browser automation, no code — export to CSV, Excel or JSON minutes later. Built for competitor price and stock tracking, dropshipping product research and store qualification, and it also finds popular Shopify stores by niche (nicheKeywords).

✨ What you get

  • Scrape any store's full catalog — paste its URL and the Shopify product catalog comes out of the store's public /products.json, 250 products per request, page after page, up to the 25,000 products that feed serves per store: one row per product with a price range (price_min / price_max, in_stock, variants_count), or one row per variant with sku, price, compare_at_price, available and grams — ideal for price and stock monitoring.
  • Price & stock monitor — turn on onlyChangedProducts and schedule the run. The first run of a store is its baseline; later runs deliver only new products and price, compare-at price or stock changes, each tagged with change_types. Unchanged products are not delivered and not billed. See "Price & stock monitor" below.
  • Store intelligence most Shopify scrapers don't give you — one extra record_type: "store" row per catalog store with theme, currency, country, catalogue size, shipping reach and social handles, so you learn whether a store has 40 products or 25,000 before paying to scrape the catalogue.
  • Many stores in one run, failures skipped, flat rows — a store that fails (password-protected, feed disabled, not actually Shopify) is logged and skipped, a store that answers HTTP 429, 403 or 430 (Shopify's throttle and bot refusals) is retried on up to 5 proxy IPs before it is skipped, and both row shapes export to CSV, Excel or JSON with no post-processing.
  • Find Shopify stores by niche — type a word like coffee and get the popular Shopify stores whose web address contains it, each confirmed live on its own /meta.json, with public email, country and catalogue size. Its section below says where the stores come from and what it cannot find.

Copy-paste input — this is the default run; storeUrls is all a catalog run needs:

{
"storeUrls": ["https://www.kith.com"],
"maxProducts": 50,
"oneRowPerVariant": false,
"includeStoreProfile": true,
"includeCollections": false,
"market": "US",
"proxyConfiguration": { "useApifyProxy": true }
}

Add "onlyChangedProducts": true and schedule the run for the price & stock monitor. Leave nicheKeywords out or empty: every run without it is a catalog run, {} included.

Use it from AI agents (Claude, ChatGPT, Cursor) via Apify MCP

A Shopify API for agents: one call exports a store's Shopify products catalog with prices, SKUs and stock. nicheKeywords finds stores by niche.

  • Connect: add https://mcp.apify.com as a custom connector in Claude or ChatGPT (Developer mode), or install the Apify plugin in Cursor; sign in with OAuth or send Authorization: Bearer <APIFY_TOKEN>.
  • Call: call-actor with flash_scraper/shopify-store-scraper, or load this Shopify scraper as its own tool with https://mcp.apify.com?tools=flash_scraper/shopify-store-scraper. This input exports a store's catalog and bills at most 20 rows, $0.06 ($0.003 per row on every plan) plus at most $0.00005 for the start: 19 products plus one record_type: "store" row. callOptions.maxTotalChargeUsd caps spend:
{"storeUrls": ["https://www.kith.com"], "maxProducts": 19}
  • Monitor: add "onlyChangedProducts": true and repeat the call: after the first run, only new products and price or stock changes come back.
  • Wait: call-actor waits at most 45 s and returns the run status and storage IDs, not rows; poll get-actor-run until it finishes. Its statusMessage gives the delivered count, or why nothing was delivered.
  • Read: get-dataset-items with the run's datasetId returns the rows (default limit 20 fits this call). Product rows carry price_min, price_max and in_stock; the store row has published_products_count.

What it does

  • Finds Shopify stores by niche — searches the Chrome UX Report list of the 1 million most-visited websites for web addresses that contain your keyword, keeps those on Shopify's storefront network (a DNS check), confirms each store on its own /meta.json (a myshopify_domain must be present), reads its public email from the homepage and /pages/contact, and delivers one record_type: "store_lead" row per confirmed store. Unconfirmed candidates and keywords without stores cost nothing.
  • Filters the store list by country, catalogue size and email — storeCountry, minProducts and requireEmail are checked after a store is confirmed and before it is delivered, so a filtered store is neither in the dataset nor on the bill; filtered stores do not count toward maxStores.
  • Picks the mode from nicheKeywords — empty or absent means a catalog run on storeUrls: the form's default, and every API call or saved task without it, {} included. Typed niche keywords start a store search instead, and storeUrls is then ignored with a note. A form saved before 2026-10-26 may still hold the old sample niche coffee beans: empty it for a catalog run.
  • Reads each store's public /products.json feed and paginates the full catalog — the same product data Shopify serves to any visitor, 250 products per request, page after page, until the store runs out or Shopify's own feed limit is reached: the public feed serves at most 100 pages of 250, i.e. 25,000 products per store, so a bigger catalog delivers its first 25,000 products and the run marks it as capped. kith.com, www.kith.com or a full product URL all resolve to the correct store base.
  • Adds store intelligence to every catalog store — one extra record_type: "store" row with theme, currency, country, catalogue size, shipping reach and social handles (details below).
  • Rides out throttling and bot refusals — a store that answers HTTP 429, 403 or 430 is retried on up to 5 proxy IPs, resuming at the page it stopped on; a store that still refuses is skipped and named in the run's status message, and the rest of the run continues.

Input

FieldTypeDefaultDescription
nicheKeywordsarray—Find-stores mode: words to find popular Shopify stores by (coffee, candle), up to 20 keywords. A store is found only when its web address contains the word; a multi-word keyword searches its longest word, and a word of 4 letters or fewer must also be in the store's name or description. Leave it empty for a catalog run.
maxStoresinteger50Find-stores mode: stop after this many confirmed stores (1–500). Filtered-out stores do not count.
includeContactsbooleantrueFind-stores mode: return public emails from each store's homepage and /pages/contact. Off = no email columns (profile, theme and socials still returned).
storeCountryarray[]Find-stores filter: keep only stores whose Shopify country (from its /meta.json) is one of these ISO-2 codes, e.g. ["US", "CA"] (one entry US, CA works too; USA and UK are read as US and GB; anything else, like United States, is ignored and the run says so). A store with no declared country is dropped when this is set. Empty = every country.
minProductsinteger0Find-stores filter: keep only stores with at least this many published products (the store's own published_products_count; none reported = 0). 0 = no minimum.
requireEmailbooleanfalseFind-stores filter: keep only stores where a public email was found. Needs includeContacts; with contacts off it is ignored and the run says so. A store whose homepage or contact page could not be read (throttled or slow) is skipped as a retry case, not billed.
storeUrlsarray["https://www.kith.com"]Catalog mode: one or more Shopify store URLs. Bare domains work too. Keep nicheKeywords empty: typed niche keywords start a store search instead and storeUrls is ignored.
maxProductsinteger50Catalog mode: products per store. 0 = the entire catalog, up to the 25,000 products Shopify's public feed serves per store — note that a large store takes about 100 requests, which can trigger HTTP 429 throttling; start with a limit, raise it once the store tolerates it.
oneRowPerVariantbooleanfalseCatalog mode: ON = one row per variant (best for price/SKU monitoring). OFF = one row per product with a price range.
onlyChangedProductsbooleanfalseCatalog mode: price & stock monitor. The first run of a store is the normal catalog (the baseline); later runs deliver and bill only new products and price, compare-at price or stock changes. See "Price & stock monitor" below.
includeStoreProfilebooleantrueCatalog mode: adds the record_type: "store" intelligence row per store.
includeCollectionsbooleanfalseCatalog mode: adds the store's published collections (title, handle, product count, URL — up to 250) to the store row.
marketstring"US"Both modes: two-letter market to price in (US, GB, DE, …). See "Prices are pinned" below.
proxyConfigurationobjectApify Proxy (datacenter)Both modes: keep this on: Shopify blocks the platform's bare run IP. The standard datacenter group is verified working — residential is not needed.

Output columns

Every product/variant row carries: store, product_id, title, handle, url, vendor, product_type, tags, first image URL, images_count, and Shopify's created_at / published_at / updated_at timestamps.

ColumnModeAvailability
store, product_id, title, handle, urlbothevery row
vendor, product_type, tagsbothstraight from the store's feed; empty when the merchant left them blank
image, images_countbothimage is null for products with no images
variants_count, price_min, price_max, in_stockone row per productprice_min/price_max are null if no variant has a parseable price
variant_id, variant_title, price, available, gramsone row per variantevery variant row
skuone row per variantempty when the merchant never assigned one
compare_at_priceone row per variantnull unless the item is marked down
change_types, previous_price, previous_compare_at_price, previous_available, changesonlyChangedProducts runswhy the row was delivered and the values on the previous run (see the monitor section)

Both shapes are flat, so CSV export needs no post-processing.

Pricing — what does it cost per 1,000 products?

$0.003 per dataset row — $3.00 per 1,000 products (or variants) — plus a fraction-of-a-cent Actor start fee ($0.00005 per run start at the default 512 MB). The price is the same flat rate on every Apify plan. You pay only for rows actually delivered: a URL that is not a live Shopify store, and a run that returns nothing, cost nothing beyond that start fee; a Shopify store whose product feed is disabled or throttled still delivers (and bills) its one record_type: "store" row when the store profile is on.

Tip: variant mode produces more rows than product mode for the same store (one per SKU), so pick the shape that matches what you actually need.

Store intelligence — what other Shopify scrapers don't give you

Most Shopify scrapers return product rows and stop. With Include store profile on (the default), every store also yields one record_type: "store" row describing the merchant itself, read from the store's public /meta.json and storefront:

FieldExample
store_name, store_city, store_province, store_countryMisen, Brooklyn, New York, US
store_currency, money_formatUSD, ${{amount}}
myshopify_domainmisenco.myshopify.com
published_products_count342 — the store's TOTAL catalogue size, without scraping it
published_collections_count218
ships_to_country_count, ships_worldwide134, true
accepted_card_brands, offers_shop_pay_installments["visa","master",...], true
theme_name, theme_id1.3.0 [New Relic/Speedsize], 198967558225
instagram, facebook, tiktok, twitter, youtubemisenkitchen

The /meta.json fields are returned whenever the store serves that endpoint. Theme and social handles are read from the storefront HTML and are best-effort: if Shopify throttles the storefront page after all IP rotations, they come back empty and the run logs a warning — the /meta.json fields are still present.

How do I check what theme a Shopify store is using?

Shopify injects Shopify.theme = {...} into every storefront page, so theme_name and theme_id on the store row come back without any extension or manual source-viewing — for every store in your list at once. theme_name is the merchant's own label for the installed theme (like 1.3.0 [New Relic/Speedsize] in the table above), so it is often not the theme's public name such as Dawn or Prestige.

How many products does a Shopify store have?

published_products_count answers it from a single request: you learn whether a store has 40 products or 25,000 before paying to scrape the catalogue. Run with a tiny maxProducts cap to qualify a long list of stores cheaply, then scrape only the ones worth it.

Find Shopify stores by niche (store leads)

Find stores by niche (nicheKeywords) finds popular Shopify stores in a niche, instead of scraping stores you already know:

{ "nicheKeywords": ["coffee", "candle"], "maxStores": 50 }
  • Where the stores come from — the Chrome UX Report (CrUX), Google's public list of the 1 million websites Chrome users visit most (the global list plus the newest US list). The finder keeps the web addresses that contain your keyword, keeps those that point at Shopify's storefront network (a DNS lookup, no visit), and then confirms each store on its own /meta.json.
  • What to expect — measured 2026-10-26: coffee 1,240 popular web addresses → 546 on Shopify (24 of 24 sampled confirmed on /meta.json), candle 277 → 134, yoga 426 → 44 (most yoga addresses are studios, not shops). The status message says how many Shopify stores the list had for each keyword, so you know the pool.
  • Limits — it finds only stores whose web address contains the word: coffee finds brandcoffee.com, never a roaster called bluecopper.com. Popular sites only (the top 1 million). About 7% of Shopify stores sit behind their own CDN and are missed by the DNS check. A multi-word keyword searches its longest word (coffee beans searches coffee, addresses with both words first), and a plural is searched without its final s (candles searches candle, pets searches pet; addresses with the word as you typed it are checked first). Web addresses are searched in Latin letters and digits, so type coffee, not кофе. A word of 4 letters or fewer also matches inside longer words (tea in steam, art in party), so for those a store is delivered only when its name or description has the word.
  • Order — addresses that hold every word of the keyword first, then the most popular (crux_rank). Several keywords take turns, so maxStores spans all of them; a store found by several is delivered once.
  • Only live Shopify stores are delivered — every candidate is checked on its own /meta.json: a closed store (HTTP 402), a 404 or a non-Shopify answer is skipped and not charged, and a store that answers only on www. is asked there.
  • What each row holds — one record_type: "store_lead" row per store: store, store_name, store_domain, myshopify_domain, city / province / country, store_currency, store_description, total catalogue size (published_products_count), collection count, countries shipped to, theme, Instagram / Facebook / TikTok / X (twitter) / YouTube handles, the keyword that found it (found_by_keyword), its popularity bucket (crux_rank: 1000 = among the 1,000 most-visited sites, up to 1000000) and, with Include contacts on, the public emails found on its homepage and contact page (primary_email is the one on the store's own domain when there is one). matched_products and shop_app_market stay as empty columns: the list names no products. Phone numbers are not returned.
  • Credit — store list: Chrome UX Report (CrUX), Google, published under CC BY 4.0 (developer.chrome.com/docs/crux/methodology), read from the zakird/crux-top-lists mirror. The list is only used to find candidates; it is never part of the output.
  • Filters — storeCountry (ISO-2 codes, e.g. ["US", "CA"], compared with the country in the store's own /meta.json), minProducts (the store's own published_products_count) and requireEmail (needs Include contacts) drop stores after they are confirmed and before they are delivered. A dropped store is not billed and does not count toward maxStores; the status message counts the dropped stores per reason, and if every confirmed store was dropped it says so and which filter to loosen.
  • Price — the same $0.003 per delivered row ($3 per 1,000 stores). Filtered-out stores, found-but-unconfirmed stores, failed searches and empty keywords cost nothing.

Then feed any store you like back into Store URLs to export its catalogue.

Price & stock monitor (schedule it)

Turn on Only changed products (onlyChangedProducts) and put the run in an Apify Schedule, for example daily.

  • The first run of a store is its baseline. You get the normal catalog, billed as usual, each row tagged change_types: ["baseline"].
  • Later runs deliver only what changed: a new product or variant, a price up or down, a new compare-at price, back in stock or out of stock. change_types says which. An older product read for the first time (it entered your maxProducts window, or an earlier run was cut off before it) is tagged first_seen, not new_product. Variant rows also carry previous_price, previous_compare_at_price and previous_available; product rows carry a changes list.
  • Unchanged products are not delivered and not billed. A run with no changes delivers nothing and says so in its status; each run still pays the small per-run start fee. The store profile and collections rows are delivered once per store.
  • One memory per store and market, in your own Apify account (a named key-value store, shopify-store-scraper-monitor). kith.com, www.kith.com and http://kith.com share one memory. Adding a store to the list does not reset the others. A change cut off by your charge limit is delivered on the next run.
  • Set maxProducts to 0 to watch the whole catalog; with a limit, only the products read are compared. Removed products are not reported.
  • The memory holds up to 100,000 variants per store (fewer if the record would pass 8 MB). A bigger store keeps watching the variants it remembers and still reports new products; changes on the variants beyond the memory are not reported, and the status says how many.

Prices are pinned to one currency — read this if you compare stores

Shopify localises prices to whichever IP requests the page. Measured: the same variant returned 1827.00 from a Moroccan exit node and 190.00 (USD) with the market pinned. Because cloud scrapers rotate proxy IPs between countries, an unpinned scrape can return a different currency on different rows of the same run — which quietly ruins any price comparison built on it.

Set Market to GB, DE, CA etc. to price in that market instead.

How does it handle the products.json limit and pagination?

Shopify caps /products.json at 250 products per request, and the storefront endpoint ignores since_id — so this Actor paginates with 1-indexed page numbers, the only reliable cursor, until the store runs out. Shopify serves at most 25,000 products per store this way: past page 100 at 250 per page it answers HTTP 400 ("Page * Limit exceeds the 25000 limit"). A bigger catalog delivers those first 25,000 products, billed per row like any other, and the run log marks the store as capped (so does the status message, unless failed stores fill it); maxProducts sets your own tighter cap. Rows are saved page by page as they are read, so a large catalog never has to fit in the run's memory.

How do I export a Shopify store's products to CSV or Excel?

Run the Actor, open the run's Dataset tab and pick CSV, Excel, JSON or HTML — or pull the dataset programmatically via the Apify API. No Shopify admin access or export app needed: this works on any store, not just your own.

Use cases

  • Competitor price tracking — schedule a daily run over competitors' stores and diff prices, new products, and stock status over time.
  • Dropshipping product research — pull a winning store's catalog with vendors, tags and product types to spot trending products and suppliers.
  • Catalog migration and backup — export a store's product list without private API keys or admin access.
  • Market analysis — scrape every store in a niche and aggregate by product_type, vendor, or price band.
  • Store qualification at scale — run with the store profile on and a tiny product cap to get each store's country, currency, catalogue size and shipping reach cheaply.
  • Competitor benchmarking — compare catalogue size, collection count, shipping reach and tech (theme) across several merchants side by side.

Example output

A real sample from a live run (default product mode):

titlevendorproduct_typeprice_minprice_maxin_stock
Men's Cruiser Terralux - Anthracite (Da…AllbirdsShoes135135false
Women's Wool Runner NZ Mid Waterproof -…AllbirdsShoes160160true
Free Returns Coveragere:doreturn,package_protection0.82.98true
Women's Wool Runner-up Mizzle - Hazy In…AllbirdsShoes6060true

Example row (default product mode):

{
"store": "https://www.allbirds.com",
"product_id": 4547069443119,
"title": "Men's Wool Runners",
"handle": "mens-wool-runners",
"url": "https://www.allbirds.com/products/mens-wool-runners",
"vendor": "Allbirds",
"product_type": "Shoes",
"tags": "mens, wool, runners",
"image": "https://cdn.shopify.com/s/files/1/1104/4168/products/wool-runner.jpg",
"images_count": 6,
"created_at": "2020-03-18T09:12:44-07:00",
"published_at": "2020-03-20T08:00:01-07:00",
"updated_at": "2026-06-28T02:14:09-07:00",
"variants_count": 14,
"price_min": 110.0,
"price_max": 125.0,
"in_stock": true
}

What this Actor does NOT do

  • No inventory quantities — Shopify's public feed carries no stock counts; available/in_stock are true/false only.
  • No product descriptions — the HTML description body is not in the rows.
  • No sales, revenue or best-seller rankings — Shopify does not publish sales numbers. Many stores expose a "best sellers" collection you can spot via includeCollections, but this Actor does not order products by sales.
  • No reviews, and no Shopify App Store data — it scrapes merchant storefronts, not apps.apple/shopify listings.
  • No password-protected storefronts — those are skipped with a warning, never logged into.

Tips / FAQ

Is this legal? It reads each store's own public /products.json feed — data Shopify serves to any anonymous visitor. No login, no private data, no paywall bypass. As always, use scraped data responsibly and within applicable laws and the store's terms.

Do I need a proxy or an API key? No API key, no login, no headless browser. A proxy, yes — Shopify blocks Apify's bare run IP, so the Actor routes through Apify Proxy by default. The standard datacenter group is verified working and costs far less than residential; just leave the default on.

Does it work on every Shopify store? Most standard stores, yes. Password-protected storefronts and stores that have disabled the public product feed return an error for /products.json; those are logged with a warning and skipped (their record_type: "store" row is still delivered when the store profile is on, since /meta.json answered), and the rest of the run continues. Non-Shopify URLs deliver nothing and are not billed. A few large stores (allbirds.com is a common example) throttle or refuse shared cloud IPs aggressively — the Actor retries each 429, 403 or 430 on up to 5 proxy IPs, resuming at the page it stopped on, before giving up on that store. A store that still refuses is named in the status message (as many names as fit); a catalog cut off part-way delivers (and bills) only the products read before it stopped and is named as incomplete. The run log has one outcome line per store, with its HTTP codes and the IPs tried.

Does it include product descriptions? No — rows include title, vendor, type, tags, timestamps, image URL and price/variant data, but not the HTML description body.

How do I track prices over time? Turn oneRowPerVariant ON so every SKU gets its own row with price and compare_at_price, then schedule the actor (Apify Schedules) and compare datasets between runs — a competitor price tracker in two clicks.

How do I get only the price and stock changes? Turn on onlyChangedProducts and schedule the run. The first run of each store delivers its catalog as a baseline; every later run delivers only new products and price, compare-at price or stock changes. You pay per delivered row, plus the small per-run start fee; a run with no changes bills no results. If a store's memory cannot be read, that store delivers nothing in that run - it is never re-delivered as a new baseline.

How fast is it, and are there limits? It fetches 250 products per request, so even multi-thousand-product catalogs finish in minutes. Shopify's public feed stops at 25,000 products per store (100 pages of 250); a bigger catalog delivers its first 25,000 and is marked as capped in the run. maxProducts lets you set your own tighter cap.


Found a bug or need a feature? Open an Issue on this actor's Issues tab.