Shopify Collection Revenue Exposure Audit avatar

Shopify Collection Revenue Exposure Audit

Pricing

from $20.00 / 1,000 audit completeds

Go to Apify Store
Shopify Collection Revenue Exposure Audit

Shopify Collection Revenue Exposure Audit

Compare two dated Shopify collection snapshots with your sales weights to quantify delistings and sold-out flags.

Pricing

from $20.00 / 1,000 audit completeds

Rating

0.0

(0)

Developer

Practical AI Lab

Practical AI Lab

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

15 hours ago

Last modified

Security

Limited permissions

Share

Compare two dated snapshots of a Shopify collection and your own product sales weights to see how much of your historical sales weight sits on products that were delisted or are now published as sold out. The Actor also tells you which affected products have no weight and why on-hand stock stays unknown.

What it does

  1. Reads a baseline and a current product list. Each side is either inline JSON or a public products.json collection feed.
  2. Matches products by handle and classifies each baseline product as delisted, newly_sold_out, still_sold_out or restocked.
  3. Adds up your supplied weights (units sold, revenue, or any non-negative measure) for the affected products.
  4. Reports the new exposure and its share of the weighted baseline total, plus warnings about missing weights, truncated feeds and small samples.

This is not a general product scraper. It does not export catalog data; it produces an audit of change between two dates.

Sample input

{
"baselineDate": "2026-10-01",
"currentDate": "2026-11-01",
"baselineProducts": [
{"handle": "trail-bottle", "title": "Trail Bottle", "variants": [{"available": true}]},
{"handle": "canvas-tote", "title": "Canvas Tote", "variants": [{"available": true}]},
{"handle": "desk-lamp", "title": "Desk Lamp", "variants": [{"available": true}]},
{"handle": "wool-socks", "title": "Wool Socks", "variants": [{"available": true}]}
],
"currentProducts": [
{"handle": "trail-bottle", "title": "Trail Bottle", "variants": [{"available": true}]},
{"handle": "canvas-tote", "title": "Canvas Tote", "variants": [{"available": false}]},
{"handle": "wool-socks", "title": "Wool Socks", "variants": [{"available": true}]}
],
"salesWeights": [
{"handle": "trail-bottle", "weight": 400},
{"handle": "canvas-tote", "weight": 250},
{"handle": "desk-lamp", "weight": 120}
],
"weightUnit": "units_90d"
}

Here canvas-tote is newly sold out (250), desk-lamp is delisted (120) and wool-socks is unaffected but has no weight. New exposure is 370 of a weighted baseline total of 770.

Using live feeds

Instead of inline products, set baselineFeedUrl and/or currentFeedUrl to a public https://<shop>/products.json or https://<shop>/collections/<handle>/products.json URL and set confirmFeedPermission to true. A live URL only shows the shop as it is now, so the baseline normally comes from an earlier saved snapshot: every run stores a compact CURRENT_SNAPSHOT in its key-value store that you can paste into baselineProducts next time.

Output

The dataset holds one summary item followed by one finding item per affected product, sorted by weight.

Key summary fields: newExposureWeight, newExposureShareOfBaselineWeight, delistedCount, newlySoldOutCount, standingSoldOutWeight, missingWeightAffectedHandles, exposureIsLowerBound, hiddenStockUnknownCount, unmatchedWeightHandles, warnings and limitations.

Key finding fields: handle, status, weight, weightMissing, confidence, stockNote.

Safety and fair use

  • Only https URLs of the form /products.json or /collections/<handle>/products.json are accepted. Credentials, custom ports, IP addresses, query strings and hosts resolving to non-public addresses are rejected, and redirects are not followed.
  • robots.txt is checked before the feed is requested; a disallow or an unreachable robots file stops the run.
  • Requests are bounded by maxPages (up to 10 pages of 250 products), a delay between pages, a timeout and a response size cap.
  • No logins, proxies or personal data are involved.

Pricing event

The code emits one audit-completed event after a run has stored its results. Failed or invalid runs emit nothing. Pricing is configured separately by the account owner.

Limitations and sample-size caveats

  • Public feeds show an availability flag per variant, not inventory counts. "Sold out" can still mean backorders are allowed, and "available" can mean one unit. Hidden stock is always reported as unknown.
  • A missing product could be unpublished, deleted, removed from the collection, or beyond the page cap. Truncated feeds lower the confidence of delisting findings.
  • Exposure is a share of the historical weights you provide, not predicted lost revenue. If affected products lack weights, the figure is a lower bound.
  • With fewer than about 30 products or 20 weighted products, one item can dominate the share; the Actor warns in these cases.
  • The robots.txt check uses Python's standard parser, which matches path prefixes and does not interpret wildcard patterns. DNS is checked before the request, but the connection resolves the host again, so a hostile DNS change between the two is not fully excluded.
  • Product handles must be unchanged between snapshots; renamed handles appear as a delisting plus a new product.

Local development

Tests run offline with the standard library: python -m unittest discover -s tests.