Skip to content

Price and stock watching

Waiting for a graphics card or a ticket to get cheaper, watching for an item to come back in stock? Instead of refreshing a tab ten times a day, put a check on cron: the script downloads the page itself, extracts the price and sends a push when something changes.

The script downloads the page, extracts the price (here via grep, though a CSS selector in pup or JSON via jq is often handier) and compares it to a stored value:

#!/usr/bin/env bash
# ~/bin/notifly-price — `notifly-price <url> <target_price>`
set -eu
set -a; source ~/.notifly.env; set +a
URL="${1:?url}"
TARGET="${2:-0}" # threshold: push if the price is at or below this
UA="Mozilla/5.0 (compatible; notifly-price/1.0)"
STATE="/tmp/notifly-price-$(echo -n "$URL" | md5sum | cut -c1-8).state"
PREV=$(cat "$STATE" 2>/dev/null || echo 0)
HTML=$(curl -fsSL --compressed -A "$UA" "$URL")
# Grab the first number from the price tag. Tune the selector to the site:
# many shops keep the price in JSON-LD ("price":"12990") — then use jq.
PRICE=$(echo "$HTML" | grep -oE '"price":"[0-9]+' | head -1 | grep -oE '[0-9]+')
[[ -z "$PRICE" ]] && { echo "Price not found — fix the selector" >&2; exit 1; }
# Did the price drop compared to the previous check?
if (( PREV > 0 )) && (( PRICE < PREV )); then
notifly-send "📉 Цена упала: $PRICE" "Было $PREV ₽\n$URL" 6
fi
# Reached the target threshold?
if (( TARGET > 0 )) && (( PRICE <= TARGET )) && (( PREV > TARGET || PREV == 0 )); then
notifly-send "🎯 Цена ≤ $TARGET" "Сейчас $PRICE ₽\n$URL" 8
fi
echo "$PRICE" > "$STATE"

Add it to cron once every two hours (be kind to the site — no more often):

# ~/bin/notifly-price <url> <target>
17 */2 * * * ~/bin/notifly-price 'https://shop.example.com/gpu-4080' 89990

Sometimes what matters isn’t the price but the fact it appeared. Detect it by a marker in the markup (“Add to cart” instead of “Out of stock”):

#!/usr/bin/env bash
# ~/bin/notifly-stock — `notifly-stock <url>`
set -eu
set -a; source ~/.notifly.env; set +a
URL="${1:?url}"
STATE="/tmp/notifly-stock-$(echo -n "$URL" | md5sum | cut -c1-8).state"
PREV=$(cat "$STATE" 2>/dev/null || echo OUT)
HTML=$(curl -fsSL --compressed -A "Mozilla/5.0 (notifly-stock)" "$URL")
# In-stock marker. Tune the string to the specific shop.
if echo "$HTML" | grep -qi 'в корзину\|add to cart'; then
NOW=IN
else
NOW=OUT
fi
if [[ "$NOW" == "IN" && "$PREV" == "OUT" ]]; then
notifly-send "🛒 Товар в наличии!" "$URL" 8
fi
echo "$NOW" > "$STATE"

Tip: reading the price from JSON with jq is more robust

Section titled “Tip: reading the price from JSON with jq is more robust”

Many sites put the price in JSON-LD <script type="application/ld+json">. That’s more stable than parsing HTML:

Окно терминала
PRICE=$(echo "$HTML" \
| grep -oP '(?<=<script type="application/ld\+json">).*?(?=</script>)' \
| jq -r '..|.price? // empty' | head -1)

A PowerShell equivalent. Assumes a configured function Send-Notifly.

C:\scripts\Notifly-Price.ps1
param([Parameter(Mandatory)][string]$Url, [int]$Target = 0)
. $env:USERPROFILE\Documents\WindowsPowerShell\Notifly.ps1
$hash = ([System.BitConverter]::ToString(
(New-Object Security.Cryptography.MD5CryptoServiceProvider).ComputeHash(
[Text.Encoding]::UTF8.GetBytes($Url))) -replace '-').Substring(0, 8)
$State = "$env:LOCALAPPDATA\notifly-price-$hash.txt"
$prev = if (Test-Path $State) { [int](Get-Content $State) } else { 0 }
$html = Invoke-WebRequest -Uri $Url -UserAgent "Mozilla/5.0 (notifly-price)" -UseBasicParsing
# First number from "price":"12990". Tune the selector to the site.
$price = [int]([regex]::Match($html.Content, '"price":"(\d+)').Groups[1].Value)
if ($price -eq 0) { throw "Price not found — fix the selector" }
if ($prev -gt 0 -and $price -lt $prev) {
Send-Notifly -Title "📉 Цена упала: $price" -Message "Было $prev`n$Url" -Priority 6
}
if ($Target -gt 0 -and $price -le $Target -and ($prev -gt $Target -or $prev -eq 0)) {
Send-Notifly -Title "🎯 Цена ≤ $Target" -Message "Сейчас $price`n$Url" -Priority 8
}
$price | Set-Content $State

Schedule it once every two hours:

Окно терминала
$Action = New-ScheduledTaskAction -Execute "powershell.exe" `
-Argument "-NoProfile -WindowStyle Hidden -ExecutionPolicy Bypass -File C:\scripts\Notifly-Price.ps1 -Url 'https://shop.example.com/gpu' -Target 89990"
$Trigger = New-ScheduledTaskTrigger -Once -At (Get-Date) `
-RepetitionInterval (New-TimeSpan -Hours 2)
Register-ScheduledTask -TaskName "Notifly Price Watch" -Action $Action -Trigger $Trigger
  • Catch the discount without living in the browser. Cron checks for you around the clock.
  • Threshold + drop. Separately “got cheaper” (priority 6) and “hit the goal” (priority 8, so you definitely don’t sleep through it).
  • Not just prices. The same trick works for any change on a page: an application status, a schedule, a free doctor’s appointment slot.
  • For sites where JavaScript draws everything, read the price with the built-in browser monitor instead of curl.
  • Keep a price history and send a chart of the weekly minimum.
  • Attach a product image link to the push via extra fields.