Shopify Intelligence Tracker
Pricing
from $1.20 / 1,000 product scrapeds
Shopify Intelligence Tracker
Pull structured product data from any Shopify storefront - no API key needed. One row per variant with prices, SKUs, stock status, images, tags and weights, from the public product feed. Built for competitor research, price monitoring and catalog audits. Billed per product, not per variant.
Pricing
from $1.20 / 1,000 product scrapeds
Rating
0.0
(0)
Developer
Molly Vir
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
14 days ago
Last modified
Categories
Share
Pull clean, structured product data out of any Shopify storefront — no API key, no scraping code.
Give it a storefront URL. It returns one row per product variant with prices, stock status, images, SKUs, tags, weights and options, taken straight from the store's public product feed.
Built for price monitoring, competitor research, catalog audits and catalog-import workflows.
Example input
{"urls": ["https://www.everlane.com"],"max_products": 25}
Example output
One row per variant. Real record from a live Everlane run:
{"storeDomain": "https://www.everlane.com","productName": "Pull-On Maxi Skirt in Cotton Silk | White","variantName": "XXS","option1": "XXS","price": "138.00","sku": "F-BTM-VLE-MAXI-SKIRT-WHT-XXS","inStock": true,"productUrl": "https://www.everlane.com/products/womens-pull-on-maxi-skirt-in-cotton-silk-white","productId": 7859023741014,"variantId": 44641683898454,"handle": "womens-pull-on-maxi-skirt-in-cotton-silk-white","vendor": "Everlane","productType": "Bottoms","tags": ["category: bottoms", "fabric: silk", "Style SKU: F-BTM-VLE-MAXI-SKIRT"],"imageUrl": "https://cdn.shopify.com/s/files/1/0623/7916/3734/files/2026-09-27_F-BTM-VLE-MAXI-SKIRT-WHT_flat.jpg","weight": 1901,"weightUnit": "g","requiresShipping": true,"taxable": true}
Pricing
Pay per event. You are charged for what you get, and the price does not depend on how long the run takes or how much bandwidth it uses.
| Event | Price | When it is charged |
|---|---|---|
| Product scraped | $0.0012 | Once per product found, including all of its variants |
| Storefront | $0.002 | Once per storefront URL requested |
| Actor Start | $0.00005 | Once per run, when the run begins |
Example: one storefront, 100 products, any number of variants per product:
Actor Start 1 × $0.00005 = $0.00005Storefront 1 × $0.002 = $0.00200Product scraped 100 × $0.0012 = $0.12000--------Total $0.12205
A product with twelve variants still costs $0.0012, so you never pay per row. Duplicate products and products with no variant data are not charged. A storefront that fails is still charged, because it still consumed network resources — but a URL that is rejected as invalid before any request is made is not charged.
There is no minimum charge and no separate platform-usage fee. If a run finds nothing, you pay nothing.
Features
- No API key. Uses the public product feed every Shopify store already exposes.
- Every variant, not just products. One row per variant with its own price, SKU and stock status.
- Residential proxy by default. Datacenter IPs are blocked by most Shopify stores, so the default configuration routes through residential proxies that storefronts actually accept.
- Works with many storefronts per run. Pass as many URLs as you like in a single run.
- Automatic pagination. Follows the feed page by page until your product cap or the end of the catalog.
- Deduplicated. Products appearing on multiple pages and repeated records are removed.
- Retry-aware. Rate limits and transient server errors are retried with exponential backoff.
- Honest failures. If every storefront fails, the run exits non-zero instead of reporting an empty success, so scheduled runs and monitors can tell the difference.
Input
| Field | Type | Default | Description |
|---|---|---|---|
urls | array of strings | — | Shopify storefront URLs to scan, for example https://example.com. Maximum 50 per run; split larger batches across runs. |
max_products | integer | 25 | Maximum products per storefront. Minimum 1, maximum 100000. |
proxyConfiguration | object | {"useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"]} | Proxy routing. See Proxies; this default matters. |
Output
One item per product variant:
| Field | Type | Description |
|---|---|---|
storeDomain | string | Base URL of the storefront. |
productName | string | null | Product name. |
variantName | string | null | Variant name. |
option1 / option2 / option3 | string | null | Variant options, such as size or color. |
price | string | null | Current variant price as reported by the storefront. |
compareAtPrice | string | null | Comparison price when supplied by Shopify. |
sku | string | null | Merchant SKU. |
inStock | boolean | null | Whether Shopify reports the variant as currently available. Reflects public availability only, not an inventory quantity. |
productUrl | string | null | Direct link to the product page. |
productId | integer | null | Shopify product identifier. |
variantId | integer | null | Shopify variant identifier. |
handle | string | null | Product URL handle. |
vendor | string | null | Vendor or brand. |
productType | string | null | Product category or type. |
tags | array<string> | null | Product tags, normalized into a list. |
createdAt / updatedAt / publishedAt | string | null | Product timestamps. |
imageUrl | string | null | Variant image from featured_image, falling back to the product's first image. |
barcode | string | null | Variant barcode or UPC. Shopify's public products.json feed does not include barcodes, so this is normally null. |
weight | number | null | Variant weight in grams, as reported by Shopify. |
weightUnit | string | null | Always "g" when a weight is present, otherwise null. |
requiresShipping | boolean | null | Whether the variant requires shipping. |
taxable | boolean | null | Whether the variant is taxable. |
Prices are reported exactly as Shopify returns them and are not labeled with any currency.
Proxies
This Actor does not work without a proxy. Apify Actors send requests from datacenter IP ranges, and most Shopify storefronts block those outright with HTTP 429. A run without a proxy returns zero items, and fails with a non-zero exit code rather than reporting a misleading success.
The default proxyConfiguration routes requests through Apify's residential proxy group, which storefronts accept. Verified against live stores.
The default Apify proxy group when you only set useApifyProxy: true is datacenter, which Shopify blocks just as hard as a direct request. RESIDENTIAL is specified explicitly for that reason.
This default is applied by the Actor itself, not only by the run form. Runs started through the API, the CLI, a schedule or the Apify MCP server do not get input-schema defaults applied, so without a code-level default those callers would silently fall back to DATACENTER. Passing proxyConfiguration explicitly always overrides it.
| Setting | Behaviour | Typical cost |
|---|---|---|
apifyProxyGroups: ["RESIDENTIAL"] (default) | Works against most stores | ~$8 / GB, absorbed by the Actor price |
apifyProxyGroups: ["DATACENTER"] | Cheaper, blocked by most stores | from $0.6 / IP |
useApifyProxy: false | Direct request, no proxy | Free, but returns nothing |
Each storefront is fetched through a single proxy session, so all pages and retries for one store come from the same IP. Rotating mid-crawl makes the request pattern look erratic and defeats backoff.
The one exception is a broken proxy session. When the proxy itself returns an upstream error, retrying on the same session cannot succeed, so the Actor transparently retries that storefront on a fresh session. A store answering 429 or 404 keeps its original IP and is never rotated, since that would defeat backoff. Because every network call happens before any data is pushed or charged, a rotated retry cannot bill the same storefront twice.
How it works
- Uses the storefront's public
products.jsonfeed. - Routes each storefront through its own proxy session (see Proxies).
- Paginates with page-based requests (up to 250 products per page) until
max_productsis reached or the feed is exhausted. - Deduplicates products that appear on multiple pages.
- Retries rate-limited (HTTP 429) and server errors (HTTP 5xx) with exponential backoff, three attempts per request.
- Output records are deduplicated by
(productId, variantId, productUrl). - Exits non-zero if every storefront failed, so scheduled runs and monitoring can distinguish failure from an empty result.
Limitations
Worth knowing before you rely on this:
-
50 storefronts per run. Larger batches are truncated to the first 50 and the log says how many were dropped. Each URL consumes proxy bandwidth, so the limit keeps a single run's cost bounded.
-
Only Shopify stores. The Actor reads the public
products.jsonfeed that Shopify generates. Other platforms have no equivalent, and will fail with a clear error. -
Public data only. This reads what the storefront publishes to its public feed. Discount codes, customer-specific pricing, inventory counts and sold-out items hidden from the feed are not available. Prices reflect the store's public list price, not what a customer would actually pay at checkout.
-
Some stores rate-limit aggressively. A small number of high-traffic stores return HTTP 429 even through residential proxies. Retry the run, or lower
max_products. -
Some stores block automated clients outright. The Actor identifies itself honestly as
ShopifyIntelligenceTracker/1.0and links to its Apify page, rather than claiming to be a browser. When a store refuses anyway, the Actor retries from a second and third residential IP before giving up, so an IP-level block usually clears. A store that blocks bot traffic on every IP will still fail. -
No barcodes. Shopify does not publish barcodes in
products.json, sobarcodeis normallynull. -
No currency. Prices are raw strings from the store, without a currency code.
-
inStockis a flag, not a count. It reflects public availability, not inventory quantity.
Troubleshooting
Every storefront failed, run exited non-zero.
Most likely the proxy is not working. Check that proxyConfiguration still includes apifyProxyGroups: ["RESIDENTIAL"]. The Actor logs the exact failure per storefront, including HTTP status or network error type.
One storefront returned nothing. Either the URL is not a Shopify store, or that store is blocking the request. The log names the storefront and the reason, including every IP that was tried.
A storefront failed with HTTP 403. The Actor retries from up to three residential IPs before failing that storefront, so an IP-level block usually clears on its own. If it still fails, the store is blocking automated traffic regardless of source IP. Raising an issue is worth it if the store is widely used.
A product is missing a field such as imageUrl or sku.
That store does not publish the field. Coverage is very high across stores, but it is never 100% because merchants configure their own data.
barcode is always null.
Expected. See Limitations.
Development
python3 -m venv .venvsource .venv/bin/activatepip install -r requirements.txtpip install -r requirements-dev.txtpytest
Run locally:
apify run
Input security
Storefront URLs are validated before fetching. Private IPs, loopback, link-local, reserved IPs, and localhost targets are rejected to avoid SSRF.
Deploy
apify push