โ† Back to blog

Website Watchdog API: Monitor Public URLs for Changes with curl, Python & AI Agents

July 16, 2026 ยท 6 min read ยท Web Monitoring

Your agent needs to know when a competitor updates their pricing page, when documentation drops a new section, or when a status page flips from green to red. Polling manually is fragile. Checking every minute burns credits. What you want is a background watcher that only talks to you when something actually changes.

The Website Watchdog at Agent Media Tools is exactly that: register a URL once, pick a check interval, and get notified via an account webhook, Discord webhook, or email when the content changes. Two watches are free โ€” enough to cover a status page and a docs index without spending a dime.

Quick start โ€” free tier

Free accounts get 2 active watches; Pro (โ€ฏ$5/moโ€ฏ) unlocks 25 and Builder unlocks 50. Check intervals can be set from 10 minutes to 24 hours. No credit card for the free tier โ€” sign up and grab your API key in under two minutes.

How it works

The Watchdog runs as a background loop in the Agent Media Tools server. Every ten minutes it picks up due watches (configured interval elapsed), fetches the page content, hashes it, and compares against the stored hash. On a mismatch it:

  1. Records the change event with before/after previews
  2. Fires any linked webhook with a watchdog.change payload
  3. Sends a Discord message or email if you configured those channels

Creating your first watch

You'll need an API key. Get one from /first-win, then export it:

export AMT_KEY="mt_xxxxxxxxxxxx"

POST /api/agent/watch โ€” Register a URL

๐Ÿ’ก Windows users: Run these from Git Bash or WSL โ€” PowerShell's curl is an alias for Invoke-WebRequest. Or use Invoke-RestMethod with the same URL/body.

curl -s -X POST https://agentmediatools.com/api/agent/watch \
  -H "Authorization: Bearer $AMT_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com/status",
    "label": "Example Status Page",
    "interval_minutes": 60,
    "notify_email": "you@example.com"
  }' | jq .

Response:

{
  "success": true,
  "watch": {
    "id": 47,
    "url": "https://example.com/status",
    "label": "Example Status Page",
    "interval_minutes": 60,
    "webhook_id": null,
    "discord_webhook": null,
    "notify_email": "you@example.com",
    "next_check_at": "2026-07-16T12:00:00.000Z",
    "active": true
  },
  "plan": "free",
  "limit": 2,
  "active_watches": 1
}

The watch starts instantly โ€” next_check_at is set to now so the first check runs on the upcoming tick.

Parameters

FieldRequiredDescription
urlโœ…Public HTTP(S) URL to watch. Private/reserved ranges are blocked for SSRF safety.
labelโŒHuman-readable name (max 100 chars). Helps identify the watch in lists.
interval_minutesโŒCheck interval: 10โ€“1440 minutes (default 60).
webhook_idโŒID of a registered webhook to fire on watchdog.change events.
discord_webhookโŒDiscord incoming webhook URL for change notifications.
notify_emailโŒEmail address to notify on change.

Listing your watches

GET /api/agent/watch โ€” Show all watches

curl -s https://agentmediatools.com/api/agent/watch \
  -H "Authorization: Bearer $AMT_KEY" | jq .
{
  "success": true,
  "plan": "free",
  "limit": 2,
  "watches": [
    {
      "id": 47,
      "url": "https://example.com/status",
      "label": "Example Status Page",
      "interval_minutes": 60,
      "webhook_id": null,
      "discord_webhook": null,
      "notify_email": "you@example.com",
      "is_active": true,
      "last_checked_at": "2026-07-16 12:05:22",
      "last_changed_at": null,
      "next_check_at": "2026-07-16 13:05:22",
      "consecutive_failures": 0,
      "history_url": "/api/agent/watch/47/history"
    }
  ],
  "active_count": 1,
  "total": 1
}

Each watch includes a history_url โ€” hit that endpoint to see past change events.

Reading change history

GET /api/agent/watch/:id/history โ€” Change event log

curl -s https://agentmediatools.com/api/agent/watch/47/history \
  -H "Authorization: Bearer $AMT_KEY" | jq .
{
  "success": true,
  "watch_id": 47,
  "url": "https://example.com/status",
  "label": "Example Status Page",
  "count": 3,
  "events": [
    {
      "id": 12,
      "watch_id": 47,
      "event_type": "change",
      "previous_hash": "a1b2c3d4...",
      "new_hash": "e5f6g7h8...",
      "preview_before": "All systems operational\nUptime: 99.97%",
      "preview_after": "Degraded performance reported\nRegion: us-east-1",
      "status_code": 200,
      "created_at": "2026-07-16 13:05:22"
    }
  ]
}

The preview_before and preview_after fields provide a short content sample from before and after the change โ€” often enough to decide whether this matters without fetching the full page again.

Pausing, resuming, and deleting

PATCH /api/agent/watch/:id โ€” Manage a watch

Use the action field to pause, resume, update notify channels, or delete:

# Pause โ€” stops checking until you resume
curl -s -X PATCH https://agentmediatools.com/api/agent/watch/47 \
  -H "Authorization: Bearer $AMT_KEY" \
  -H "Content-Type: application/json" \
  -d '{"action": "pause"}' | jq .

# Resume
curl -s -X PATCH https://agentmediatools.com/api/agent/watch/47 \
  -H "Authorization: Bearer $AMT_KEY" \
  -d '{"action": "resume"}' | jq .

# Update notification channels
curl -s -X PATCH https://agentmediatools.com/api/agent/watch/47 \
  -H "Authorization: Bearer $AMT_KEY" \
  -d '{
    "action": "update",
    "discord_webhook": "https://discord.com/api/webhooks/...",
    "interval_minutes": 30
  }' | jq .

# Delete โ€” removes the watch and its event history
curl -s -X PATCH https://agentmediatools.com/api/agent/watch/47 \
  -H "Authorization: Bearer $AMT_KEY" \
  -d '{"action": "delete"}' | jq .

Python example: watcher agent

This script creates a watch on a documentation URL, polls its history until a change is detected, then summarizes the diff. Useful for CI pipelines or agent workflows that need to react to docs changes.

#!/usr/bin/env python3
"""Website Watchdog agent โ€” create a watch, wait for change, report."""
import os, sys, json, time, hashlib
import urllib.request, urllib.error

API = "https://agentmediatools.com"
KEY = os.environ.get("AMT_KEY")
if not KEY:
    print("Set AMT_KEY environment variable")
    sys.exit(1)

HEADERS = {
    "Authorization": f"Bearer {KEY}",
    "Content-Type": "application/json",
}

def api(path, method="GET", data=None):
    req = urllib.request.Request(f"{API}{path}", data=data, headers=HEADERS, method=method)
    with urllib.request.urlopen(req) as resp:
        return json.loads(resp.read())

# 1. Create a watch on a changelog or docs page
target_url = "https://docs.example.com/changelog"
watch = api("/api/agent/watch", "POST",
    json.dumps({
        "url": target_url,
        "label": "Docs Changelog",
        "interval_minutes": 60,
        "notify_email": "agent@example.com",
    }).encode()
)["watch"]
watch_id = watch["id"]
print(f"Created watch {watch_id} on {target_url}")

# 2. Poll history every 30 seconds for up to 5 minutes
print("Waiting for first change detection (up to 5 min)...")
start = time.time()
while time.time() - start < 300:
    history = api(f"/api/agent/watch/{watch_id}/history")
    if history["count"] > 0:
        event = history["events"][0]
        print(f"\n  Change detected at {event['created_at']}")
        print(f"  Status: {event['status_code']}")
        print(f"  Before preview: {event.get('preview_before', '(none)')[:120]}")
        print(f"  After preview:  {event.get('preview_after', '(none)')[:120]}")
        break
    time.sleep(30)
else:
    print("No change detected within timeout. The page may be static.")

# 3. Clean up the watch
api(f"/api/agent/watch/{watch_id}", "PATCH",
    json.dumps({"action": "delete"}).encode()
)
print(f"Watch {watch_id} deleted.")

Webhook + n8n integration

Instead of polling, register a webhook URL and let the Watchdog push change events to you. Here's how to wire it into n8n:

  1. Create a webhook target in n8n: add a Webhook node (POST, no auth), copy the production URL.
  2. Register the webhook at Agent Media Tools via /api/agent/webhook with events ["watchdog.change"]. Note the returned webhook ID.
  3. Create your watch with webhook_id set to that ID:
# Register webhook target
curl -s -X POST https://agentmediatools.com/api/agent/webhook \
  -H "Authorization: Bearer $AMT_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://your-n8n.example.com/webhook/watchdog-alert",
    "events": ["watchdog.change"],
    "label": "n8n watchdog alerts"
  }'

# Create watch linked to that webhook
curl -s -X POST https://agentmediatools.com/api/agent/watch \
  -H "Authorization: Bearer $AMT_KEY" \
  -d '{
    "url": "https://status.example.com",
    "label": "Production Status",
    "interval_minutes": 30,
    "webhook_id": 12
  }'

When a change is detected, the Watchdog POSTs a JSON payload to your n8n endpoint:

{
  "event": "watchdog.change",
  "watch_id": 47,
  "url": "https://status.example.com",
  "label": "Production Status",
  "previous_preview": "All systems operational",
  "current_preview": "Degraded performance โ€” us-east-1",
  "detected_at": "2026-07-16T13:05:22Z"
}

Limits per plan

PlanActive watchesMin intervalNotifications
Free260 minEmail + webhook
Pro ($5/mo)2510 minEmail + webhook + Discord
Builder ($15/mo)5010 minAll channels

๐Ÿ’ก Tip: Use shorter intervals (10โ€“15 min) for status pages and pricing pages. Use hourly intervals for docs, blog archives, and reference sites that change less frequently. Each plan limits the number of active watches, and deleting old watches frees those slots.

MCP tool access

If your agent connects via MCP (Claude Desktop, Cursor, etc.), the Watchdog tools are already available. Look for these MCP tools in the Agent Media Tools MCP server:

See the MCP setup guide for the connection details, or use the Claude MCP extension for the fastest path.

What to watch

Good candidate URLs for your first watches:

Try it now

Get your API key โ†’
Open the Watchdog dashboard โ†’
Full monitoring workflow playbook โ†’
Pricing โ†’

The Website Watchdog is designed for public content monitoring. Do not use it to bypass authentication, scrape private pages, or monitor content you do not have permission to access. Private and reserved network destinations are blocked server-side.