Workflow

YouTube Comment Monitoring Pipeline

Daily automated YouTube comment collection with sentiment scoring. Monitor brand mentions, competitor channels, and audience feedback.

Overview

This workflow collects YouTube comments daily for monitored channels and videos, scores them for sentiment, and surfaces notable mentions, complaints, and feature requests. It replaces manual YouTube Studio checks with an automated pipeline that catches important audience feedback within 24 hours.

Trigger

Cron schedule (daily at 9 AM UTC)

Schedule

Runs daily at 9 AM UTC

Workflow Steps

1

Load monitored channels and keywords

Read the list of channels, video URLs, and brand keywords to monitor from configuration.

2

Search for recent videos

Query YouTube via Scavio for recent videos from monitored channels and keywords.

3

Extract comment data

Pull structured comment data including text, author, timestamp, and like count.

4

Score sentiment

Apply basic keyword-based sentiment scoring to categorize comments as positive, negative, or neutral.

5

Flag notable comments

Surface comments with high engagement, brand mentions, or negative sentiment for review.

6

Generate daily digest

Compile flagged comments into a digest and send to Slack or email.

Python Implementation

Python
import requests
import json
from pathlib import Path
from datetime import datetime

API_KEY = "your_scavio_api_key"

POSITIVE_WORDS = ["love", "great", "amazing", "helpful", "best", "awesome", "excellent"]
NEGATIVE_WORDS = ["bad", "terrible", "broken", "hate", "worst", "scam", "awful", "disappointed"]

def search_youtube(query: str) -> list[dict]:
    res = requests.post(
        "https://api.scavio.dev/api/v1/search",
        headers={"x-api-key": API_KEY},
        json={"platform": "youtube", "query": query},
        timeout=15,
    )
    res.raise_for_status()
    return res.json().get("organic", [])

def score_sentiment(text: str) -> str:
    lower = text.lower()
    pos = sum(1 for w in POSITIVE_WORDS if w in lower)
    neg = sum(1 for w in NEGATIVE_WORDS if w in lower)
    if neg > pos:
        return "negative"
    if pos > neg:
        return "positive"
    return "neutral"

def extract_comments(videos: list[dict]) -> list[dict]:
    comments = []
    for video in videos:
        for comment in video.get("comments", []):
            sentiment = score_sentiment(comment.get("text", ""))
            comments.append({
                "video_title": video.get("title", ""),
                "author": comment.get("author", ""),
                "text": comment.get("text", ""),
                "likes": comment.get("likes", 0),
                "sentiment": sentiment,
                "video_link": video.get("link", ""),
            })
    return comments

def run():
    config = json.loads(Path("monitor_config.json").read_text())
    keywords = config.get("keywords", ["your brand name"])
    all_comments = []

    for kw in keywords:
        videos = search_youtube(kw)
        comments = extract_comments(videos)
        all_comments.extend(comments)

    # Flag notable comments
    notable = [c for c in all_comments if c["sentiment"] == "negative" or c["likes"] >= 10]
    notable.sort(key=lambda x: x["likes"], reverse=True)

    date = datetime.utcnow().strftime("%Y-%m-%d")
    report = {
        "date": date,
        "total_comments": len(all_comments),
        "positive": sum(1 for c in all_comments if c["sentiment"] == "positive"),
        "negative": sum(1 for c in all_comments if c["sentiment"] == "negative"),
        "neutral": sum(1 for c in all_comments if c["sentiment"] == "neutral"),
        "notable": notable[:20],
    }

    Path(f"yt_comments_{date}.json").write_text(json.dumps(report, indent=2))
    print(f"Collected {len(all_comments)} comments, {len(notable)} notable")
    for c in notable[:5]:
        print(f"  [{c['sentiment']}] {c['text'][:80]}")

if __name__ == "__main__":
    run()

JavaScript Implementation

JavaScript
const API_KEY = "your_scavio_api_key";

const POSITIVE_WORDS = ["love", "great", "amazing", "helpful", "best", "awesome", "excellent"];
const NEGATIVE_WORDS = ["bad", "terrible", "broken", "hate", "worst", "scam", "awful", "disappointed"];

async function searchYouTube(query) {
  const res = await fetch("https://api.scavio.dev/api/v1/search", {
    method: "POST",
    headers: { "x-api-key": API_KEY, "content-type": "application/json" },
    body: JSON.stringify({ platform: "youtube", query }),
  });
  if (!res.ok) throw new Error(`scavio ${res.status}`);
  return (await res.json()).organic ?? [];
}

function scoreSentiment(text) {
  const lower = text.toLowerCase();
  const pos = POSITIVE_WORDS.filter((w) => lower.includes(w)).length;
  const neg = NEGATIVE_WORDS.filter((w) => lower.includes(w)).length;
  if (neg > pos) return "negative";
  if (pos > neg) return "positive";
  return "neutral";
}

function extractComments(videos) {
  const comments = [];
  for (const video of videos) {
    for (const comment of video.comments ?? []) {
      comments.push({
        videoTitle: video.title ?? "",
        author: comment.author ?? "",
        text: comment.text ?? "",
        likes: comment.likes ?? 0,
        sentiment: scoreSentiment(comment.text ?? ""),
        videoLink: video.link ?? "",
      });
    }
  }
  return comments;
}

async function run() {
  const fs = await import("fs/promises");
  const config = JSON.parse(await fs.readFile("monitor_config.json", "utf8"));
  const keywords = config.keywords ?? ["your brand name"];
  const allComments = [];

  for (const kw of keywords) {
    const videos = await searchYouTube(kw);
    allComments.push(...extractComments(videos));
  }

  const notable = allComments
    .filter((c) => c.sentiment === "negative" || c.likes >= 10)
    .sort((a, b) => b.likes - a.likes);

  const date = new Date().toISOString().slice(0, 10);
  const report = {
    date,
    totalComments: allComments.length,
    positive: allComments.filter((c) => c.sentiment === "positive").length,
    negative: allComments.filter((c) => c.sentiment === "negative").length,
    neutral: allComments.filter((c) => c.sentiment === "neutral").length,
    notable: notable.slice(0, 20),
  };

  await fs.writeFile(`yt_comments_${date}.json`, JSON.stringify(report, null, 2));
  console.log(`Collected ${allComments.length} comments, ${notable.length} notable`);
  for (const c of notable.slice(0, 5)) console.log(`  [${c.sentiment}] ${c.text.slice(0, 80)}`);
}

run();

Platforms Used

YouTube

Video search with transcripts and metadata

Frequently Asked Questions

This workflow collects YouTube comments daily for monitored channels and videos, scores them for sentiment, and surfaces notable mentions, complaints, and feature requests. It replaces manual YouTube Studio checks with an automated pipeline that catches important audience feedback within 24 hours.

This workflow uses a cron schedule (daily at 9 am utc). Runs daily at 9 AM UTC.

This workflow uses the following Scavio platforms: youtube. Each platform is called via the same unified API endpoint.

Yes. Scavio's free tier includes 250 credits per month with no credit card required. That is enough to test and validate this workflow before scaling it.

YouTube Comment Monitoring Pipeline

Daily automated YouTube comment collection with sentiment scoring. Monitor brand mentions, competitor channels, and audience feedback.