Shopify Variant Availability Trends
Pricing
from $2.00 / 1,000 variant transitions
Shopify Variant Availability Trends
Compare two public Shopify variant snapshots and flag sold-out and back-in-stock transitions without guessing inventory quantities.
Pricing
from $2.00 / 1,000 variant transitions
Rating
0.0
(0)
Developer
Practical AI Lab
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
8 days ago
Last modified
Categories
Security
Limited permissionsShare
Compare two public Shopify variant snapshots and list which variants sold out or came back in stock between them. The Actor works only from Shopify's public true/false available flag. It does not estimate inventory quantities, sales volume or demand.
What it does
- Matches variants across an earlier ("before") and later ("after") snapshot by variant id.
- Labels each variant:
sold_out,back_in_stock,new_sold_out,new_available,removed,availability_unknown,still_sold_out,still_available. - Flags whether a whole product has no available variant (
productAllSoldOutBefore/productAllSoldOutAfter), plus price changes seen on the same variant. - Saves a compact
SNAPSHOTin the key-value store. Paste it asbeforeSnapshotin your next run to build a recurring comparison without storing raw feeds yourself. - Saves a
SUMMARYwith counts, snapshot interval and warnings.
Input
| Field | Meaning |
|---|---|
beforeSnapshot (required) | Shopify products.json object, or a previous run's SNAPSHOT. |
afterSnapshot | Later snapshot in the same format. |
afterStoreUrl | Alternative: a public https store domain. The Actor reads /products.json after checking robots.txt. Provide exactly one of afterSnapshot or afterStoreUrl; clear the pre-filled afterSnapshot when using a URL. |
maxPages | Live feed pages to read (250 products each, 1-20, default 5). |
beforeCapturedAt, afterCapturedAt | Optional ISO 8601 times used to report the interval. |
includeUnchanged | Also output variants whose availability did not change (default false). |
maxRecords | Upper bound on dataset rows (default 1000). |
The default input is a small copyable example and runs without any network access.
Example input
{"beforeSnapshot": {"products": [{"id": 1002, "handle": "ceramic-mug", "title": "Ceramic Mug", "variants": [{"id": 2003, "title": "White", "sku": "MUG-WHT", "available": false, "price": "14.00"}]}]},"afterSnapshot": {"products": [{"id": 1002, "handle": "ceramic-mug", "title": "Ceramic Mug", "variants": [{"id": 2003, "title": "White", "sku": "MUG-WHT", "available": true, "price": "15.00"}]}]},"beforeCapturedAt": "2026-10-01T09:00:00Z","afterCapturedAt": "2026-10-08T09:00:00Z"}
Example output row (illustrative, derived by hand from the input above)
{"changeType": "back_in_stock","variantId": "2003","productHandle": "ceramic-mug","productPath": "/products/ceramic-mug","variantTitle": "White","availabilityBefore": false,"availabilityAfter": true,"priceBefore": "14","priceAfter": "15","priceChanged": true,"productAllSoldOutBefore": true,"productAllSoldOutAfter": false}
Prices are normalized decimal strings ("14.00" becomes "14").
Safety and live-feed rules
- Live reads use https only, a public domain name (no IP addresses, credentials, custom ports or local/internal suffixes), and refuse hosts that resolve to non-public addresses.
robots.txtis checked first; a disallow, or a robots server error, stops the run.- Redirects are not followed, pages are capped at 20, responses are size-limited, and requests are spaced one second apart.
- Only the feed fields needed for the comparison are kept in memory.
- Nothing is read from Google Maps, Amazon or any non-Shopify site.
Pricing event
The code reports one pay-per-event event, variant-transition, for each delivered sold_out or back_in_stock row. Context rows (new, removed, unchanged, unknown) are never charged, and runs that fail validation or fetching push nothing and charge nothing. No payable plan is configured in this repository; pricing must be set separately by the publisher.
Limitations and sample-size caveats
- Availability only.
availableis a boolean. Quantities, sales and restock volume are not exposed and are not guessed. - Two points in time. A variant that sold out and returned between snapshots looks unchanged. Longer intervals hide more.
- Small samples mislead. A handful of matched variants says little about a store; the summary warns below 30 matched variants. Counts are observations, not forecasts.
- Feed quirks. Some stores hide, customize or rate-limit
/products.json; draft or unpublished products never appear. Products disappearing from a feed are reported asremoved, which may mean unpublished rather than discontinued. - Incomplete live reads. If
maxPagesis reached the snapshot can be partial and may produce falseremovedrows; a warning is added. - Snapshot consistency. Both snapshots should come from the same store and market. Variant ids must be unique and are the only matching key.
- DNS timing. Public-address checks happen before connecting, but a hostile DNS server could still change answers between check and connection.
- Demand not proven. Similar Shopify scrapers have free-tier users, but paid demand for this specific comparison has not been validated.
Local development
Tests are offline: python -m unittest discover -s tests from the project root. Running the Actor requires the Apify SDK 3.x from requirements.txt.