Most teams start the same way. They write a loop that fetches a product page every few minutes and compares the price with the last one. It works until it doesn't. The loop needs a server, a store for the last value and retry logic. It also spends a request on every pass, even when nothing changed.
Monitors move that loop to our side.
A monitor is a saved call plus a rule
You pick a marketplace, an endpoint and its parameters, the same ones you would pass to the API. Then you add a condition:
price_belowfires when the price is at or under a value you set.price_abovefires when the price is at or over a value.price_change_pctfires when the price moves by at least that percent since the last check, in either direction.back_in_stockfires when an item that was out of stock comes back.any_changefires when the price or stock state changes at all.
Each monitor has its own interval. The default is once a day. Our scheduler looks for due monitors every five minutes.
What a check costs
A monitor run is an ordinary metered call, so it costs what the same API call would cost. If your team is out of credits, we skip the run instead of fetching data you have not paid for. Nothing fires, and the next run tries again.
What you receive
When a condition fires, we email the addresses on the monitor and POST this payload to each webhook subscribed to monitor.triggered:
{
"event": "monitor.triggered",
"monitor": {"id": 42, "name": "Headphones under $200",
"marketplace": "walmart", "endpoint": "product_details"},
"condition": {"type": "price_below", "value": 200},
"current": {"price": 189.0, "in_stock": true},
"previous": {"price": 219.0, "in_stock": true},
"triggered_at": "2026-10-12T09:15:00"
}
You get the old and new values side by side, so your handler doesn't need to keep its own history.
Check the signature
Anyone who learns your webhook URL can post to it. Every delivery carries an X-Ferox-Signature header: an HMAC-SHA256 of the raw request body, keyed with the whsec_ secret we show once when you create the webhook. Check it before you act on the payload:
import hashlib
import hmac
def is_from_ferox(secret: str, raw_body: bytes, signature: str) -> bool:
expected = hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, signature)
Sign the raw bytes you received. If you parse the JSON and dump it again, the key order or spacing can change and the signature will not match.
Keep a fallback
We try each delivery, but a webhook can still fail if your server is down at the wrong moment. Treat webhooks as the fast path. Read the monitor's run history in the dashboard or API when you need the full record.