How to Monitor a Website for Changes with TinyMonitor

Most page changes are noise, like a typo fix or a layout tweak. If you're tracking a competitor's pricing, you want to hear when a price moves and nothing else. TinyMonitor lets you describe that change in one line and emails you only when it happens.
How TinyMonitor tracks website changes
- Each check fetches a fresh copy of the page (or reruns a search).
- It compares that copy with the last one.
- A model judges whether the difference matches your one-line purpose.
- You get an email only when it does.
We'll set up two monitors for one competitor:
- Page monitor on their pricing page
- Topic monitor that reruns a web search daily and flags funding, acquisition or launch news
The dashboard, REST API and MCP all create the same monitors, so we'll use each. In the product it's labeled "Monitor" (/monitor, /v1/monitors, create_monitor).
It isn't uptime monitoring. It watches what a page says, not whether the server responds.
Create a Page monitor in the dashboard
- Open the TinyMonitor dashboard and click Create your first monitor (or New monitor).
- Pick Page monitor, paste the pricing URL, and name it something like "Acme pricing".
- Under What do you want to monitor for?, write the condition:A paid plan's monthly price changes, or a plan is added or removed. Ignore copy, layout and testimonial changes.
- Check every day (default 09:00, your browser's timezone) and leave Email updates on.
- Click Create monitor.

Tips for the purpose line:
- Write it like an event condition: when to ping you, and what to ignore.
- Leave it empty and every change counts.
- There's no CSS selector. If you only care about part of the page, say so here ("only the Enterprise plan price").
The first check becomes the baseline, so it finds nothing and sends nothing.
💡Pricing pages can sit still for weeks. To see a real alert while learning, also create Bitcoin price movement from the Monitor ideas cards. It checks hourly and moves a lot.
Start monitoring a website with TinyMonitor
Create a monitor, define the change you care about, and let TinyMonitor check the page on your schedule.
Read the history
Each monitor's page has a Configuration panel, where its editable settings can be updated inline, and a history with two tabs:
- Meaningful: changes that met your goal, plus any the judge couldn't assess
- All checks: everything, including quiet rows where the page changed but nothing met your goal. That's the filter doing its job.

Click a meaningful change to see:
- a plain-language summary at the top
- the purpose it was judged against
- the full line-by-line diff below
The email carries the monitor name and the summary's first sentence in the subject, up to 10 changed lines, and a link back.

Create a Topic monitor with the REST API
Send your TinyFish API key in the X-API-Key header. A Topic monitor is "type": "search", and its config takes a query, a recency window in minutes, and 1 to 10 results per check:
curl -X POST <https://agent.tinyfish.ai/v1/monitors> \
-H "X-API-Key: $TINYFISH_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"type": "search",
"name": "Acme news",
"config": {
"query": "Acme Analytics funding, acquisition or product launch",
"recency_minutes": 1440,
"result_limit": 10
},
"schedule_cron": "CRON_TZ=America/New_York 0 9 * * *",
"purpose": "Acme Analytics raises money, is acquired, launches a product or changes pricing. Ignore listicles and opinion pieces.",
"webhook_url": "<https://example.com/webhooks/monitor>"
}'The 201 response has the monitor and its baseline run. Topic monitors don't take a domain filter, so a specific query (company name plus the events you want) does most of the steering.
Track website changes with the REST API
The pricing monitor is "type": "fetch". In Python, with the management calls:
import os, requests
API = "<https://agent.tinyfish.ai/v1/monitors>"
HEADERS = {"X-API-Key": os.environ["TINYFISH_API_KEY"]}
monitor = requests.post(API, headers=HEADERS, timeout=120, json={
"type": "fetch",
"name": "Acme pricing",
"config": {"url": "<https://example.com/pricing>", "format": "markdown"},
"schedule_cron": "CRON_TZ=America/New_York 0 9 * * *",
"purpose": "A paid plan's monthly price changes, or a plan is added or removed.",
}).json()["monitor"]
url = f"{API}/{monitor['id']}"
requests.patch(url, headers=HEADERS, json={"status": "paused"}) # or "active"
requests.patch(url, headers=HEADERS, json={"schedule_cron": "0 * * * *"})
requests.post(f"{url}/runs", headers=HEADERS) # run a check now- A PATCH changes either the status or the schedule and metadata, not both at once.
- Type, URL and query are fixed after creation. To change them, delete and recreate.
Get notified when a website changes
⚠️ Monitors created over the API or MCP start with email off, and the API can't turn it on. The webhook doesn't carry the judgment either, so until you enable email, only the dashboard tells you whether a change mattered.
- Open the monitor and go to Configuration, then Notifications.
- Turn on Email updates.
- Leave Email every run off to get email only when a check finds a change, or switch it on to get every completed run.

Handle the webhook
Every scheduled check POSTs the raw run to your webhook_url, even when nothing changed, plus search_monitor_id or fetch_monitor_id.
| Monitor | What the payload includes |
|---|---|
| Topic | results and new_result_positions |
| Page | is_baseline, results and errors |
The diff, judgment and summary aren't in there, so your receiver does its own filtering. This one keeps new search results and hashes page text:
import hashlib
from flask import Flask, request
app = Flask(__name__)
seen_runs, last_hash = set(), {} # use a database in production
@app.post("/webhooks/monitor")
def monitor_webhook():
run = request.get_json()
if run["id"] in seen_runs: # retries can deliver a run twice
return "", 200
seen_runs.add(run["id"])
if "search_monitor_id" in run:
new = set(run.get("new_result_positions") or [])
for r in run["results"]:
if r["position"] in new:
print(f"New: {r['title']} {r['url']}")
elif "fetch_monitor_id" in run and run["results"]:
digest = hashlib.sha256(run["results"][0].get("text", "").encode()).hexdigest()
if last_hash.get(run["fetch_monitor_id"]) not in (None, digest):
print(f"Page changed: {run['results'][0]['url']}")
last_hash[run["fetch_monitor_id"]] = digest
return "", 200Swap print for Slack or your own agent. Our webhooks how-to has a Slack forwarder.
Things to know:
- Public URLs only. To test, point webhook_url at webhook.site and wait for a scheduled check.
- Retries: failed deliveries get up to 3 best-effort retries, so dedupe on id and return 200 fast.
⚠️ The baseline and the manual Run button never call the webhook. Only scheduled checks do.
Set it up from an AI assistant (MCP)
The TinyFish MCP server has seven monitor tools: create_monitor, list_monitors, get_monitor, pause_monitor, resume_monitor, run_monitor and cancel_monitor.
- Connect it. In Claude Code:
npx -y install-mcp@latest <https://agent.tinyfish.ai/mcp> --client claude-code- claude.ai has a TinyFish connector and ChatGPT a plugin, both linked from the MCP setup page.
- Ask for a monitor in plain words:Watch https://example.com/pricing every day at 9am New York time. Only flag it when a paid plan's price changes or a plan is added or removed.
The assistant calls create_monitor with the API's fields and shows you the baseline. "Pause the Acme news monitor" works too, and cancel_monitor deletes for good.
Same caveats as the API: email starts off, and get_monitor and run_monitor return the raw check, not the verdict.
Schedules and cost
Hourly, daily, weekly and custom work for both types. Over the API, schedule_cron is five-field cron in UTC, or prefix it with CRON_TZ=America/New_York for local time (crontab.guru helps).
It's $0.005 per completed check:
| Schedule | Cost per month |
|---|---|
| Daily | about $0.15 |
| Hourly | about $3.65 |
| Every 30 minutes (Topic monitors only) | about $7.30 |
- The baseline and each manual Run count as a check.
- Failed checks are free.
- Email, webhooks and the judgment are included.
Troubleshoot website change monitoring
| Symptom | What's happening | What to do |
|---|---|---|
| A change but no email | The judge decided it didn't match your purpose, or couldn't judge it | Unjudged changes only email with Email every run on |
| Too many alerts | The purpose or query is too broad | Tighten the purpose and say what to ignore. For Topic monitors, narrow the query, shorten the recency window or lower the result count. They'll still surface some noise. |
| A missed change | Page monitors compare extracted text | Content behind a login or rendered heavily client-side may not show up |
| Monitor paused itself | 4 failed scheduled checks in a row (you get an email), or a scheduled check hit an empty wallet | Fix the cause or add funds, then resume from the detail page or with PATCH {"status": "active"} |
| 403 on create | You've hit 50 monitors per account, paused and failed ones included | Delete monitors you don't need |
| 409 RETRY_REQUIRED | The state changed mid-request | Send it again |
Monitor website changes without chasing every update
Website change monitoring is most useful when you can define what actually matters and ignore everything else. With TinyMonitor, you can monitor a website for changes, track broader topics through search, and choose how often those checks run.
You can set up monitors from the dashboard, REST API, or MCP, then review meaningful changes in the dashboard or get notified when website changes match the condition you defined. Start with one page or topic you already check manually, define the change you care about, and let TinyMonitor handle the recurring checks.
AI disclosure
Content on this website may be created or refined with the assistance of AI tools and is subject to human editorial review.



