Shopify Scraper avatar

Shopify Scraper

Pricing

from $3.00 / 1,000 product results

Go to Apify Store
Shopify Scraper

Shopify Scraper

Shopify store scraper, Shopify store finder: find Shopify stores by niche, search Shopify stores for a Shopify stores list with public emails, find Shopify store theme; export Shopify store data. Shopify catalog scraper, Shopify product data scraper (Shopify store catalog, Shopify products JSON).

Pricing

from $3.00 / 1,000 product results

Rating

5.0

(2)

Developer

Flash Scrape

Flash Scrape

Maintained by Community

Actor stats

0

Bookmarked

14

Total users

3

Monthly active users

2 days ago

Last modified

Share

Shopify Store Scraper finds Shopify stores by niche and exports any store's full catalog, in one Actor for $0.003 per result ($3 per 1,000 rows), the same flat price on every Apify plan. Type a niche like coffee beans and this Shopify scraper searches Shop by Shopify, confirms every candidate on its own /meta.json and returns one row per store with its public email, theme, catalogue size, country, currency and social handles — a Shopify stores list you can filter by country, catalogue size or email, and a filtered-out store is never billed. Then paste any store's URL (any Shopify website, not just your own) to export Shopify store data from 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. No login, no API key, no browser automation, no code — export to CSV, Excel or JSON minutes later. Built for Shopify lead lists, competitor price tracking, dropshipping product research and store qualification.

✨ What you get

  • Find Shopify stores by niche — each keyword is one search on Shop by Shopify (shop.app) and reads one results page. Measured 2026-09-25: coffee beans gave 17 confirmed stores from one page, yoga-mat phrasings 8–19 each, soy candles 9, plain candles 1 (one large candle store filled the page). Several phrasings of the same niche find more stores; a store found by several keywords is delivered once.
  • One row per store, confirmed on its own /meta.json — record_type: "store_lead" with primary_email and emails, theme, published_products_count (the store's TOTAL catalogue size), city / province / country, store_currency, Instagram / Facebook / TikTok / X / YouTube handles, the keyword that found it and up to three of its matching products. In the 2026-09-25 sample, 7 of 12 stores (58%) showed a public email.
  • Filter the lead list before you pay for it — storeCountry (ISO-2 codes), minProducts (catalogue size, from the store's own count) and requireEmail drop the stores you do not want: a dropped store is not delivered and not billed, and the run's status message says how many were dropped and why.
  • Then 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.
  • 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. Prices are pinned to one market (default US) and store_currency is returned, so a price is always interpretable.
  • 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.

Copy-paste input — this is the default run; nicheKeywords is all a store-finding run needs:

{ "nicheKeywords": ["coffee beans"], "maxStores": 50, "includeContacts": true }

Add "storeCountry": ["US", "CA"], "minProducts": 100 or "requireEmail": true to filter the list. To export a store's catalog instead, send storeUrls without nicheKeywords - every API call and saved task that leaves nicheKeywords out runs the catalog export exactly as before, {} included. In the console form, type your URLs under Store URLs; to export the sample https://www.kith.com itself from the form, empty the niche keywords.

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

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

A Shopify API for agents: one call finds Shopify stores by niche (a Shopify store finder) or exports a store's Shopify products catalog.

  • 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 finds Shopify stores and bills at most 20 rows, $0.06 ($0.003 per row on every plan) plus at most $0.00005 for the start; callOptions.maxTotalChargeUsd caps spend:
{"nicheKeywords": ["yoga mats"], "maxStores": 20}
  • Catalog: for a store's products, leave nicheKeywords out and send {"storeUrls": ["https://www.kith.com"], "maxProducts": 20}: at most 21 rows, 20 products plus one record_type: "store" row.
  • 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; pass limit: 21 for the catalog call). Store-finder rows (record_type: "store_lead") carry primary_email and published_products_count.

What it does

  • Finds Shopify stores by niche (the default run) — searches Shop by Shopify once per keyword, confirms each candidate 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, failed searches and empty keywords cost nothing.
  • Filters the 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.
  • Switches to the catalog export when you type Store URLs — leave nicheKeywords untouched (the sample niche) and type storeUrls, and the run exports those stores' catalogs instead; an API call or saved task that does not send nicheKeywords always keeps its catalog run (the field has a form prefill, no API default). A typed nicheKeywords means a store-finding run, and storeUrls is then ignored with a note; an empty nicheKeywords means a catalog run. In the form, storeUrls cleared next to the untouched sample niche runs the store finder; empty the niche keywords to export the sample kith.com.
  • 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— (form prefill ["coffee beans"])Find-stores mode: products or niches to find Shopify stores for (coffee beans, yoga mats). Up to 20 keywords, one Shop by Shopify search each. The default run searches the sample niche.
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. Mode rule: leave nicheKeywords untouched and type storeUrls → catalog run; change nicheKeywords and storeUrls is ignored. An API call or saved task without nicheKeywords always runs the catalog.
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.
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, …) and the Shop by Shopify market to search. 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

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. A free Apify plan is enough to try it on a full mid-size store.

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)

Don't have a Shopify stores list yet? This is the default run: Find stores by niche (nicheKeywords) searches Shopify stores by niche and the Actor finds Shopify stores that sell those products, instead of scraping stores you already know:

{ "nicheKeywords": ["yoga mats", "cork yoga mat", "travel yoga mat"], "maxStores": 50 }
  • Where the stores come from — each keyword is one search on Shop by Shopify (shop.app), Shopify's own shopping marketplace. One search reads one results page: in our test runs that was 1 to 19 distinct stores per keyword (travel yoga mat 19, coffee beans 17, candles 1 because one large candle store filled the whole page).
  • More stores = more phrasings — five phrasings of "yoga mat" found 33 different stores. Stores found by several keywords are delivered once.
  • Only real Shopify stores are delivered — every candidate is checked on its own /meta.json; one that does not answer as a live Shopify store is skipped and not charged.
  • 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), up to three of its matching products (matched_products) 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). In two test runs 12 of 20 and 10 of 12 stores showed a public email; phone numbers are not returned.
  • Market — Shop by Shopify localises results to the visitor's country. With Apify Proxy on and no proxy country chosen, the run pins the exit to market (default US); each row's shop_app_market and the status message say which market was actually served.
  • 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.

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.

This Actor pins the market (default US) and returns store_currency on the store row, so a price is always interpretable. 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 (see the FAQ for the workaround).
  • 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. If you need descriptions, pair it with an extraction actor on the product URLs (see Related actors below).

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 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 — typical response within 1 business day.