Skip to main content
Let’s build a system that monitors a Pittsburgh webcam and sends you an SMS via Twilio when snow is detected.
Remember: Jobs auto-stop after 10 minutes. For continuous monitoring, your webhook should restart the job.

The Setup

We’ll use:
  • VibeStream to monitor a Pittsburgh traffic cam
  • Twilio to send SMS alerts
  • A simple webhook receiver that restarts jobs when they expire

Step 1: Find a Live Stream

Pittsburgh has several public traffic cams. Search YouTube for “pittsburgh live cam” or “traffic cam live”.
The URL must be an active live stream. Regular videos and past streams will be rejected with a 400 error.

Step 2: Create the Webhook Receiver

webhook_server.py
from fastapi import FastAPI, Request, BackgroundTasks
from twilio.rest import Client
import httpx
import os

app = FastAPI()

twilio_client = Client(
    os.environ["TWILIO_ACCOUNT_SID"],
    os.environ["TWILIO_AUTH_TOKEN"]
)

VIBESTREAM_API = "https://vibestream-production-64f3.up.railway.app"

# Track current job
current_job_id = None
YOUTUBE_URL = "https://youtube.com/watch?v=PITTSBURGH_LIVE_CAM"
CONDITION = "Is it snowing? Look for falling snowflakes or white accumulation."

async def start_live_monitor_job():
    """Start or restart the live monitor job."""
    global current_job_id

    async with httpx.AsyncClient() as client:
        response = await client.post(f"{VIBESTREAM_API}/live-monitor", json={
            "youtube_url": YOUTUBE_URL,
            "condition": CONDITION,
            "webhook_url": f"{os.environ['WEBHOOK_HOST']}/snow-alert",
            "interval_seconds": 30
        })

        if response.status_code == 200:
            current_job_id = response.json()["job_id"]
            print(f"Started job: {current_job_id}")
        else:
            print(f"Failed to start job: {response.text}")

@app.on_event("startup")
async def startup():
    """Start monitoring on server startup."""
    await start_live_monitor_job()

@app.post("/snow-alert")
async def snow_alert(request: Request, background_tasks: BackgroundTasks):
    payload = await request.json()

    # Handle trigger events
    if payload.get("type") == "live_monitor_result":
        data = payload.get("data", {})

        if data.get("triggered"):
            # Send SMS alert
            twilio_client.messages.create(
                body=f"It's snowing in Pittsburgh!\n\n{data.get('explanation', '')}",
                from_=os.environ["TWILIO_PHONE"],
                to=os.environ["MY_PHONE"]
            )
            print("Snow alert sent!")

    # Handle job status events (auto-stop after 10 min)
    elif payload.get("type") == "job_status":
        status = payload.get("status")
        details = payload.get("details", {})

        if status == "stopped":
            # Check if auto-stopped due to 10-minute limit
            if details.get("auto_stopped"):
                print("Job auto-stopped after 10 minutes, restarting...")
                background_tasks.add_task(start_live_monitor_job)
            else:
                print("Job was manually stopped")

    return {"status": "ok"}
Deploy this to Railway, Vercel, or any hosting provider.

Step 3: Start Monitoring

The webhook server automatically starts the job on startup. If you need to manually start:
curl -X POST https://vibestream-production-64f3.up.railway.app/live-monitor \
  -H "Content-Type: application/json" \
  -d '{
    "youtube_url": "https://youtube.com/watch?v=PITTSBURGH_LIVE_CAM",
    "condition": "Is it snowing? Look for falling snowflakes or white accumulation.",
    "webhook_url": "https://your-webhook-server.com/snow-alert",
    "interval_seconds": 30
  }'

What Happens

  1. VibeStream validates the URL is a live stream
  2. Captures a frame every 30 seconds
  3. Pre-filter checks for motion (skips static frames)
  4. If motion detected, VLM analyzes the frame
  5. If snow is detected, webhook fires with live_monitor_result
  6. Your server sends SMS via Twilio
  7. After 10 minutes, job auto-stops and sends job_status
  8. Your server restarts the job automatically

Webhook Payloads

Watch Trigger

{
  "type": "live_monitor_result",
  "timestamp": "2024-01-26T15:30:00Z",
  "source_url": "https://youtube.com/watch?v=PITTSBURGH_LIVE_CAM",
  "data": {
    "triggered": true,
    "condition": "Is it snowing?",
    "explanation": "Light snow is falling, visible as white particles against the dark buildings.",
    "frame_b64": "base64-encoded-image..."
  }
}

Job Auto-Stop (10 minutes)

{
  "type": "job_status",
  "job_id": "abc123...",
  "status": "stopped",
  "details": {
    "checks_performed": 20,
    "triggers_fired": 3,
    "frames_skipped": 80,
    "auto_stopped": true,
    "reason": "max_duration_reached",
    "elapsed_seconds": 600
  }
}

Cost Analysis

With 10-minute job cycles and 30-second intervals:
MetricPer Job (10 min)Per HourPer Day
Frames captured201202,880
VLM calls (with pre-filter)~5-10~30-60~720-1,440
Gemini cost~$0.001~$0.006~$0.14
The pre-filter typically saves 50-70% on API costs by skipping static frames.

Monitoring Dashboard

Check your job status and metrics:
# Current jobs
curl https://vibestream-production-64f3.up.railway.app/jobs

# API usage metrics
curl https://vibestream-production-64f3.up.railway.app/metrics

# Recent logs
curl "https://vibestream-production-64f3.up.railway.app/logs?level=INFO&limit=20"

Wrapping Up

You now have a production-ready snow alerting system that:
  • Automatically restarts after 10-minute job limits
  • Sends SMS alerts via Twilio
  • Costs less than $0.15/day with pre-filtering
The same pattern works for:
  • Rain detection
  • Fog/visibility monitoring
  • Traffic incident detection
  • Crowd density monitoring

Full API Reference

Explore all live monitor endpoint options