Tutorial

How to Build a Reddit Sentiment Tracking Agent for Brand Keywords

Build an agent that tracks Reddit sentiment for brand keywords. Fetch posts via Scavio, classify sentiment, and generate daily sentiment reports.

Reddit discussions contain unfiltered opinions about products, brands, and industries that surveys and review sites miss. Tracking sentiment across subreddits manually is impractical -- threads move fast and span hundreds of communities. This tutorial builds a Python agent that uses the Scavio API to search Reddit for brand-related posts, applies a simple keyword-based sentiment classifier, and aggregates the results into a daily report with overall sentiment score, top positive threads, top negative threads, and trending concerns. The approach uses no ML models -- just regex-weighted keyword scoring that runs instantly and costs nothing beyond the API calls.

Prerequisites

  • Python 3.8 or higher installed
  • requests library installed
  • A Scavio API key from scavio.dev
  • Brand keywords or product names to track

Walkthrough

Step 1: Define brand keywords and sentiment lexicon

Set up the brand terms to search for and a simple weighted keyword lexicon for sentiment classification. Positive and negative words each carry a score.

Python
BRAND_QUERIES = [
    "scavio api",
    "scavio search",
    "scavio mcp"
]

POSITIVE_WORDS = ["love", "great", "fast", "reliable", "cheap", "easy", "best", "solid", "recommend", "impressed"]
NEGATIVE_WORDS = ["slow", "expensive", "broken", "hate", "worst", "terrible", "buggy", "unreliable", "scam", "overpriced"]

Step 2: Fetch Reddit posts via the Scavio API

Search Reddit through the Scavio endpoint using the platform parameter. Collect post title, snippet, link, and subreddit for each result.

Python
import os
import requests

API_KEY = os.environ.get("SCAVIO_API_KEY", "your_scavio_api_key")

def search_reddit(query: str) -> list[dict]:
    r = requests.post(
        "https://api.scavio.dev/api/v1/search",
        headers={"x-api-key": API_KEY},
        json={"query": query, "platform": "reddit"}
    )
    r.raise_for_status()
    return r.json().get("organic_results", [])

Step 3: Score sentiment for each post

Apply the keyword lexicon to each post title and snippet. Sum positive and negative hits to produce a net sentiment score per post.

Python
def score_sentiment(text: str) -> dict:
    text_lower = text.lower()
    pos = sum(1 for w in POSITIVE_WORDS if w in text_lower)
    neg = sum(1 for w in NEGATIVE_WORDS if w in text_lower)
    net = pos - neg
    if net > 0:
        label = "positive"
    elif net < 0:
        label = "negative"
    else:
        label = "neutral"
    return {"positive": pos, "negative": neg, "net": net, "label": label}

def analyze_post(post: dict) -> dict:
    text = f"{post.get('title', '')} {post.get('snippet', '')}"
    sentiment = score_sentiment(text)
    return {
        "title": post.get("title", ""),
        "link": post.get("link", ""),
        "subreddit": post.get("source", ""),
        "sentiment": sentiment
    }

Step 4: Generate the daily sentiment report

Aggregate sentiment across all posts, identify top positive and negative threads, and write a summary report.

Python
import json
from datetime import date

def generate_report(brand_queries: list[str]) -> dict:
    all_posts = []
    for query in brand_queries:
        results = search_reddit(query)
        analyzed = [analyze_post(p) for p in results]
        all_posts.extend(analyzed)
    total = len(all_posts)
    positive = [p for p in all_posts if p["sentiment"]["label"] == "positive"]
    negative = [p for p in all_posts if p["sentiment"]["label"] == "negative"]
    neutral = [p for p in all_posts if p["sentiment"]["label"] == "neutral"]
    report = {
        "date": str(date.today()),
        "total_posts": total,
        "positive": len(positive),
        "negative": len(negative),
        "neutral": len(neutral),
        "sentiment_ratio": round(len(positive) / total, 2) if total else 0,
        "top_positive": sorted(positive, key=lambda p: p["sentiment"]["net"], reverse=True)[:3],
        "top_negative": sorted(negative, key=lambda p: p["sentiment"]["net"])[:3]
    }
    with open(f"reddit_sentiment_{report['date']}.json", "w") as f:
        json.dump(report, f, indent=2)
    print(f"Report: {total} posts, {len(positive)} positive, {len(negative)} negative")
    return report

generate_report(BRAND_QUERIES)

Python Example

Python
import os
import json
import requests
from datetime import date

API_KEY = os.environ.get("SCAVIO_API_KEY", "your_scavio_api_key")
ENDPOINT = "https://api.scavio.dev/api/v1/search"

POSITIVE = ["love", "great", "fast", "reliable", "cheap", "easy", "best", "solid", "recommend", "impressed"]
NEGATIVE = ["slow", "expensive", "broken", "hate", "worst", "terrible", "buggy", "unreliable", "scam", "overpriced"]

def search_reddit(query: str) -> list[dict]:
    r = requests.post(
        ENDPOINT,
        headers={"x-api-key": API_KEY},
        json={"query": query, "platform": "reddit"}
    )
    r.raise_for_status()
    return r.json().get("organic_results", [])

def score(text: str) -> dict:
    t = text.lower()
    pos = sum(1 for w in POSITIVE if w in t)
    neg = sum(1 for w in NEGATIVE if w in t)
    net = pos - neg
    label = "positive" if net > 0 else "negative" if net < 0 else "neutral"
    return {"pos": pos, "neg": neg, "net": net, "label": label}

def track_sentiment(queries: list[str]) -> dict:
    posts = []
    for q in queries:
        for result in search_reddit(q):
            text = f"{result.get('title', '')} {result.get('snippet', '')}"
            posts.append({
                "title": result.get("title", ""),
                "link": result.get("link", ""),
                "sentiment": score(text)
            })
    total = len(posts)
    pos_count = sum(1 for p in posts if p["sentiment"]["label"] == "positive")
    neg_count = sum(1 for p in posts if p["sentiment"]["label"] == "negative")
    report = {
        "date": str(date.today()),
        "total": total,
        "positive": pos_count,
        "negative": neg_count,
        "neutral": total - pos_count - neg_count,
        "ratio": round(pos_count / total, 2) if total else 0,
        "top_positive": sorted([p for p in posts if p["sentiment"]["label"] == "positive"],
                               key=lambda x: x["sentiment"]["net"], reverse=True)[:3],
        "top_negative": sorted([p for p in posts if p["sentiment"]["label"] == "negative"],
                               key=lambda x: x["sentiment"]["net"])[:3]
    }
    output = f"reddit_sentiment_{report['date']}.json"
    with open(output, "w") as f:
        json.dump(report, f, indent=2)
    print(f"{total} posts: {pos_count} positive, {neg_count} negative")
    return report

if __name__ == "__main__":
    track_sentiment(["scavio api", "scavio search"])

JavaScript Example

JavaScript
const fs = require("fs");
const API_KEY = process.env.SCAVIO_API_KEY || "your_scavio_api_key";
const ENDPOINT = "https://api.scavio.dev/api/v1/search";

const POSITIVE = ["love", "great", "fast", "reliable", "cheap", "easy", "best", "solid", "recommend"];
const NEGATIVE = ["slow", "expensive", "broken", "hate", "worst", "terrible", "buggy", "unreliable"];

async function searchReddit(query) {
  const res = await fetch(ENDPOINT, {
    method: "POST",
    headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
    body: JSON.stringify({ query, platform: "reddit" })
  });
  const data = await res.json();
  return data.organic_results || [];
}

function score(text) {
  const t = text.toLowerCase();
  const pos = POSITIVE.filter(w => t.includes(w)).length;
  const neg = NEGATIVE.filter(w => t.includes(w)).length;
  const net = pos - neg;
  return { pos, neg, net, label: net > 0 ? "positive" : net < 0 ? "negative" : "neutral" };
}

async function trackSentiment(queries) {
  const posts = [];
  for (const q of queries) {
    const results = await searchReddit(q);
    for (const r of results) {
      const text = `${r.title || ""} ${r.snippet || ""}`;
      posts.push({ title: r.title, link: r.link, sentiment: score(text) });
    }
  }
  const pos = posts.filter(p => p.sentiment.label === "positive").length;
  const neg = posts.filter(p => p.sentiment.label === "negative").length;
  console.log(`${posts.length} posts: ${pos} positive, ${neg} negative`);
  const today = new Date().toISOString().split("T")[0];
  fs.writeFileSync(`reddit_sentiment_${today}.json`, JSON.stringify({ date: today, total: posts.length, positive: pos, negative: neg }, null, 2));
}

trackSentiment(["scavio api", "scavio search"]).catch(console.error);

Expected Output

JSON
{
  "date": "2026-05-17",
  "total": 24,
  "positive": 9,
  "negative": 4,
  "neutral": 11,
  "ratio": 0.38,
  "top_positive": [
    {
      "title": "Scavio is the best cheap alternative to SerpApi",
      "link": "https://reddit.com/r/webdev/comments/abc123",
      "sentiment": { "pos": 2, "neg": 0, "net": 2, "label": "positive" }
    }
  ],
  "top_negative": [
    {
      "title": "Any search APIs that are not slow and overpriced?",
      "link": "https://reddit.com/r/artificial/comments/def456",
      "sentiment": { "pos": 0, "neg": 2, "net": -2, "label": "negative" }
    }
  ]
}

Related Tutorials

Frequently Asked Questions

Most developers complete this tutorial in 15 to 30 minutes. You will need a Scavio API key (free tier works) and a working Python or JavaScript environment.

Python 3.8 or higher installed. requests library installed. A Scavio API key from scavio.dev. Brand keywords or product names to track. A Scavio API key gives you 250 free credits per month.

Yes. The free tier includes 250 credits per month, which is more than enough to complete this tutorial and prototype a working solution.

Scavio has a native LangChain package (langchain-scavio), an MCP server, and a plain REST API that works with any HTTP client. This tutorial uses the raw REST API, but you can adapt to your framework of choice.

Start Building

Build an agent that tracks Reddit sentiment for brand keywords. Fetch posts via Scavio, classify sentiment, and generate daily sentiment reports.