Social media comments API for sentiment analysis

A social media comments API returns public Instagram, TikTok and YouTube comments as JSON, 1 credit per post. Merge them and score sentiment with an LLM.

By Scrape Everything team · · 11 min read

Last updated: October 2026

TL;DR: A social media comments API returns the public comments on a post or video as structured JSON, so you can analyze them without opening the app. With Scrape Everything, one POST request returns a post's top-level comments from Instagram (up to 15), TikTok (up to 100) or YouTube (up to 100) for 1 credit.

Merge the three responses into one table and send the text to an LLM for sentiment and themes. That's the whole pipeline. The catch: each platform returns different fields, orderings and limits, and if you ignore them your numbers go wrong without telling you.

Two different products share this name. Some comments APIs manage comments on accounts you own (reply, hide, delete) through OAuth. A read-only comments API like ours fetches public comments on any post, competitors' included. This guide covers the read-only kind, which is part of our social media scraper catalog.

Which comments API fits your job?

You need toBest fitWhy
Reply to, hide or moderate comments on your own accountsThe platform's official API, or a social media management toolWrite access needs OAuth to your account. A read-only scraper can't do it.
Every comment and reply on a YouTube video, at no costYouTube Data API commentThreads.listFree daily quota, 1 quota unit per call, up to 100 threads per page
TikTok comments and replies for academic researchTikTok Research APIApplication-based and limited to qualifying non-commercial research
Public comments on any post on Instagram, TikTok and YouTube with one keyScrape Everything1 credit per request, same auth, response envelope and error codes everywhere

What does each platform's comments endpoint return?

A comment scraper API is a service that fetches the public comments shown on a social post and returns them as typed fields (text, likes, time, author) instead of HTML. Reference pages: the Instagram comments scraper, the YouTube comments scraper and the TikTok scrapers.

InstagramTikTokYouTube
EndpointPOST /v1/instagram/commentsPOST /v1/tiktok/commentsPOST /v1/youtube/comments
Inputpost or reel url, or shortcodevideo url or idvideo url or id
Max comments per request15 (newest top-level only)100 (default 20)100 (default 20)
OrderNewest firstTikTok's relevance ordertop or newest via sort
Replies includedNoNo (reply_count only)No (reply_count only)
Comment timecreated_at, Unix secondscreated_at, ISO 8601published_text, relative ("2 months ago")
Comment likeslike_countlike_countlike_count parsed from rounded text, plus raw like_count_text ("8.4K")
Language fieldNolanguageNo
Creator signalsNoneis_author_liked, is_pinnedis_pinned, is_hearted, author.is_channel_owner
Total comment countcomment_countcomment_count (as TikTok reports it)comment_count (exact)
Credits per request111

Four things in that table shape the pipeline.

Instagram's cap is hard. Logged-out Instagram only shows the 15 most recent top-level comments of a post, so limit maxes out at 15 and older comments aren't reachable. has_more tells you the post has more than you got.

YouTube gives you no absolute timestamp, just "3 weeks ago", and its comment like_count is the rounded figure parsed back into a number. Read 8,400 as "about 8.4K". TikTok sorts by relevance rather than time and hides some comments, so you can get fewer than comment_count even when you're under your limit.

No endpoint returns replies. If the fight happens in the replies, you get the reply_count, not the fight.

What about the official APIs?

If you only need one platform, look at them first. The YouTube Data API commentThreads.list method returns replies and an ISO 8601 publishedAt per comment, costs 1 quota unit per call and is free within your daily quota. It needs a Google Cloud project and credentials. TikTok's official comment data sits behind the TikTok Research API, which you apply for and which is limited to qualifying non-commercial research. On Instagram, Meta's official API is built around accounts you manage, not other people's posts; we compared the two routes in Instagram Graph API vs a scraper API. Our case is narrower: one key and one schema for three platforms.

How do I collect comments with one request per post?

Every endpoint is a POST with a JSON body and a Bearer key. Here's the YouTube comments scraper API from a terminal:

bash
curl -X POST https://api.scrapeeverything.com/v1/youtube/comments \
  -H "Authorization: Bearer $SCRAPEEVERYTHING_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ", "sort": "newest", "limit": 50}'

A trimmed response (real field names, placeholder values):

json
{
  "success": true,
  "data": {
    "video_id": "dQw4w9WgXcQ",
    "sort": "newest",
    "comment_count": 2400000,
    "comments_fetched": 50,
    "has_more": true,
    "comments": [
      {
        "id": "UgxEXAMPLE0000000000",
        "link": "https://www.youtube.com/watch?v=dQw4w9WgXcQ&lc=UgxEXAMPLE0000000000",
        "text": "Bought the v2 after this. Battery is better, the app still logs me out.",
        "author": {
          "name": "@example_user",
          "handle": "@example_user",
          "is_channel_owner": false,
          "is_verified": false
        },
        "like_count": 8400,
        "like_count_text": "8.4K",
        "reply_count": 12,
        "published_text": "2 days ago",
        "is_pinned": false,
        "is_hearted": false
      }
    ]
  },
  "meta": {
    "request_id": "req_example",
    "cached": false,
    "credits_charged": 1,
    "credits_remaining": 99
  }
}

Errors return success: false, a stable code and a message. Expect COMMENTS_DISABLED (404) on TikTok and YouTube videos with comments switched off, PRIVATE_ACCOUNT or AGE_RESTRICTED (404) on Instagram posts that exist but aren't public, and NOT_FOUND. Per the errors reference, a 404 is final for that target, so a retry is charged again for the same answer.

Two TikTok gotchas. Short share links (vm.tiktok.com/..., tiktok.com/t/...) aren't supported, so expand them to the full tiktok.com/@user/video/<id> URL first. And send id as a string, never a number: JavaScript rounds a 19-digit integer, and you'll quietly ask for the wrong video.

How do I normalize three platforms into one table?

This step is what makes cross-platform sentiment analysis of social media comments possible. Pick one schema and force everything into it. Ours:

ColumnInstagram sourceTikTok sourceYouTube source
platformconstantconstantconstant
post_idshortcodevideo_idvideo_id
comment_idcomments[].idcomments[].idcomments[].id
texttext (empty for GIF-only)texttext
likeslike_countlike_countlike_count
likes_exacttruetruefalse
repliesreply_count (often null)reply_countreply_count
postedcreated_at to ISOcreated_atpublished_text kept as text
creator_engagedfalseis_author_liked or is_pinnedis_hearted or is_pinned
author_hashsha256 of author.usernamesha256 of author.usernamesha256 of author.handle

We hash handles on purpose. You need to know that two comments came from the same person (one angry user posting six times isn't six angry users), not who that person is.

Here's the collector in Python. It runs with pip install requests and your key in SCRAPEEVERYTHING_API_KEY, and it appends to comments.csv so you can rerun it without losing or duplicating rows:

python
import csv
import hashlib
import os
from datetime import datetime, timezone

import requests

API = "https://api.scrapeeverything.com/v1"
HEADERS = {"Authorization": f"Bearer {os.environ['SCRAPEEVERYTHING_API_KEY']}"}
OUT = "comments.csv"
FIELDS = ["platform", "post_id", "comment_id", "text", "likes", "likes_exact",
          "replies", "posted", "creator_engaged", "author_hash"]

POSTS = [
    ("instagram", {"url": "https://www.instagram.com/reel/DYxz0A8Kinw/", "limit": 15}),
    ("tiktok", {"url": "https://www.tiktok.com/@khaby.lame/video/7694335322922044703", "limit": 100}),
    ("youtube", {"url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ", "sort": "newest", "limit": 100}),
]


def fetch(platform, body):
    r = requests.post(f"{API}/{platform}/comments", headers=HEADERS, json=body, timeout=90)
    try:
        j = r.json()
    except ValueError:
        print(f"skip {platform}: HTTP {r.status_code}")
        return None
    if not j.get("success"):
        print(f"skip {platform}: {j.get('code')} {j.get('message')}")
        return None
    return j["data"]


def h(value):
    return hashlib.sha256(value.encode()).hexdigest()[:16] if value else None


def normalize(platform, data):
    for c in data["comments"]:
        a = c.get("author") or {}
        if platform == "instagram":
            ts = c.get("created_at")
            posted = datetime.fromtimestamp(ts, timezone.utc).isoformat() if ts else None
            post_id, handle, engaged, exact = data["shortcode"], a.get("username"), False, True
        elif platform == "tiktok":
            posted = c.get("created_at")
            post_id, handle, exact = data["video_id"], a.get("username"), True
            engaged = c.get("is_author_liked") or c.get("is_pinned")
        else:
            posted = c.get("published_text")
            post_id, handle, exact = data["video_id"], a.get("handle"), False
            engaged = c.get("is_hearted") or c.get("is_pinned")
        yield {
            "platform": platform,
            "post_id": post_id,
            "comment_id": c["id"],
            "text": c.get("text") or "",
            "likes": c.get("like_count"),
            "likes_exact": exact,
            "replies": c.get("reply_count"),
            "posted": posted,
            "creator_engaged": bool(engaged),
            "author_hash": h(handle),
        }


# Load keys from earlier runs so polling only adds new comments.
seen = set()
is_new_file = not os.path.exists(OUT)
if not is_new_file:
    with open(OUT, newline="", encoding="utf-8") as f:
        seen = {(r["platform"], r["comment_id"]) for r in csv.DictReader(f)}

rows = []
for platform, body in POSTS:
    data = fetch(platform, body)
    if not data:
        continue
    for row in normalize(platform, data):
        key = (row["platform"], row["comment_id"])
        if row["text"].strip() and key not in seen:
            seen.add(key)
            rows.append(row)

with open(OUT, "a", newline="", encoding="utf-8") as f:
    w = csv.DictWriter(f, fieldnames=FIELDS)
    if is_new_file:
        w.writeheader()
    w.writerows(rows)

print(f"{len(rows)} new comments from {len(POSTS)} posts")

It drops empty text rows on purpose: Instagram GIF-only comments come back as an empty string plus a gif_url, and a model will happily invent a sentiment for nothing.

How do I get more than 15 Instagram comments?

You can't reach older ones, but you can poll. On launch day, rerun the collector for the same post every couple of hours; it keeps only new comment_ids. If all 15 comments in a poll are new, you probably missed some, so halve the interval. Don't set cache_days on polling calls, or you'll get the stored copy back instead of fresh comments.

Should I fetch the post metadata too?

Yes, once per post. POST /v1/youtube/video returns the title, exact view count and like count, POST /v1/tiktok/video returns the caption plus play, like, comment, share and save counts, and POST /v1/instagram/post returns the caption, like count and comment count. Each costs 1 credit. Put the title or caption into the LLM prompt, because "it finally works" means one thing under a bug-fix announcement and another under a teaser.

How do I run sentiment analysis on social media comments with an LLM?

You have three realistic options, and they trade cost for nuance:

ApproachExampleGood atWeak at
Lexicon and rulesVADERFree, fast, tuned for social media textSarcasm, slang it hasn't seen, non-English text
Fine-tuned transformercardiffnlp/twitter-roberta-base-sentiment-latest on Hugging FaceRuns locally, positive/neutral/negative labelsNo themes, no question or objection types
General-purpose LLMAny current chat model with JSON outputSentiment, theme and type in one pass, uses post contextCosts per token, needs a fixed label set

We use an LLM for launch posts because the useful output is the themes: who's unhappy, and about what. "This is sick" is praise on TikTok and a complaint in a support ticket, and you need context to tell.

Batch 50 to 100 comments per call, ask for JSON and pass the post context. The prompt we start from:

text
You are analyzing public comments on a product launch post.
Post context: {platform} | "{title_or_caption}"

For each comment, return:
- id: the comment_id
- sentiment: positive | negative | neutral | mixed
- theme: 2-4 word label (e.g. "price too high", "battery life", "shipping delay")
- type: praise | complaint | question | objection | spam | other
- language: ISO 639-1 code (only TikTok returns one natively)

Rules:
- Judge sarcasm by context. "Great, another subscription" is negative.
- Emoji-only comments: label by the emoji, type "other".
- If you can't tell, use neutral. Don't guess.

Return a JSON array only.

Comments:
{id}\t{text}
...

Then run a smaller call over the theme labels alone: "Merge these into at most 8 clusters and return a mapping." That turns "too pricey", "price is high" and "$$$" into one bar on a chart.

Before aggregating, drop everything typed spam. Likes act as agreement votes, so weighting by likes makes sense on TikTok and Instagram; on YouTube likes_exact is false, so rank by likes but don't do arithmetic with them. Compare platforms by share of comments, never raw counts: 15 Instagram comments against 100 YouTube comments only tells you about the sampling caps.

Can I do this without writing code?

Yes. The comment-sentiment-mining skill in our Agent Skills pack runs the whole loop from a prompt: it fetches the comments, then clusters them into sentiment, themes, questions and objections. Install the pack with:

bash
npx skills add scrapeeverything/skills

Then ask your agent "What are people saying in the comments of these 5 launch videos?" and paste the links. Same endpoints, same limits.

If your agent speaks the Model Context Protocol (MCP), our MCP server (@scrapeeverything/mcp, listed in the official MCP Registry) exposes the endpoints as tools. n8n users can build the same flow with the n8n-nodes-scrapeeverything node. Full request and response schemas live in our OpenAPI spec at https://scrapeeverything.com/openapi.json.

What does a social media comments API cost per post?

Each comments request costs 1 credit on every platform. Failed requests are refunded automatically. A definitive answer, such as a private or missing post, is billed because the lookup completed. Cache hits are free: send cache_days (1 to 14) and a repeat request for the same target inside that window returns the stored result for 0 credits. Per the caching docs, the match is on the endpoint plus its normalized parameters, so keep limit and sort the same.

Cost per 1,000 comments on each pack, best case with full pages (a video with 40 comments still costs 1 credit):

PlatformCredits per 1,000 commentsBuilder ($1.56 / 1k credits)Growth ($1.19)Scale ($0.90)
YouTube, TikTok (100 per request)10$0.016$0.012$0.009
Instagram (15 per request)67$0.10$0.08$0.06

A launch budget, 10 posts per platform:

TaskRequestsCredits
Comments, 30 posts, one sweep3030
Post metadata, 30 posts3030
Instagram polling, 10 posts, every 2 hours for 24 hours120120

That's 180 credits, and a rerun inside a cache_days window costs 0. The 100 free credits on signup cover the first sweep plus metadata. After that, credits come in one-time packs that never expire: Builder $39 for 25,000 credits, Growth $119 for 100,000 and Scale $449 for 500,000. The pricing page has the details, the credit calculator turns a post count into a pack, and our social media scraping API pricing comparison puts those numbers next to other providers.

Start with one real launch post per platform. Get 100 free credits, run the collector and read the CSV before writing any LLM code. The LLM bill is separate and depends on your model.

What are the limits and ethics of scraping comments?

Scrape Everything returns public data only: no logins, no private accounts. We're not affiliated with Instagram, TikTok or YouTube.

Public isn't anonymous, though. Comment authors are people, and under the EU General Data Protection Regulation personal data is any information relating to an identifiable person, which a username often is. What we recommend:

  1. Collect only the fields you need (for sentiment: text, likes, time and a hashed author), and hash or drop handles and avatar URLs before the data leaves your collector.
  2. Report aggregates, like the share of comments that mention price, not quotes tied to a person.
  3. Set a retention period and delete raw comments when the analysis is done.
  4. Read the platform's terms and talk to your counsel before any commercial use.

We cover the legal background (not legal advice) in Is scraping public social media data legal?. See also the researchers use case for reproducible datasets and analytics SaaS for building comment analytics into a product.

FAQ

Is there a single social media comments API for Instagram, TikTok and YouTube?

Yes. Scrape Everything has one comments endpoint per platform (/v1/instagram/comments, /v1/tiktok/comments, /v1/youtube/comments), all with the same auth, the same response envelope and the same error codes. Each costs 1 credit per request.

Do the comments APIs return replies or older comments?

No. All three return top-level comments only, with a reply_count per comment, and Instagram stops at the 15 newest. For replies, use the official YouTube Data API or, if you qualify, the TikTok Research API.

Is there a free comments API?

The YouTube Data API is free within its daily quota, and the TikTok Research API is free for approved non-commercial researchers. Scrape Everything gives you 100 free credits on signup with no payment details, which at 1 credit per comments request covers 100 posts.

Can I get Facebook or X comments?

Not today. Our catalog has comments endpoints for Instagram, TikTok and YouTube only. For X we return profiles and posts, and for Facebook public page profiles, but no comment threads.

What happens if comments are turned off?

TikTok and YouTube return a 404 with the code COMMENTS_DISABLED. A video that simply has no comments returns an empty list. Treat both as final and don't retry.

More from the blog

Start with 100 free credits.

Continue with Google, create an API key and send your first request in under a minute.

Get 100 free credits