InsightSocial API
Recipes

Sentiment analysis

Find the conversation about a topic on YouTube and Reddit, pull the comments, and read the sentiment label every comment already carries for free. From 400 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 tally the sentiment of those comments. Every comment list on the API comes back with a free sentiment label on each comment, so there is no model to run.

Cost per run

400 to 1,900 credits. Two searches (YouTube 20–380, Reddit 20–680), three YouTube comment pages (20–100 each) and three Reddit comment trees (100–180 each). The labels add nothing. 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, and each comment carries computed.labels.sentiment: a level from 0 (very negative) to 4 (very positive), the same value as score_0_1 on a 0 to 1 scale, and a confidence. Count the levels 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 topic20–380
GET /v1/reddit/searchFind Reddit threads about the topic20–680
GET /v1/youtube/video/commentsTop-level comments on a video, labelled20–100
GET /v1/reddit/post/commentsA thread's full comment tree, labelled100–180

Every comment list carries four labels by default, at no extra cost: sentiment, question, purchase_intent and complaint. Labels are judged under a short time budget. A comment not finished in time arrives without them and is filled on your next call for the same page. To have every comment judged inside this response, name the labels you want, for example label=sentiment. Asking for a default label by name is still free. See Labels.

The code

Python
# sentiment.py
# Harvests labelled 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

KEY = os.environ["INSIGHTSOCIAL_API_KEY"]
BASE = "https://api.insightsocial.app/v1"
TOPIC = "github copilot pricing"
THREADS_PER_PLATFORM = 3

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

def band(level):
    if level is None:
        return "not_yet"
    return "negative" if level <= 1 else "positive" if level >= 3 else "neutral"

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

videos = [i["post"]["url"] for i in yt.get("data", {}).get("items", [])
          if "watch?v=" in (i.get("post") or {}).get("url", "")][:THREADS_PER_PLATFORM]
threads = [i["post"]["url"] for i in rd.get("data", {}).get("items", [])
           if (i.get("post") or {}).get("url")][:THREADS_PER_PLATFORM]

# Step 2: pull labelled 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], label="sentiment")), jobs))

# Step 3: 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 item in body["data"].get("items", []):
        sentiment = ((item.get("computed") or {}).get("labels") or {}).get("sentiment")
        split[platform][band(sentiment["level"] if sentiment else None)] += 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, 'not_yet': 3}
credits used: 520

The figures are illustrative. data.labels on each comment page reports how many comments were judged, how many are still pending and what the labels cost (extra_credits: 0 for these).

Where the free labels stop

The sentiment label 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. When you need sentiment toward one named thing, either:

  • Hand the harvested comment text to your own model with a prompt that names the topic, or
  • On post lists and post searches, use label=mention&brand=..., which judges whether each post is about the brand and how it feels toward it. This label is metered and adds credits, so check the price first with dry_run=1, which is never charged.

The same comments also carry question, purchase_intent and complaint probabilities, which are often more useful than the split itself: "how many people are asking how to cancel" is a sharper signal than "38% negative".

What to change

  • TOPIC. Any brand, product, feature or announcement.
  • How deep to go. THREADS_PER_PLATFORM sets most of the price. At 3 per platform the floor is 400 credits. At 1 per platform it is 160.
  • Other platforms. Swap in tiktok/search with tiktok/post/comments (20–140), twitter/search/tweets with twitter/tweet/replies (20–100), or instagram/search/reels with instagram/post/comments (100–380). Every comment list carries the same labels.
  • A fixed window. Use timeframe on Reddit and max_age_days on the searches to cover a launch week.

On this page