Get Back-in-Stock Alerts for Any Product
Set a back in stock alert on any product page in 10 minutes: watch mode, phone push, a cron recipe that filters for stock, and the honest limits.

On this page
The PSU you want sells out at 9:04 and comes back at 9:11. You find out at lunch. A back in stock alert fixes that: software watches the product page and pings your phone the moment the page text changes. One catch before anything else — the tool I use, Magpie, watches for any change on the page. It has no “back in stock” condition, no price threshold. This guide sets up the watch, gets the alert to your phone, and then adds the stock filter yourself with one line of grep.
What you need
- Magpie, the free CLI. One binary, no runtime dependencies:
go install github.com/motherlodelab/magpie@latestor grab a release. The Desktop app is optional; this guide stays in the terminal. - The product page URL. A public one: the fetcher refuses private addresses, so
localhostand LAN IPs are rejected. - A machine that stays on for 24/7 watching. A NAS, a VPS, a Raspberry Pi, or a laptop that never sleeps all work.
- For phone alerts: the ntfy app (free, no account) or a Discord/Slack webhook you already own.
Ten minutes, start to finish.
Step 1: Start watching the product page
The command is watch. It takes a URL and an interval:
magpie watch "https://books.toscrape.com/catalogue/the-grand-design_405/index.html" --every 5m
The first pass stores a markdown snapshot of the page and prints nothing. Every later pass re-fetches, cleans the page, and compares it word-by-word against the snapshot. No change means total silence. On change, you get a word-diff on stdout and the snapshot updates.
I ran exactly this against that demo bookstore (a legal scraping sandbox). The cleaned page carries the availability line in its main content, In stock (5 available), so a restock shows up like this:
- (5
+ (12
That’s abridged, and the snapshot was hand-edited to simulate the restock (the live store didn’t actually restock on cue). The diff format is real output though: word pairs, minus for the old snapshot, plus for the live page. When a store flips Out of stock to In stock, those words land in the diff the same way.
Failure case: JavaScript-only stores. watch fetches statically first, and some product pages render nothing without a browser; the check fails with quality blocked (empty) rather than pretending all is well. Add --render browser and Magpie escalates to a real browser fetch. It costs seconds per check, so keep the interval at 5 minutes or more.
Intervals below 30 seconds are refused. Five to fifteen minutes is the polite and sufficient range for a restock.
Step 2: Send the alert to your phone
--webhook turns the diff into a POST. On change, and only on change, Magpie sends one JSON body:
{"url":"https://news.ycombinator.com","changed":true,
"old_hash":"4f35cf…","new_hash":"e87afd…",
"diff":"- 57\n+ 59\n- [55\n+ [57\n…"}
That’s a real payload I caught with a local receiver while writing this. The retry policy: if your endpoint doesn’t answer, Magpie tries 1+3 times over roughly two minutes (2-second backoff, capped at 30 seconds between tries). Your receiver should answer 200 fast and process later.
The simplest useful receiver is a tiny Python server that forwards the diff to ntfy. I ran this exact code and confirmed the message landed on the ntfy topic:
from http.server import BaseHTTPRequestHandler, HTTPServer
import json, urllib.request
class Forward(BaseHTTPRequestHandler):
def do_POST(self):
body = json.loads(self.rfile.read(int(self.headers["Content-Length"])))
msg = f"Restock watch: {body['url']}\n{body['diff'][:3500]}"
req = urllib.request.Request(
"https://ntfy.sh/your-private-topic", msg.encode(),
{"Title": "Back in stock?"})
urllib.request.urlopen(req)
self.send_response(200); self.end_headers(); self.wfile.write(b"ok")
def log_message(self, *a): pass
HTTPServer(("127.0.0.1", 8898), Forward).serve_forever()
Wire it up:
python3 forward.py &
magpie watch "https://example.com/store/product/rtx-5080" --every 5m \
--webhook "http://127.0.0.1:8898/hook"
Webhook targets may point at 127.0.0.1: the SSRF guard that blocks private fetch targets deliberately allows operator-chosen private webhook URLs, so a localhost n8n or this receiver works. Pick an unguessable ntfy topic name; anyone who knows it can read your alerts. Discord and Slack want their own body shape, so route them through the same receiver pattern. The full webhook format, including signed delivery, is the subject of send scraped data to a webhook automatically; signature verification gets its own treatment in verify webhook signatures.
Step 3: Make it survive reboots
The watch loop dies when your SSH session ends. For 24/7 coverage, flip to cron mode: --once runs a single check (store or compare) and exits, keeping the baseline in Magpie’s local SQLite cache between runs.
*/5 * * * * ~/.local/bin/magpie watch "https://example.com/store/product/rtx-5080" --every 5m --once >> ~/watch.log 2>&1
I verified the mechanics with a fresh cache: the first --once stored a baseline silently, and a check 90 seconds later detected the change and printed the diff. Cron’s */5 sets the real cadence; --every is still required by the command and should match it.
What a real back in stock alert needs
A back in stock alert is change detection plus a condition. Magpie supplies the first half only. It diffs the whole cleaned page, so anything that moves fires the alert: the price ticking from £13.76 to £13.99, a review count, “23 people are viewing this”, a banner rotation. I watched HN’s front page for two minutes while testing and the diff was all noise — score counts and comment totals shifting (- 75 + 77). A busy retail page behaves the same way.
The fix is one line on your side. The diff already contains the words that changed, so filter it:
*/5 * * * * ~/.local/bin/magpie watch "https://example.com/store/product/rtx-5080" --every 5m --once \
| grep -qiE "in stock|sold out|out of stock|add to (basket|cart|bag)" \
&& curl -s -d "Back in stock: https://example.com/store/product/rtx-5080" \
https://ntfy.sh/your-private-topic
No stock words in the diff, no alert. grep -q exits silently, the && never fires. I tested both directions: an HN diff matched nothing, a simulated stock flip matched. Two refinements worth making:
- Watch the button, not just the badge. Many stores keep the product page alive and swap
Add to basketforSold out. The pattern above catches that swap. - Words alone can mislead.
Preorder nowappearing is a change, and if it contains none of your patterns, you’ll miss it. Widen the pattern when a store teaches you its vocabulary.
This is genuinely a workaround for a missing feature. If you want the condition handled for you, that’s a legitimate reason to pick a different tool.
When to use a different tool
- changedetection.io (Apache-2.0, free self-hosted; paid hosted tier) is the closest fit for condition-style alerting. You scope the monitor to a single element with a CSS selector or XPath, so noise is filtered before the alert rather than after. If you already run Docker, it’s the stronger choice for a shelf of products.
- Visualping (hosted, free plan covers about 5 pages checked once a day) needs no server at all. Daily checks are wrong for hot restocks, though. The GPU is gone in eleven minutes.
- Distill Web Monitor (browser extension, free tier, paid from $15/month) watches a single element and follows you across devices through its app. Element-level is genuinely useful on JS-heavy stores.
- NowInStock.net (free, ad-supported) skips your setup entirely: it tracks availability for hot products like GPUs and consoles across retailers. The trade-off is coverage — it watches what it watches, not the arbitrary URL you paste.
Where Magpie earns its keep: everything stays on your machine, the diff lands in your own pipeline as JSON, and the same install handles scraping, crawling, and watching. If you’re monitoring a whole competitor catalog rather than one product page, that’s a different job; see competitor website monitoring. And when the change you care about is a number with a currency sign, the setup in price drop alerts hits the same limitation and the same grep fix.
Make it stick
- Multiple products, one liner. A bash loop with one
watch --onceper line covers a wishlist; give each product its own ntfy topic so your phone buzzes differently. - Login-walled stores. Magpie’s session support persists cookies per site: log in once through a saved profile and later checks carry your session. The CLI has no credential flags; cookies come from the profile.
- Be polite. One product page every 5 minutes is trivial traffic: a few thousand requests a month, slower than any human visitor. Magpie respects robots.txt and throttles per host automatically. It does not bypass CAPTCHAs or bot walls, by design; stores that wall anonymous visitors may simply refuse. Amazon in particular serves different stock text to different sessions; the tracker setup in Amazon price scraping covers that store’s quirks.
- Expect pages to change shape. A store redesign can rename
In stocktoAvailableovernight. When your grep goes quiet for weeks, re-check the vocabulary rather than assuming the product vanished.
FAQ
How fast does the alert arrive?
Within one check interval. A 5-minute cron means up to 5 minutes late; a watch loop checks every interval continuously. The webhook POST itself lands in seconds. On my test setup, the message was on the ntfy topic within three.
Does this work on Amazon? Partly. Amazon walls and throttles anonymous automated traffic, and Magpie doesn’t bypass bot protections. Public product pages may load, but expect layout shifts and session-gated content. The dedicated guide covers what works there.
Will the store block me for polling?
At 5-minute intervals with default throttling, no realistic risk; that’s gentler than one person refreshing. Respect robots.txt disallows and back off if a store returns 429s.
Can I get an SMS instead of an app push? Not built in. ntfy push is free and instant; for SMS, forward from your receiver to Twilio or use a carrier email-to-SMS gateway in the same Python handler.
What about sites that need JavaScript to show stock?
Add --render browser to the watch. It launches a real browser per check, adds seconds of latency, and handles Vue/React storefronts that serve an empty shell statically.
Start tonight
Pick the product you actually want, run the Step 1 command once, and leave it. If the diff wakes you up for nothing this week, add the grep filter from “What a real back in stock alert needs” — that’s the moment the alert becomes a back in stock alert instead of a page-change notepad.


