api-monitoring-bots skill
Build monitoring bots that poll APIs and send notifications on state changes (new listings, price alerts, status updates)
Is the api-monitoring-bots skill safe?
Clean: nothing in its files matched our rules. We read 1 file in the folder on 2026-09-28.
- medium
SKILL.md:619Edits shell startup files, cron or launch agents, so something runs again after the skill is done.
sudo systemctl enable api-monitor
Install the api-monitoring-bots skill
A skill is a folder. Copy it into your agent's skills folder and the agent loads it when the task matches its description.
git clone --depth 1 https://github.com/kevinnft/ai-agent-skills.git /tmp/ai-agent-skills mkdir -p ~/.claude/skills cp -r /tmp/ai-agent-skills/skills/devops/api-monitoring-bots ~/.claude/skills/api-monitoring-bots
In the Claude apps, zip the folder and upload it from the Skills settings. The folder on GitHub
The instructions your agent would load
SKILL.md as published, without the frontmatter. Read it on GitHub
API Monitoring Bots
Build lightweight monitoring bots that poll REST APIs and send notifications when state changes (new items, price alerts, status updates).
When to Use
- User wants notifications for new listings/posts/items
- Need to track price changes or threshold alerts
- Monitor API for specific conditions
- Watchdog for service status changes
Architecture Pattern
Stateful polling bot:
- Fetch current state from API
- Compare with last known state (stored in file)
- Detect changes (new IDs, price deltas, status transitions)
- Format and send notifications
- Update state file
Key principle: Silent when no changes (watchdog pattern, no spam).
Implementation
1. Core Script Structure
#!/usr/bin/env python3
import requests
import json
from pathlib import Path
from datetime import datetime
API_URL = "https://api.example.com/items"
STATE_FILE = Path.home() / ".hermes" / "monitor_state.json"
def load_state():
"""Load last seen state"""
if STATE_FILE.exists():
with open(STATE_FILE) as f:
return json.load(f)
return {"seen_ids": [], "last_check": None}
def save_state(state):
"""Save current state"""
STATE_FILE.parent.mkdir(parents=True, exist_ok=True)
with open(STATE_FILE, "w") as f:
json.dump(state, f, indent=2)
def fetch_items():
"""Fetch current items from API"""
try:
r = requests.get(API_URL, timeout=10)
r.raise_for_status()
return r.json()
except Exception as e:
return None
def check_new_items():
"""Check for new items and return notifications"""
state = load_state()
items = fetch_items()
if not items:
return []
seen_ids = set(state["seen_ids"])
new_items = []
for item in items:
if item["id"] not in seen_ids:
new_items.append(item)
seen_ids.add(item["id"])
# Update state (k2. Hermes Cron Integration
Create cron job:
hermes cron create \
--name "api-monitor" \
--schedule "every 1m" \
--script "monitor.py" \
--no-agent \
--deliver "telegram:Username"Key flags:
- --no-agent: Pure script execution, no LLM cost
- --deliver "telegram:Username": Send to specific Telegram user/group (use send_message list to see targets)
- --schedule "every 1m": Run every minute (adjust as needed)
⚠️ CRITICAL: --deliver origin does NOT work for cron jobs — it doesn't know the chat ID. Always use explicit target like telegram:Username or telegram:GroupName.
Delivery target patterns:
- telegram:Username — DM to specific user (use send_message list to see available names)
- telegram:GroupName — Group chat (must be in available targets list)
- telegram:-1001234567890 — Chat ID (numeric, for channels/groups not in target list)
- ❌ telegram:https://t.me/channelname — DOES NOT WORK (causes invalid literal for int() error)
Getting chat ID for channels:
- Forward a message from the channel to @userinfobot
- Bot replies with chat ID (format: -100xxxxxxxxxx)
- Use that numeric ID: --deliver "telegram:-1003963927119"
Delivery target patterns:
- telegram:Username — DM to specific user (use send_message list to see available names)
- telegram:GroupName — Group chat (must be in available targets list)
- telegram:-1001234567890 — Chat ID (numeric, for channels/groups not in target list)
- ❌ telegram:https://t.me/channelname — DOES NOT WORK (causes invalid literal for int() error)
Getting chat ID for channels:
- Forward a message from the channel to @userinfobot
- Bot replies with chat ID (format: -100xxxxxxxxxx)
- Use that numeric ID: --deliver "telegram:-1003963927119"
Management:
# List jobs
hermes cron list
# Pause monitoring
hermes cron pause <job_id>
# Resume monitoring
hermes cron resume <job_id>
# Check logs
cat ~/.hermes/cron/logs/<job_id>.log3. Notification Formatting
Markdown for Telegram:
def format_notification(item):
# Detect anomalies
alert = ""
if item["price"] < threshold:
alert = "🚨 ALERT! "
return f"""
{alert}**New Item Detected**
💰 **Price:** {item['price']}
📊 **Quantity:** {item['quantity']}
🆔 **ID:** `{item['id'][:8]}...`
⏰ **Created:** {item['created_at']}
🔗 https://example.com/item/{item['id']}
""".strip()Alert levels:
- 🚨 Critical (price anomaly, urgent action)
- ⚠️ Warning (notable but not urgent)
- ℹ️ Info (normal notification)
- (no emoji) Standard update
4. State Management
State file structure:
{
"seen_ids": ["id1", "id2", "..."],
"last_check": "2026-05-10T13:00:00",
"last_price": 0.5,
"alert_count": 3
}Best practices:
- Keep last 1000 IDs max (prevent unbounded growth)
- Store timestamp for debugging
- Include metadata for threshold tracking
- Use .hermes/ directory for state files
State file location:
STATE_FILE = Path.home() / ".hermes" / f"{bot_name}_state.json"Patterns
Pattern 1: New Item Detection (rpow2swap.com case study)
Use case: Notify on every new token listing.
API: https://rpow2swap.com/api/listings
Response:
[
{
"id": "uuid",
"type": "sell",
"amount": 10000,
"sol_price": 10,
"status": "active",
"created_at": "2026-05-10 02:43:08",
"solana_wallet_address": "..."
}
]Detection logic:
def check_new_listings():
state = load_state()
listings = fetch_listings()
seen_ids = set(state["seen_ids"])
new_listings = []
for listing in listings:
if listing["status"] == "active" and listing["id"] not in seen_ids:
new_listings.append(listing)
seen_ids.add(listing["id"])
state["seen_ids"] = list(seen_ids)[-1000:]
save_state(state)
return new_listingsNotification:
def format_listing(listing):
price_per_token = listing["sol_price"] / listing["amount"]
alert = ""
if price_per_token < 0.0001:
alert = "🚨 SANGAT MURAH! "
elif price_per_token < 0.0005:
alert = "⚠️ Harga Menarik! "
return f"""
{alert}**Token Listing Baru**
💰 Amount: {listing['amount']:,} RPOW2
📊 Total: {listing['sol_price']:.4f} SOL
💵 Price/Token: {price_per_token:.6f} SOL
🆔 ID: `{listing['id'][:8]}...`
👤 Wallet: `{listing['solana_wallet_address'][:8]}...`
⏰ {listing['created_at']}
🔗 https://rpow2swap.com/
"""Cron setup:
hermes cron create \
--name "rpow2swap-monitor" \
--schedule "every 1m" \
--script "rpow2_monitor.py" \
--no-agent \
--deliver "telegram:Aboy" # Use explicit target, NOT "origin"Files:
- Script: ~/.hermes/scripts/rpow2_monitor.py
- State: ~/.hermes/rpow2monitorstate.json
See references/rpow2swap-monitor.md for full implementation.
Pattern 2: Price Threshold Alert
Use case: Alert when price drops below threshold.
def check_price_alert():
state = load_state()
data = fetch_price()
if not data:
return None
current_price = data["price"]
last_price = state.get("last_price", current_price)
# Alert on 10% drop
if current_price < last_price * 0.9:
alert = f"🚨 Price dropped {((last_price - current_price) / last_price * 100):.1f}%"
state["last_price"] = current_price
save_state(state)
return alert
# Update price silently
state["last_price"] = current_price
save_state(state)
return NonePattern 3: Status Change Detection
Use case: Monitor service status, alert on downtime.
def check_status():
state = load_state()
try:
r = requests.get(API_URL, timeout=5)
current_status = "up" if r.status_code == 200 else "down"
except:
current_status = "down"
last_status = state.get("last_status", "up")
if current_status != last_status:
state["last_status"] = current_status
state["status_changed_at"] = datetime.now().isoformat()
save_state(state)
if current_status == "down":
return "🚨 Service is DOWN!"
else:
return "✅ Service is back UP"
return None # Silent when no changeMore skills from kevinnft/ai-agent-skills
- Aaddyosmani-tddDrives development with tests. Use when implementing any logic, fixing any bug, or changing any behavior. Use when you need to prove that code works, when a bug report arrives, or when you're about to modify existing functionality.
- AairtableAirtable REST API via curl. Records CRUD, filters, upserts.
- Aapi-and-interface-designGuides stable API and interface design. Use when designing APIs, module boundaries, or any public interface. Use when creating REST or GraphQL endpoints, defining type contracts between modules, or establishing boundaries between frontend and backend.
- Aapple-notesManage Apple Notes via memo CLI: create, search, edit.
- Aapple-remindersApple Reminders via remindctl: add, list, complete.
- Aarchitecture-diagramDark-themed SVG architecture/cloud/infra diagrams as HTML.
- AarxivSearch arXiv papers by keyword, author, category, or ID.
- Aascii-artASCII art: pyfiglet, cowsay, boxes, image-to-ascii.
- Aascii-videoASCII video: convert video/audio to colored ASCII MP4/GIF.
- AaudiocraftAudioCraft: MusicGen text-to-music, AudioGen text-to-sound.
- CaxolotlAxolotl: YAML LLM fine-tuning (LoRA, DPO, GRPO).
- Abaoyu-comicKnowledge comics (知识漫画): educational, biography, tutorial.