Getting started
Quickstart
1. Get a key
Keys start with sw_test_ or sw_live_. A test key can send webhooks to http://localhost, which is handy while you build. Ask us for one, then keep it in an environment variable:
export SNAG_KEY=sw_test_...2. Preview
Preview fetches the page once and shows what a watcher would see. Nothing is saved.
curl https://watcher-api-staging.manwhas.shop/v1/watchers/preview \
-H "Authorization: Bearer $SNAG_KEY" \
-H "Content-Type: application/json" \
-d '{
"target": { "query": "https://www.sweetwater.com/store/detail/SM58--shure-sm58-cardioid-dynamic-vocal-microphone", "match": "url" },
"conditions": { "all": [{ "field": "price", "op": "<=", "value": 100 }] },
"where": { "sites": ["sweetwater.com"] },
"schedule": { "every": "1h", "until": "+30d" }
}'const response = await fetch('https://watcher-api-staging.manwhas.shop/v1/watchers/preview', {
method: 'POST',
headers: { Authorization: `Bearer ${process.env.SNAG_KEY}`, 'Content-Type': 'application/json' },
body: JSON.stringify({
target: { query: 'https://www.sweetwater.com/store/detail/SM58--shure-sm58-cardioid-dynamic-vocal-microphone', match: 'url' },
conditions: { all: [{ field: 'price', op: '<=', value: 100 }] },
where: { sites: ['sweetwater.com'] },
schedule: { every: '1h', until: '+30d' },
}),
});
console.log(await response.json());import os, requests
response = requests.post(
"https://watcher-api-staging.manwhas.shop/v1/watchers/preview",
headers={"Authorization": f"Bearer {os.environ['SNAG_KEY']}"},
json={
"target": {"query": "https://www.sweetwater.com/store/detail/SM58--shure-sm58-cardioid-dynamic-vocal-microphone", "match": "url"},
"conditions": {"all": [{"field": "price", "op": "<=", "value": 100}]},
"where": {"sites": ["sweetwater.com"]},
"schedule": {"every": "1h", "until": "+30d"},
},
)
print(response.json())
{
"kind": "price",
"schedule": { "every": "1h" },
"clamped": [],
"warnings": ["sweetwater.com has no extractor of its own; it's read as Any product page with schema.org data. Check the preview before relying on it."],
"would_match": true,
"sites": [
{
"site": "sweetwater.com",
"outcome": "ok",
"candidates": 1,
"matched": true,
"fields": [{ "title": "SM58 Cardioid Dynamic Vocal Microphone", "price": { "cents": 8900, "currency": "USD" }, "availability": "in_stock" }]
}
]
}3. Create it
Same body, plus where to send the webhook, to POST /v1/watchers:
curl https://watcher-api-staging.manwhas.shop/v1/watchers \
-H "Authorization: Bearer $SNAG_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: sm58-under-100" \
-d '{
"target": { "query": "https://www.sweetwater.com/store/detail/SM58--shure-sm58-cardioid-dynamic-vocal-microphone", "match": "url" },
"conditions": { "all": [{ "field": "price", "op": "<=", "value": 100 }] },
"where": { "sites": ["sweetwater.com"] },
"schedule": { "every": "1h", "until": "+30d" },
"notify": { "channels": ["webhook"], "webhook": { "url": "https://you.example/snag", "events": ["matched", "changed", "blocked"] } }
}'The response includes webhook_secret. It's shown once, so store it now. You'll use it to verify signatures.
4. Receive the webhook
The first run happens within a minute. When the price is at or under $100, we POST:
{
"id": "evt_4m2k9q8x7c1v0b3n5z6w",
"type": "matched",
"created_at": "2026-11-04T19:38:44.390Z",
"watcher_id": "wch_dhjqcvaqnhrchsnv7gtc",
"kind": "price",
"data": {
"group": "site:sweetwater.com",
"results": [{ "site": "sweetwater.com", "title": "SM58 Cardioid Dynamic Vocal Microphone", "price": { "cents": 8900, "currency": "USD" }, "availability": "in_stock" }]
}
}Answer with any 2xx. With repeat left at on_change, you hear again only when the result changes.