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
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
VibeStream validates the URL is a live stream
Captures a frame every 30 seconds
Pre-filter checks for motion (skips static frames)
If motion detected, VLM analyzes the frame
If snow is detected, webhook fires with live_monitor_result
Your server sends SMS via Twilio
After 10 minutes, job auto-stops and sends job_status
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:
Metric Per Job (10 min) Per Hour Per Day Frames captured 20 120 2,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