Skip to main content
POST
/
live-digest
const response = await fetch(
  "https://vibestream-production-64f3.up.railway.app/live-digest",
  {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      youtube_url: "https://youtube.com/watch?v=abc123",
      window_minutes: 5,
    }),
  }
);

const reader = response.body.getReader();
const decoder = new TextDecoder();

while (true) {
  const { done, value } = await reader.read();
  if (done) break;
  
  const text = decoder.decode(value);
  // Parse SSE events
  const events = text.split('\n\n');
  for (const event of events) {
    const match = event.match(/event: (\w+)\ndata: (.+)/);
    if (match) {
      const [, eventType, data] = match;
      console.log(eventType, JSON.parse(data));
    }
  }
}
event: job_started
data: {"job_id": "abc-123", "youtube_url": "https://youtube.com/watch?v=abc123"}

event: progress
data: {"frames_captured": 1, "frames_needed": 5}

event: progress
data: {"frames_captured": 2, "frames_needed": 5}

event: summary
data: {"summary": "Traffic flow was moderate...", "frame_count": 5, "window_minutes": 5, "grid_b64": "..."}

event: job_stopped
data: {"job_id": "abc-123", "reason": "max_duration_reached", "summaries_generated": 2}

Request

youtube_url
string
required
Live stream URL to summarize. Supports YouTube (youtube.com/watch?v=, youtu.be/) and Twitch (twitch.tv/channel) formats.
window_minutes
integer
default:"10"
Summary interval in minutes. Every N minutes, a summary is generated and streamed.
capture_interval_seconds
integer
default:"60"
How often to capture frames within each window (builds the frame buffer).
model
string
default:"claude-3-5-sonnet"
VLM model for summarization. Options: claude-3-5-sonnet, gpt-4o
skip_validation
boolean
default:"false"
Skip livestream validation if already validated by /validate-url.

Response (SSE Stream)

Returns a text/event-stream response with the following events:
EventDataDescription
job_started{job_id, youtube_url}Job has started
progress{frames_captured, frames_needed}Frame capture progress
summary{summary, frame_count, window_minutes, grid_b64}Generated summary
error{error_type, message}Error occurred
job_stopped{job_id, reason, summaries_generated}Job completed
const response = await fetch(
  "https://vibestream-production-64f3.up.railway.app/live-digest",
  {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      youtube_url: "https://youtube.com/watch?v=abc123",
      window_minutes: 5,
    }),
  }
);

const reader = response.body.getReader();
const decoder = new TextDecoder();

while (true) {
  const { done, value } = await reader.read();
  if (done) break;
  
  const text = decoder.decode(value);
  // Parse SSE events
  const events = text.split('\n\n');
  for (const event of events) {
    const match = event.match(/event: (\w+)\ndata: (.+)/);
    if (match) {
      const [, eventType, data] = match;
      console.log(eventType, JSON.parse(data));
    }
  }
}
event: job_started
data: {"job_id": "abc-123", "youtube_url": "https://youtube.com/watch?v=abc123"}

event: progress
data: {"frames_captured": 1, "frames_needed": 5}

event: progress
data: {"frames_captured": 2, "frames_needed": 5}

event: summary
data: {"summary": "Traffic flow was moderate...", "frame_count": 5, "window_minutes": 5, "grid_b64": "..."}

event: job_stopped
data: {"job_id": "abc-123", "reason": "max_duration_reached", "summaries_generated": 2}

Frame Selection

VibeStream uses farthest-point sampling to select diverse frames for summarization:
  • Captures frames at capture_interval_seconds rate
  • Selects visually diverse frames using O(n*k) algorithm
  • Sends a grid composite to the VLM for narrative generation