Shopify Store Catalog & Pricing Extractor avatar

Shopify Store Catalog & Pricing Extractor

Pricing

from $0.49 / 1,000 results

Go to Apify Store
Shopify Store Catalog & Pricing Extractor

Shopify Store Catalog & Pricing Extractor

Extract a Shopify store's product catalog with titles, prices, compare-at prices, variants, SKUs, images and stock availability from any custom or myshopify.com domain.

Pricing

from $0.49 / 1,000 results

Rating

0.0

(0)

Developer

Nimbryxo-studio

Nimbryxo-studio

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

5 days ago

Last modified

Share

Turn any public Shopify storefront into a structured product feed. Point the actor at one store or a list of stores, choose a mode, and get prices, compare-at prices, variants, stock status, images, options, collection metadata and optional barcode/weight/inventory detail.

No Shopify account, admin API key, cookie or login is required. The actor reads the same public storefront JSON that a shopper's browser receives. Proxy setup is also optional: requests go direct first, and the actor switches to Apify's datacenter proxy automatically only after a real 403 or 429 block.

Modes

ModeWhat it doesSource endpoint
catalogFull published product feed for every store/products.json?limit=250&page=N
collectionProducts from one or more collection handles/URLs/collections/{handle}/products.json
productExact products by handle or product URL/products/{handle}.json
searchIn-store product search, up to 10 results per store/search/suggest.json
collectionsCollection directory with titles, handles and reported counts/collections.json?limit=250&page=N
autoMixed startUrls; each URL selects its own jobDetected per URL

auto treats /products/<handle> as a product job, /collections/<handle> as a collection job, /collections as a collection-directory job, and any other store URL as a catalog job.

Input

FieldTypeDefaultNotes
modeselectcatalogOne of the six modes above.
storeUrlsstring list-Store domains or full URLs; *.myshopify.com is supported. The prefill uses deathwishcoffee.com.
startUrlsstring listemptyMixed store/product/collection URLs. Used mainly by auto.
collectionHandlesstring listemptyHandles or collection URLs for mode=collection.
productHandlesstring listemptyHandles or product URLs for mode=product.
searchQuerystringemptyRequired for mode=search.
currencystringstoreISO 4217 code such as USD or EUR; store uses /meta.json.
countrystringemptyOptional ISO-2 country passed as ?country=.
vendorstringemptyCase-insensitive vendor substring filter.
productTypestringemptyCase-insensitive product-type substring filter.
tagstringemptyKeep products with a tag containing this substring.
minPricenumberemptyDrop products below this lowest-variant price.
maxPricenumberemptyDrop products above this lowest-variant price.
availableselectanyany, available or unavailable.
maxItemsinteger10001-50,000 dataset rows; variant rows count individually when flattening.
maxPagesinteger1001-100 pages per feed. Shopify refuses offsets above 25,000.
includeDetailsbooleanfalseOne extra /products/{handle}.json request per product for barcode, weight and inventory.
flattenVariantsbooleanfalseEmit one recordType: variant row per variant instead of nested product rows.
proxyConfigurationobjectemptyDirect requests are always tried first. After a 403/429 the actor retries via AUTO, or via this configuration if provided.

The smallest useful run is also the daily prefill: deathwishcoffee.com, three records, no detail enrichment.

Examples

Full catalog from one store:

{
"mode": "catalog",
"storeUrls": ["https://deathwishcoffee.com"],
"currency": "USD",
"maxItems": 100
}

Collection and sale filters:

{
"mode": "collection",
"storeUrls": ["allbirds.com"],
"collectionHandles": ["mens-sale"],
"maxPrice": 100,
"available": "available",
"flattenVariants": true
}

Exact products by URL:

{
"mode": "product",
"startUrls": [
"https://deathwishcoffee.com/products/valhalla-java",
"https://deathwishcoffee.com/products/dark-roast"
],
"includeDetails": true
}

Search a set of stores:

{
"mode": "search",
"storeUrls": ["allbirds.com", "rothys.com"],
"searchQuery": "wool runner",
"maxItems": 20
}

Mixed URLs in one run:

{
"mode": "auto",
"startUrls": [
"https://allbirds.com",
"https://allbirds.com/collections/mens",
"https://allbirds.com/products/wool-runner"
]
}

Product output

Product records use recordType: "product" and contain:

  • productId and legacy id, handle, title, url
  • vendor, productType, tags
  • bodyHtml (raw) and description (plain text)
  • createdAt, updatedAt, publishedAt, publishedScope
  • price, priceMin, priceMax, compareAtPrice, compareAtPriceMax, discountPercent, onSale, currency
  • available, variantCount, availableVariantCount
  • variants: variant id, title, SKU, price, compare-at price, availability, option values and named options
  • images (URL list), imageDetails (URL/width/height/position), featuredImage
  • options: option names and their values
  • store: {url, name, currency}, plus legacy storeUrl
  • source, sourceUrl, scrapedAt, recordType

With includeDetails enabled, variants can also contain barcode, weight, weightUnit, grams, inventoryQuantity, inventoryPolicy, inventoryManagement, quantityRule, requiresShipping and taxable. Inventory quantities are only emitted when the store exposes inventory_management and the value is not Shopify's 999999 untracked sentinel.

When the detail endpoint omits the available flag but publishes inventory management, availability is derived from stock: positive quantity, or inventory_policy=continue, means available. Without published inventory the flag stays omitted rather than being guessed.

Variant output

With flattenVariants: true, each variant becomes its own row with recordType: "variant", plus the parent productId, productHandle, productTitle, productUrl, vendor and productType. Product and variant IDs are de-duplicated.

Collection output

mode=collections emits recordType: "collection" rows with collectionId, handle, url, title, description, productsCount (the store's reported count), timestamps, image, store, sourceUrl and scrapedAt.

Run summary

The dataset stays clean: products, variants and collections only. A separate key-value record named OUTPUT holds the run summary with each store's status, HTTP code, pages fetched, record counts, detail failures and capped flag.

Per-store statuses:

StatusMeaning
OKThe feed was read normally.
CAPPEDThe page cap or Shopify's 25,000-offset ceiling was reached.
PARTIALSome pages or detail requests failed after data was collected.
PASSWORD_PROTECTEDThe store returned 401.
FROZENThe store returned 402.
BLOCKEDThe store returned 403, including after the automatic proxy retry.
NO_JSONThe store returned 404 or a non-JSON response.
RATE_LIMITEDThe store returned 429 after retries.

If every store fails, the run fails with the per-store map instead of finishing silently with an empty dataset. If some stores succeed, the run succeeds and the summary reports the failures.

Pagination notes

  • 250 products per page, the Shopify maximum.
  • Short pages are not the end of the feed. The walker continues until an empty page, an error, maxPages, or the 25,000-offset ceiling.
  • Products are de-duplicated by store and product ID; variants by store and variant ID.
  • maxItems is a global cap across all stores and jobs.

Currency

Shopify localizes prices by IP. The actor pins ?currency= on every list, collection and product request. The default store setting reads the store's base currency from /meta.json; an explicit ISO code overrides it. Each product records the currency reported by its variants when available.

FAQ

Do I need a Shopify account or API key? No. The actor only reads public storefront JSON.

Do I need a proxy? No. Direct requests are tried first. Apify's datacenter proxy is used only after a 403 or 429.

Why does search return at most 10 products per store? That is Shopify's public suggest endpoint limit. There is no pagination on it. Use catalog or collection mode for larger feeds.

Why is the product count lower than the collection page says? Collection product endpoints expose only purchasable/published products, while products_count counts more. The reported count is kept as productsCount and is not treated as ground truth.

Why are some inventory fields missing? Inventory is a merchant setting. Stores that do not publish inventory_quantity cannot be forced to reveal it, and the untracked 999999 sentinel is never emitted as stock.

What happens on password-protected stores? Their public JSON returns 401. The actor reports PASSWORD_PROTECTED and does not retry with a proxy.

Why did a large store stop at 25,000 products? Shopify rejects page offsets above 25,000 on /products.json. Split large catalogs by collection to go deeper.

Are descriptions HTML or plain text? Both are available: bodyHtml keeps the original markup and description is a plain-text version.

Is this affiliated with Shopify? No. It is an independent third-party tool that reads publicly available storefront data.

Limits

  • Password-protected stores are not scrapable through the public JSON.
  • Stores that disable the JSON endpoints return NO_JSON.
  • Collection membership is not exposed publicly; the actor emits collection records instead of inventing per-product membership.
  • Search is capped at 10 products per store.
  • Inventory counts are available only where the store publishes them.
  • The product feed is limited to the newest 25,000 products per store/feed.