InsightSocial API
Recipes

Sentiment analysis

Find the conversation about a topic on YouTube and Reddit, pull the comments, and score each comment's sentiment with a model you run. From 200 credits per run.

This recipe measures public mood around a topic in three steps: locate the posts where it is discussed, collect the comments on the most active ones, and score the sentiment of those comments. The API returns the comments; the scoring runs on your side, so you choose the model and can change it without changing the data.

Cost per run

200 to 630 credits. Two searches (YouTube 10–150, Reddit 10–300), three YouTube comment pages (10 each) and three Reddit comment trees (50 each). Scoring the comments costs nothing on the API side. The Reddit leg costs the most because each call returns a whole discussion with nested replies expanded, not one page.

How do you analyze social media sentiment with an API?

Two stages. Search finds the posts where the topic is being discussed. The comment endpoints then return what people said under them, one comment per item with its text in comment.text. Score each text with a sentiment model and count the results, and you have a sentiment split.

Sentiment lives in comments, not in posts. The post says "we changed our pricing". The comments say what people think of it.

What you need

EndpointRoleCredits
GET /v1/youtube/searchFind videos about the topic10–150
GET /v1/reddit/searchFind Reddit threads about the topic10–300
GET /v1/youtube/video/commentsTop-level comments on a video10
GET /v1/reddit/post/commentsA thread's full comment tree, replies nested in comment.replies50

The code uses VADER in Python (pip install vaderSentiment requests) and the sentiment package in Node.js (npm install sentiment). Both are small English word lists that run offline. Swap in any model you prefer: a multilingual transformer, or a language model with a prompt that names your topic.

The code

Python
# sentiment.py
# Collects comments about a topic and counts the sentiment split.
# Run with: INSIGHTSOCIAL_API_KEY=isk_live_... python sentiment.py
import os
from collections import Counter
from concurrent.futures import ThreadPoolExecutor

import requests
from vaderSentiment.vaderSentiment import SentimentIntensityAnalyzer

KEY = os.environ["INSIGHTSOCIAL_API_KEY"]
BASE = "https://api.insightsocial.app/v1"
TOPIC = "github copilot pricing"
KEYWORDS = ["copilot"]  # a result must mention one of these to count as on-topic
THREADS_PER_PLATFORM = 3

analyzer = SentimentIntensityAnalyzer()

def get(path, **params):
    res = requests.get(f"{BASE}/{path}", params=params,
                       headers={"x-api-key": KEY}, timeout=120)
    return res.json()

def on_topic(post):
    text = (post["content"]["text"] or "").lower()
    return any(k in text for k in KEYWORDS)

def band(text):
    # VADER's usual cut-offs on the compound score, from -1 to 1.
    score = analyzer.polarity_scores(text)["compound"]
    return "negative" if score <= -0.05 else "positive" if score >= 0.05 else "neutral"

def flatten_thread(comments):
    # Reddit nests replies inside each comment; other platforms leave `replies` null.
    for c in comments:
        yield c
        yield from flatten_thread(c["replies"] or [])

# Step 1: find the conversation on both platforms, keeping on-topic results.
yt = get("youtube/search", query=TOPIC)
rd = get("reddit/search", query=TOPIC, timeframe="month")

def top_urls(body, keep=lambda url: True):
    if not body["success"]:
        return []
    posts = [i["post"] for i in body["data"]["items"]]
    return [p["url"] for p in posts if p["url"] and keep(p["url"]) and on_topic(p)][:THREADS_PER_PLATFORM]

videos = top_urls(yt, keep=lambda u: "watch?v=" in u)
threads = top_urls(rd)

# Step 2: pull the comments under the top results.
jobs = [("youtube/video/comments", u) for u in videos] + \
       [("reddit/post/comments", u) for u in threads]

with ThreadPoolExecutor(max_workers=6) as pool:
    pages = list(pool.map(lambda j: (j, get(j[0], url=j[1])), jobs))

# Step 3: score every comment and count the split per platform.
split = {"youtube": Counter(), "reddit": Counter()}
spent = yt.get("credits_used", 0) + rd.get("credits_used", 0)
for (path, url), body in pages:
    spent += body.get("credits_used", 0)
    if not body["success"]:
        continue
    platform = path.split("/")[0]
    for c in flatten_thread(item["comment"] for item in body["data"]["items"]):
        # The scorer is English-only. `language` is often null; keep those.
        if not c["text"] or c["language"] not in (None, "en"):
            split[platform]["skipped"] += 1
            continue
        split[platform][band(c["text"])] += 1

for platform, counts in split.items():
    print(platform, dict(counts))
print(f"credits used: {spent}")

What you get back

Example output
youtube {'negative': 212, 'neutral': 96, 'positive': 71}
reddit {'negative': 88, 'neutral': 54, 'positive': 40, 'skipped': 3}
credits used: 260

The figures are illustrative. skipped counts comments with no text, or tagged with a language other than English.

Where a word list stops

A word-list scorer judges how a comment feels, not how it feels about your topic in particular. A comment that is angry about a bug in a competing product still reads as negative, and sarcasm reads as praise. When you need sentiment toward one named thing, hand the comment text to a model you run with a prompt that names the topic.

The same comments are worth reading for more than polarity. Counting comments that ask a question (a ?, or "how do I") or mention cancelling is often a sharper signal than "38% negative".

What to change

  • TOPIC and KEYWORDS. Any brand, product, feature or announcement. KEYWORDS drops search results that only share a word with the topic.
  • How deep to go. THREADS_PER_PLATFORM sets most of the price. At 3 per platform the floor is 200 credits. At 1 per platform it is 80.
  • Other platforms. Swap in tiktok/search with tiktok/post/comments (10–30), twitter/search/tweets with twitter/tweet/replies (10), or instagram/search/reels with instagram/post/comments (50–150). Every comment list returns the same comment shape.
  • Other languages. Comments carry comment.language when the platform states it. Route each language to a model that reads it, instead of skipping it.
  • A fixed window. Use timeframe on Reddit and max_age_days on the searches to cover a launch week.

On this page