Sentiment analysis
Build a social sentiment pipeline. Universal search finds the conversation, and comment endpoints pull what real people said with sentiment already labelled, or you label it with your own model. 23-35 credits per run.
You will build a "how do people actually feel about X" pipeline: find the conversation across the social web, harvest the comments where the real sentiment lives, and hand an LLM a clean batch to classify.
Cost per run: 23-35 credits, plus your LLM tokens. Twenty of those are the flat universal-search charge. The rest is the three deep comment fetches, and their price depends on which platforms the top results land on.
How do you analyze social media sentiment with an API?
Two stages: data, then judgment. GET /v1/search/everywhere returns posts from 16 platforms with each post's top comments already attached. Per-platform comment endpoints deepen the sample on the hottest threads. Then a single LLM call labels every comment positive, negative, or neutral.
Sentiment lives in comments, not posts. The post says "we launched a new pricing page"; the comments say what people think about it. Keyword-based scoring ("bad" means negative) falls over on sarcasm immediately, which is why the labelling step is a model, not a dictionary.
The fast path: sentiment is already on the comments
The comment endpoints below label each comment for you, by default and at no extra credit. Read computed.labels.sentiment and skip Step 3 entirely:
const res = await fetch(
"https://www.socialcrawl.dev/v1/youtube/video/comments?url=https://www.youtube.com/watch?v=dQw4w9WgXcQ&label=sentiment",
{ headers: { "x-api-key": process.env.SOCIALCRAWL_KEY! } },
);
const { data } = await res.json();
const bands = { negative: 0, neutral: 0, positive: 0, not_yet: 0 };
for (const item of data.items) {
const s = item.computed?.labels?.sentiment; // { level 0-4, score_0_1, confidence }
if (!s) bands.not_yet++; // null: not judged yet, filled on your next call
else if (s.level <= 1) bands.negative++;
else if (s.level >= 3) bands.positive++;
else bands.neutral++;
}
console.log(bands, data.labels.status); // "complete", "partial" or "skipped"label=sentiment names the label the code reads. Sentiment is one of the default comment labels, so naming it is still free, and a label you name is judged before the page is sent instead of under the short default time budget, so fewer comments come back null.
level runs from 0 (very negative) to 4 (very positive), and the same comments also carry question, purchase_intent and complaint probabilities. A comment whose labels are null was not judged in time; it is filled on your next call for the same page, including a free cache hit. Labels documents every field. The labels judge how the comment feels, not how it feels about your topic in particular, so keep the pipeline below when you need sentiment toward one named thing, your own label set, or comments from the universal search's top_comments, which are not labelled.
What you need
| Endpoint | What it does | Cost |
|---|---|---|
GET /v1/search/everywhere | Finds posts about the topic across 15 platforms, with top_comments pre-attached | 20cr |
GET /v1/tiktok/post/comments | Deeper TikTok comment pages. Param: url | 1cr |
GET /v1/youtube/video/comments | Deeper YouTube comment pages. Param: url | 1cr |
GET /v1/reddit/post/comments | The full Reddit comment tree in one call, nested replies auto-expanded. Param: url | 5cr |
The Reddit leg is the expensive one for a reason: it is not a page of top-level comments but the whole tree with nested replies already expanded, so it replaces a pagination loop you would otherwise write yourself. A run that samples three Reddit threads costs 15 credits in comment fetches alone.
Want an aggregate without running an LLM at all? GET /v1/content_analysis/sentiment (param: keyword, 20 credits) returns a
positive/negative/neutral split plus a 6-axis emotional breakdown (anger,
happiness, love, sadness, share, fun) over the keyword's web mentions in one
call. It does not undercut the pipeline below on price. Pick it when you want
one number and no LLM plumbing, not when you want to save credits.
The code
// recipe-sentiment.ts
// Harvests comments about a topic, builds an LLM-ready classification batch.
// Run with: SOCIALCRAWL_KEY=sc_... npx tsx recipe-sentiment.ts
const KEY = process.env.SOCIALCRAWL_KEY;
if (!KEY) throw new Error("Set SOCIALCRAWL_KEY");
const BASE = "https://www.socialcrawl.dev/v1";
const topic = "github copilot pricing";
async function get(path: string, params: Record<string, string>) {
const url = new URL(`${BASE}/${path}`);
for (const [k, v] of Object.entries(params)) url.searchParams.set(k, v);
const res = await fetch(url, {
headers: { "x-api-key": KEY!, accept: "application/json" },
});
return (await res.json()) as {
success: boolean;
data?: Record<string, any>;
};
}
// ── Step 1: Find the conversation (comments come pre-attached) ────────────
const search = await get("search/everywhere", {
query: topic,
lookback_days: "14",
});
if (!search.success) throw new Error("search failed");
type Sample = { platform: string; post_url: string; text: string };
const samples: Sample[] = [];
for (const item of search.data!.items as Array<{
source: string;
url: string;
source_items: Array<{
metadata?: { top_comments?: Array<{ text: string }> };
}>;
}>) {
for (const c of item.source_items[0]?.metadata?.top_comments ?? []) {
samples.push({ platform: item.source, post_url: item.url, text: c.text });
}
}
// ── Step 2: Deepen the sample on the 3 hottest posts ──────────────────────
const COMMENTS_PATH: Record<string, string> = {
tiktok: "tiktok/post/comments",
youtube: "youtube/video/comments",
reddit: "reddit/post/comments",
};
const hot = (search.data!.items as Array<{ source: string; url: string }>)
.filter((i) => COMMENTS_PATH[i.source])
.slice(0, 3);
for (const post of hot) {
const res = await get(COMMENTS_PATH[post.source]!, { url: post.url });
for (const item of (res.data?.items ?? []) as Array<{
comment?: { text?: string | null };
}>) {
if (item.comment?.text)
samples.push({
platform: post.source,
post_url: post.url,
text: item.comment.text,
});
}
}
console.log(`${samples.length} comments harvested about "${topic}"`);
// ── Step 3: Hand the batch to your LLM of choice ──────────────────────────
const prompt = `Classify the sentiment of each social media comment about
"${topic}" as "positive", "negative", or "neutral". Judge sentiment toward
the topic itself, not general mood. Account for sarcasm. Return a JSON array
of { index, sentiment, confidence } objects.
Comments:
${samples.map((s, i) => `${i}. [${s.platform}] ${s.text.slice(0, 280)}`).join("\n")}`;
// Send `prompt` to any LLM, OpenAI, Anthropic, Gemini, a local model.
// Then aggregate: share positive vs negative per platform, over time, etc.
console.log(prompt.slice(0, 600), "…");What you get back
// `samples` after Step 2, the LLM input batch:
[
{ "platform": "reddit", "post_url": "https://reddit.com/r/programming/...", "text": "Honestly worth every cent, it writes half my tests." },
{ "platform": "youtube", "post_url": "https://www.youtube.com/watch?v=...", "text": "$39/mo for autocomplete, lol no thanks" },
{ "platform": "tiktok", "post_url": "https://www.tiktok.com/@.../video/...", "text": "the new pricing killed it for students fr" },
// ... 80-200 more, depending on how active the topic is
]
// And the LLM's output after Step 3:
[
{ "index": 0, "sentiment": "positive", "confidence": 0.95 },
{ "index": 1, "sentiment": "negative", "confidence": 0.9 },
{ "index": 2, "sentiment": "negative", "confidence": 0.85 },
]What to change
topic: any brand, product, feature, or announcement.- How deep to go:
.slice(0, 3)in Step 2 sets the number of threads you expand, and therefore most of the price. Three TikTok or YouTube posts is 23 credits, a mixed TikTok + YouTube + Reddit sample is 27, three Reddit threads is 35. A topic with fewer than three comment-capable results costs less. - The classifier:
promptis plain text, so any model works. Swap in structured outputs if your provider supports them. Or drop Step 3 for the Step 2 comments and read thecomputed.labels.sentimentthey already carry. - Drop spam before you classify: add
label=spam&exclude=spamto a Step 2 comment call. Comments whose spam probability is 0.8 or higher are removed and their ids listed indata.labels.dropped_ids. Spam is not a default label, so it is metered: 1 credit per started 25 newly judged comments, on top of the page. See Labels. - Go further back: raise
lookback_days, or usefrom_date/to_datefor a fixed window such as a launch week. - Skip the LLM: use
GET /v1/content_analysis/sentimentfor a 20-credit aggregate distribution without per-comment labels.
Related
Brand mention monitoring
The daily feed this pipeline classifies.
Hybrid search-then-enrich
Classify spoken content instead of comments.
Pagination
Walk deeper into any comment thread with cursors.
Which endpoint should I use?
The four comment endpoints, side by side, with prices.
Quickstart
New here? Get a key and make your first request.
