Shopify Variant Availability & Price Evidence Monitor
Pricing
$5.00 / 1,000 successful product checks
Shopify Variant Availability & Price Evidence Monitor
Check if selected Shopify variants (size, color) are in stock or sold out, and save the raw price as evidence. Restock and competitor variant watch. $0.005 per product check; blocked, failed or unknown checks are free. No login, proxy or browser.
Pricing
$5.00 / 1,000 successful product checks
Rating
0.0
(0)
Developer
LatentLark
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
9 hours ago
Last modified
Categories
Features
Share
Check whether specific Shopify variants (a size, a color) are in stock or sold out on a public storefront, and save the price Shopify returned as timestamped evidence. Paste a product URL and a variant ID and click Start. You get one clear row per variant, and sold out is never confused with blocked, failed or unknown.
No login, no cookies, no proxy, no browser. It reads only Shopify's documented public product endpoint.
Built and maintained by LatentLark. Want the rows in a spreadsheet? Send every run to Google Sheets with Google Sheets Export & Import (No OAuth).
Quick start (first run in under a minute)
- Click Try for free (or Start in Console). The form is prefilled with a real product on Shopify's public Dawn theme demo store: the Thelma sandal, variant
39576998346841(Black / 35). - Click Start. The run takes a few seconds and gives you 1 row in the Output tab, for $0.005.
- Replace the URL and variant ID with the products you want to watch, up to 5 products and 20 variants per run.
- To be told what changed, click Save as task and add a Schedule (for example, daily). Each run of a task compares against the earlier runs of that same task. A one-off run reports
initial_observation.
Finding a variant ID: open the product page, pick the size or color, and copy the number after ?variant= in the address bar. You can also open https://STORE/products/HANDLE.js and look at variants[].id.
What you can do with it
- Restock watch: see when a sold-out size or color becomes buyable again (
availability_changedfromout_of_stocktoin_stock). - Competitor variant availability: track which sizes or colors a competitor has run out of, and when they come back.
- Ad and campaign QA: label each variant with the ad or campaign it belongs to (
label), so you know when an advertised variant is no longer buyable. - Price evidence snapshots: keep a dated record of the raw price integer Shopify returned for each variant, with the exact source URL and HTTP status.
Example: the prefilled demo
Input (this is the form prefill):
{"watchlist": [{"productUrl": "https://theme-dawn-demo.myshopify.com/products/thelma-sandal","variantId": "39576998346841","label": "dawn-demo-thelma-sandal-black-35"}]}
Real output row from a platform run on 9 November 2026 (some fields shortened):
{"label": "dawn-demo-thelma-sandal-black-35","productUrl": "https://theme-dawn-demo.myshopify.com/products/thelma-sandal","sourceUrl": "https://theme-dawn-demo.myshopify.com/products/thelma-sandal.js","productHandle": "thelma-sandal","productId": "6920720777305","variantId": "39576998346841","variantTitle": "Black / 35","availability": "out_of_stock","previousAvailability": null,"event": "initial_observation","priceMinor": 38500,"currency": null,"currencyStatus": "unknown","quantity": null,"httpStatus": 200,"variantCoverage": "complete","returnedVariantCount": 42,"baselineWrite": "appended","charged": true,"chargeEvent": "successful-product-check","chargeScope": "availability_only","requestsUsed": 2,"requestLimit": 12,"limitations": ["currency_unknown", "currency_endpoint_disallowed_by_robots"],"checkedAt": "2026-11-09T18:16:38.035Z"}
The Black / 35 variant really is sold out on that demo store, while other variants of the same product are in stock. Results on a live demo can change over time.
Pricing (pay per event)
| Event | Price |
|---|---|
| Successful product check | $0.005 per product ($5 per 1,000) |
| Actor start | none |
- One charge per product, not per variant. It covers every variant you selected on that product (up to 20 in total per run).
- Charged only when every selected variant on the product was classified:
in_stock,out_of_stock,variant_not_foundorremoved. - Free: blocked, rate-limited, failed (404, 5xx), unsupported (password page), truncated, timed-out or unknown results. If one selected variant of a product is unknown, that whole product is free.
- Availability check, not a price check. Most stores don't expose currency to this read-only method (see Limitations). The price integer is saved as evidence, but you pay for the availability classification.
- Cheap to run: our 1-product test run used about $0.0003 of Apify platform usage in roughly 4 seconds at 256 MB. Example: checking 5 products once a day for 30 days costs 150 × $0.005 = $0.75 in events.
Input
| Field | Required | What it does |
|---|---|---|
watchlist | yes | List of { productUrl, variantId, label?, locale? }. Duplicate product and variant pairs are merged. |
watchlist[].productUrl | yes | HTTPS Shopify product URL, for example https://store.com/products/handle. Any ?variant= in the URL is ignored. |
watchlist[].variantId | yes | Numeric Shopify variant ID, as a string. |
watchlist[].label | no | Your own name for the row (an ad, campaign or SKU). It is never sent to the store. |
watchlist[].locale | no | URL locale prefix such as en or en-ca. It does not simulate a shopper's country. |
previousSnapshot | no | Paste OUTPUT.snapshot from an earlier run to compare against it without using a task. |
stateScope | no | Snapshot scope name for local (non-Apify) runs. On Apify, use a saved task or previousSnapshot for history. A task's own ID always takes priority. |
maxProducts | no | Lower the product cap (1–5, default 5). |
maxVariants | no | Lower the variant cap (1–20, default 20). |
maxRequests | no | Lower the HTTP request cap (1–12, default 12). |
timeoutSeconds | no | Lower the time budget (5–60 seconds, default 60). |
Output
Dataset: one row per selected variant, including unchanged and failed checks. Main fields:
| Field | Meaning |
|---|---|
availability | in_stock, out_of_stock, variant_not_found, removed, truncated, blocked, rate_limited, unsupported, http_error or unknown |
event | initial_observation, unchanged, availability_changed, price_changed, currency_changed, variant_not_found, removal_confirmed, truncated or unknown |
previousAvailability, previousPriceMinor | Values from the last good check of the same task or scope |
priceMinor, compareAtPriceMinor | Price integers exactly as Shopify returned them, not divided or converted |
currency, currencyStatus, currencySource | The currency code only when a documented field supplied it. Otherwise null / unknown |
quantity | Always null. This Actor does not report unit counts. |
httpStatus, reason, sourceUrl | Where the evidence came from, and why a check failed |
variantCoverage, returnedVariantCount | Whether the product JSON listed all of the product's variants (Shopify lists at most 250) |
baselinePreserved, baselineWrite | Whether an error left the earlier good result untouched, and whether this run's snapshot was saved |
charged, chargeReason, chargeScope | Whether this product was billed, and why or why not |
label, labels, checkedAt, limitations | Your label, the check time, and any caveats |
Key-value store OUTPUT: a run summary with counts per availability and event, requests used, the caps applied, the charges made, and snapshot, which you can pass as previousSnapshot next time.
Limits
| Cap | Maximum |
|---|---|
| Unique products per run | 5 |
| Selected variants per run | 20 |
| HTTP requests per run (robots.txt, product, redirects, one retry) | 12 |
| Run time | 60 seconds |
| Memory | 256 MB |
If an input is over a cap, the run stops before sending any request and nothing is charged. Requests to each store are limited to 1 per second, with at most 1 retry. Proxies and notifications are not supported, and the run refuses them.
How it works
- It reads only
GET /{locale}/products/{handle}.js, Shopify's documented Ajax product endpoint, and respects each store'srobots.txt. out_of_stockis reported only when Shopify returnsavailable: falsefor that exact variant. A 403, a 404, a timeout or a password page is never recorded as sold out.- A variant missing from a complete product document is
variant_not_foundthe first time andremovedonly after two consecutive complete checks. - Errors never overwrite the earlier good result. Each run appends its own snapshot and never rewrites an older one.
- There are no cart actions, no Storefront or Admin API tokens, and no browser.
Limitations
- Currency is often unknown. The product JSON has no currency field. Shopify's documented place to read it (
/cart.js) is disallowed by many stores'robots.txt, including Shopify's theme demos, and this Actor respects that. In those casescurrencyisnullandprice_changedis not emitted, because a price can't be compared safely without a currency. - At most 250 variants per product. Shopify's product JSON stops at 250. A selected variant that isn't in a 250-variant response is reported as
truncated(unknown), not sold out. - Overlapping runs may duplicate events. If two runs of the same task overlap, both can report the same
availability_changed. Schedule runs far enough apart that one finishes before the next starts (a run usually takes seconds). - History needs a task or
previousSnapshot. One-off runs only reportinitial_observation. After more than 50 stored snapshots in one task, that task's history is no longer trusted; passpreviousSnapshotfrom a recent run instead. - No stock counts and no country simulation.
quantityis alwaysnull, andlocaleis only a URL prefix. - Only public storefronts are supported. Password-protected stores and non-Shopify sites return
unsupportedorblocked, free of charge. Only monitor stores you are allowed to check.
FAQ
I got blocked or http_error. Was I charged?
No. Only fully classified products are charged. Check that the URL is a public /products/... page and that the store's robots.txt allows /products/.
Why is currency null?
The store doesn't expose currency to this read-only method, or its robots.txt disallows /cart.js. The raw priceMinor is still saved.
Why don't I see availability_changed?
The first run of a task, or a one-off run, is always initial_observation. Save the input as a task and schedule it, or pass previousSnapshot.
variant_not_found but the product exists?
Check the variant ID (it's the number after ?variant=). A variant is only marked removed after two consecutive complete checks without it.
Can I watch 100 products? Not in one run. The limit is 5 products and 20 variants per run, which keeps every run fast and cheap. Use several tasks for more.
Does it tell me how many units are left?
No. available: false means the storefront isn't selling that variant right now. It isn't a unit count.
More LatentLark Actors
- Google Ads Transparency & LinkedIn Ad Library Scraper: see a competitor's paid ads next to the variants they are selling.
- Creator Commerce Competitor Finder — LTK & ShopMy: find which creators promote a brand's products.
- Google Trends Compare: Unlimited Keywords, One Scale: check demand for a product category over time.
- Google Sheets Export & Import (No OAuth): send every run's rows to a Google Sheet.