Shopify Stores API Velocity, Pricing & Brand Model
Pricing
from $21.00 / 1,000 stores
Shopify Stores API Velocity, Pricing & Brand Model
Analyse any Shopify store from its public catalog: launch velocity in products per day, price positioning, discount strategy and brand model.
Pricing
from $21.00 / 1,000 stores
Rating
0.0
(0)
Developer
Spool
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
6 days ago
Last modified
Categories
Share
Shopify Stores API — Velocity, Pricing & Brand Model
Give it a Shopify store. Find out how fast it ships, what it charges, how hard it discounts, and whether it's an own brand or a reseller.
kith.com 5.4 new/day 5.6 published/day $1,611 active luxurygymshark.com 0.0 new/day 15.8 published/day $18 republishing budgetfinisterre.com 1.9 new/day 3.3 published/day $137 steady mid-marketallbirds.com 0.0 new/day 0.1 published/day $115 dormant mid-market
Gymshark and Allbirds both created zero new products in the last 90 days. One is re-merchandising its catalog sixteen times a day; the other has genuinely gone quiet. Every tool that reads one date tells you those two stores are the same.
Every Shopify store publishes its whole catalog at /products.json — no key, no
login, permitted by robots.txt. Each product carries the date it went live,
which means a store's growth rate is readable from a single request.
Two dates, two different businesses
Every Shopify product carries created_at and published_at, and almost
everything built on this endpoint picks one and hopes.
published_at is rewritten in bulk whenever a store replatforms or rebuilds its
theme. Allbirds spans 7.6 years by created_at and 83 days by
published_at — a decade of catalog stamped with one afternoon in June.
Deriving a launch rate from it is wrong in both directions at once:
from published_at measured properly errorallbirds.com 3.54/day 0.00/day infinitegymshark.com 12.39/day 0.00/day infinitefinisterre.com 0.28/day 1.86/day 6.6x too lowrothys.com 0.22/day 0.81/day 3.7x too lowdeathwishcoffee.com 0.03/day 0.14/day 4.7x too low
Wrong on every store tested — overstating dead ones by an unbounded factor and understating live ones several-fold.
So both rates are reported. perDay counts products genuinely created in
the last 90 days; publishPerDay counts what the store published in the
same window. The gap between them is the finding:
| new/day | published/day | reading | |
|---|---|---|---|
| gymshark.com | 0.0 | 15.8 | republishing — re-merchandising, very much alive |
| allbirds.com | 0.0 | 0.1 | dormant — actually winding down |
| kith.com | 26.4 | 27.8 | constant — genuinely shipping new product |
It tells you when it doesn't know
A 90-day window only works if the read reached back 90 days, and on a fast store it doesn't. Kith reads 2.8/day at 250 products, 10.9 at 1,000 and 26.4 at 2,500 — still climbing.
Most tools present that as fact. This one flags it. velocityConfidence comes
back low, and because truncation can only ever hide recent products and never
invent them, perDay is then a lower bound rather than a wrong number.
It is not a blanket disclaimer — it fires only when it should:
organicbasics.com 250 products → 1.76/day high full catalog → 1.77/dayfinisterre.com 250 products → 1.86/day high full catalog → 1.86/dayrothys.com 250 products → 0.81/day high full catalog → 0.81/daykith.com 2,500 products → 26.4/day low still rising
Organic Basics called correctly to within 0.6% from a default read, while Kith —
the one case that genuinely can't be resolved cheaply — says so. publishPerDay
gets the same treatment, with an exact test rather than an estimated one.
Detecting a replatform, and why the obvious test fails
catalogRepublished is worth filtering on by itself: a store that just migrated
is a store currently buying apps, themes and agency time.
Getting it right took three attempts. Comparing the created span against the
published span returned true for every store tested at 250 products —
including Rothy's, Organic Basics and Finisterre, whose complete reads prove
they were never republished. The busiest-publish-day share failed the same way.
Both collapse for one reason: the catalog is paged newest-published-first, so
any truncated read looks compressed in publish time no matter what the store
did.
What survives is the lag between a product being created and being published, because that is a property of each product rather than of the read window:
lag @250 @500 @2500 verdictfinisterre.com 36d 37d 39d normalrothys.com 55d 50d 41d normalkith.com 1.8d 1.8d 1.9d normalallbirds.com 596d 559d — republishedgymshark.com 697d 724d 136d republished
Stores publishing normally measure 2–106 days; stores that rewrote their catalog measure 136–724. The verdict is now identical at every read depth for all eight test stores — it was wrong for five of them before.
Which apps the store pays for
The catalog tells you what a store sells. The storefront tells you how it operates — and that is the part an app developer or agency buys.
organicbasics.com 9 apps Klaviyo · Judge.me · Yotpo · Narvar · ShopMy · Klarnadeathwishcoffee.com 8 apps Klaviyo · Postscript · Rebuy · Smile.io · Afterpayfinisterre.com 7 apps Okendo · Gorgias · Klevu · AB Tasty · Elevar · Klarnarothys.com 2 apps Intelligems · Yotpo
Over 50 apps are recognised across email/SMS, reviews, support, subscriptions,
CRO, loyalty, search, analytics, post-purchase, payments and affiliate — each
labelled with its category. Anything third-party that cannot be named is still
reported in otherThirdPartyHosts, so a store running something unusual shows
up rather than looking simpler than it is.
Set requireApps and it becomes a lead list: every store running Klaviyo but
not Recharge is one run.
The theme comes with it, and themeIsStock flags the free Shopify themes.
A store still on stock Dawn has not invested in design — which separates a real
operation from a side project more reliably than catalog size does.
Prices you can actually compare
products.json carries prices as bare numbers with no currency anywhere in
it. A UK store showing 115 and a US store showing 115 are £115 and $115,
and a yen-priced store looks like luxury goods at about a hundred dollars.
So the currency is read from the store profile and every price is reported twice — in the store's own currency and in USD:
finisterre.com GBP median £102 → $137.01 mid-marketorganicbasics.com EUR median €41 → $46.60 mid-marketallbirds.com USD median $115 → $115 mid-market
Price positioning is judged on the USD figure, never the raw number — reading
Finisterre's 102 as dollars drops it a whole bracket, and a yen-priced store
would read as budget goods. When a rate is unavailable the USD fields are left null rather
than filled with a guess.
Quick start
{ "storeUrls": ["allbirds.com", "kith.com", "rothys.com"] }
Domains, full URLs and bare handles all work. www. is stripped, and
www.x.com and x.com are merged so you're never billed twice for one store.
What you get back
{"store": "finisterre.com","storeName": "Finisterre","country": "GB","city": "St Agnes","currency": "GBP","shipsToCountryCount": 39,"productsSampled": 500,"catalogComplete": false,"perDay": 1.86,"velocityConfidence": "high","publishPerDay": 3.33,"publishConfidence": "high","velocitySample": 167,"velocityWindowDays": 90,"newestAt": "2026-09-14T15:41:10.000Z","catalogRepublished": false,"medianPublishLagDays": 37.05,"republishShare": 0.08,"priceMin": 6, "priceMedian": 102.5, "priceMax": 295,"priceMedianUsd": 137.01,"onSaleShare": 0.12, "averageMarkdown": 0.35,"outOfStockShare": 0.1,"vendorCount": 1,"topVendors": [{ "name": "Finisterre", "count": 500 }],"productTypes": [{ "name": "Men's Jackets", "count": 41 }],"theme": "LinedUp / BFVA","themeIsStock": false,"appCount": 7,"apps": ["AB Tasty", "Back in Stock (Amp)", "Elevar", "Klarna", "Klevu", "Okendo"],"appsByCategory": { "reviews": ["Okendo"], "support": ["Gorgias"], "cro": ["AB Tasty"] },"socialChannels": ["instagram", "facebook", "youtube"],"signals": {"launchCadence": "steady","pricePositioning": "premium","discountStrategy": "occasional","brandModel": "own-brand","catalogScale": "mid","inventoryPressure": "normal"}}
signals carries the reading; every threshold is published in
signals.thresholds, so you can disagree with an interpretation without losing
the numbers behind it.
Six dataset views ship with it: Overview, Apps & tech, Growth, Pricing, Catalog and Unavailable.
Recipes — copy, paste, run
Which of my competitors is actually shipping?
{ "storeUrls": ["rival-one.com", "rival-two.com", "rival-three.com"] }
Find the resellers in a list — who stocks other people's brands
{"storeUrls": ["store-a.com", "store-b.com"],"requireSignals": ["brandModel:multi-brand-reseller"]}
Stores in trouble — heavy discounting and empty shelves
{"storeUrls": ["..."],"requireSignals": ["discountStrategy:permanent-sale", "inventoryPressure:mostly-unavailable"]}
Stores that just replatformed — buying apps, themes and agency time right now
{"storeUrls": ["..."],"requireSignals": ["launchCadence:republishing"]}
Exact catalog and full price range on a big store
{ "storeUrls": ["gymshark.com"], "maxProductsPerStore": 5000 }
Every option
| Option | Default | What it does |
|---|---|---|
storeUrls | — | Required. Domains, URLs or handles, mixed freely |
maxProductsPerStore | 500 | How deep to read. The default settles velocity on most stores; when it cannot, velocityConfidence says so. Raise it for an exact catalog count and lifetimePerDay |
requireApps | (none) | Keep only stores running Klaviyo, Recharge, Gorgias… The lead-list filter |
requireAppsMode | any | all finds a specific stack, like Klaviyo and Recharge |
detectApps | true | Read the storefront for apps, theme and socials. One extra request per store |
requireSignals | (none) | Keep only stores matching launchCadence:constant, brandModel:multi-brand-reseller, pricePositioning:luxury… |
minProducts | 0 | Skip stores smaller than this — filters out test and abandoned shops |
includeProducts | false | Add every product with title, URL, vendor, tags, date and price |
maxConcurrency | 5 | Stores analysed in parallel |
What it covers, honestly
About 5 stores in 6 work. Tested against 18 real stores: 15 served the
catalog, 3 did not. Those three return not_a_shopify_store — either they've
disabled the endpoint or they're not on Shopify. A miss is always reported, never
silent.
Stripping www. matters more than it sounds: three of those stores failed with
the www. prefix and succeeded without it, so it's normalised for you.
catalogSize is exact only when catalogComplete is true. On a catalog
larger than your read limit, the count is null rather than a number that would
be wrong. productsSampled always tells you what was actually read.
Price and discount figures come from the sample, which is the newest end of the catalog. For a complete catalog they're exact; otherwise they describe current stock rather than everything ever sold.
Currency and shipping reach are what the store serves you. Shopify switches storefront by geography, so an Italian brand answered in USD when tested from outside the EU, and reported shipping to 2 countries rather than its full list. The figures are reported exactly as the store gave them rather than normalised into something that looks tidier but is invented.
Availability depends on the store. Some publish it, some don't —
stockDataAvailable says which, rather than reporting a misleading zero.
App detection reads what the storefront loads. A large retailer that self-hosts its scripts shows fewer apps than it runs — Gymshark detects only 1 — so a low count on a big brand means "not visible", not "not installed".
No proxy, no key, no blocking. Two or three plain HTTPS requests per store.
When a store can't be read
error | Meaning |
|---|---|
not_a_shopify_store | No public catalog at /products.json — disabled, or not Shopify |
catalog_blocked | The store returned 403 or 429 |
catalog_empty | Catalog exists but has no products |
invalid_input | Couldn't read a domain from that value |
A breakdown is saved to the key-value store as RUN_SUMMARY.
FAQ
How do I find a Shopify store's product catalog?
Pass the domain. The Actor reads the public /products.json endpoint that every
Shopify store serves, and returns the catalog plus derived metrics.
How is this different from a product scraper? Product scrapers hand you rows. This measures the store: how fast it ships, where it prices, how it discounts, whether it resells. You can get to a shortlist without reading a single product.
Can it tell me how much a store earns? No, and nothing honestly can from public data. Tools quoting revenue are estimating. This reports what the store itself publishes — catalog, prices, dates, availability — and derives only what those support.
Does it work on any store? Roughly five in six. Some owners disable the public catalog, and those are reported rather than guessed at.
Is this legal?
/products.json is a public endpoint Shopify serves by design, it requires no
authentication, and robots.txt permits it. Everything returned is catalog data —
products, prices, dates. No customer data, no personal information.
How to use it
- Open the Actor and paste your store domains into Shopify stores — one per line,
www.or not. - Optionally set requireApps (e.g.
Klaviyo) or requireSignals (e.g.brandModel:multi-brand-reseller) to turn the scan into a shortlist. - Click Start. Ten stores take about ten seconds.
- Open the Growth, Pricing or Apps & tech dataset view, or export the whole thing as CSV, Excel or JSON.
- To repeat it weekly, use Schedule; to pull it into your own system, call the run through the API or an integration (Google Sheets, Make, Zapier, webhooks).
How much does it cost?
Pay per event — you are charged per store analysed, and nothing for stores that could not be read.
| Plan | per store |
|---|---|
| Free | $0.03 |
| Bronze | $0.027 |
| Silver | $0.024 |
| Gold | $0.021 |
A store that was read but filtered out by your requireSignals / minProducts settings costs $0.002. Actor start is $0.002.
Worked examples
- Analyse 10 competitors → 10 × $0.03 = $0.30
- Scan 100 Shopify stores for ones running Klaviyo → about $3.00 (Gold plan: $2.10)
- The free plan's $5 credit covers roughly 160 stores
No proxy is used, so there is no proxy bill on top.
More Shopify tools from spool
- Shopify Product Scraper — GTIN & Collections — every product and variant with GTIN barcodes, subscription plans, wholesale breaks and best-seller membership
- Shopify Scraper — Stores & Products — both of the above in one run — the store summary plus the full catalog
All three read the same public endpoints, respect robots.txt, and report a miss rather than guessing.
Support
Open an issue on the Issues tab and you'll get a reply, usually the same day. Requests for extra metrics are welcome — tell me what you need measured.