Back to Blog
Tutorial

How to Scrape Instagram Comments (Text, Usernames & Timestamps)

February 8, 2026
8 min read
S
By SociaVault Team
InstagramCommentsScrapingAPISentiment Analysis

How to Scrape Instagram Comments (Text, Usernames & Timestamps)

If you want to know what people actually think about a post, the comments are where it lives. Customer questions, complaints, praise, objections, and the occasional viral thread — all sitting under the post, unstructured and impossible to read at scale by hand.

Copying comments one by one doesn't work past the first few. This guide shows you how to scrape Instagram comments programmatically: the exact endpoint, the real response shape, how pagination works, and a few things the data honestly can't tell you.

What you can (and can't) get

From any public post or reel, each comment gives you:

FieldWhat it is
textThe comment content, including emoji
user.usernameThe handle that wrote it
created_atISO 8601 timestamp of when it was posted
user.is_verifiedWhether the commenter is verified
user.id / user.pkThe commenter's numeric user ID
user.profile_pic_urlThe commenter's avatar URL

What you can't get, so you don't build on false assumptions:

  • Comments on private accounts. If the post isn't publicly visible, it isn't reachable. This is a hard limit, not a setting.
  • Guaranteed per-comment like counts or structured reply threads. Instagram exposes these inconsistently; treat them as optional and read them defensively rather than assuming they're always present.
  • Deleted or hidden comments. You only see what's live and public at request time.

The endpoint

One GET request returns a page of comments:

GET https://api.sociavault.com/v1/scrape/instagram/comments?url=<post-or-reel-url>
ParameterRequiredDescription
urlYesFull Instagram post or reel URL
cursorNoPagination cursor from the previous response's data.cursor

It costs 1 credit per request (flat), regardless of how many comments come back in that page. To go deeper on a post with thousands of comments, you page through with cursor — each page is another 1-credit request.

Real response shape

SociaVault wraps every response in the same envelope: success, data, credits_used, and endpoint. The comments live in data.comments:

{
  "success": true,
  "data": {
    "num_comments_grabbed": 343,
    "comments": [
      {
        "id": "17916526530048829",
        "text": "This ingredient list is actually clean, impressed",
        "created_at": "2025-01-17T14:07:40.000Z",
        "user": {
          "username": "hearts4_therealest",
          "is_verified": false,
          "id": "64101106104",
          "pk": "64101106104",
          "profile_pic_url": "https://scontent.cdninstagram.com/v/..."
        }
      }
    ],
    "cursor": "eyJjYWNoZWQi..."
  },
  "credits_used": 1,
  "endpoint": "instagram/comments"
}

Two things worth internalizing: the payload is under data (not the top level), and data.cursor is what you pass back in to fetch the next page. When there are no more comments, cursor comes back empty or missing.

How to scrape Instagram comments in JavaScript

async function getComments(postUrl, cursor) {
  const params = new URLSearchParams({ url: postUrl });
  if (cursor) params.set("cursor", cursor);

  const res = await fetch(
    `https://api.sociavault.com/v1/scrape/instagram/comments?${params}`,
    { headers: { "x-api-key": "YOUR_API_KEY" } },
  );
  return res.json();
}

const first = await getComments("https://www.instagram.com/p/ABC123/");
for (const c of first.data.comments) {
  console.log(`@${c.user.username}: ${c.text}`);
}

Paginate through every comment

Loop until there's no cursor left. Cap the page count so a runaway loop can't quietly burn credits:

async function getAllComments(postUrl, maxPages = 20) {
  const all = [];
  let cursor;

  for (let page = 0; page < maxPages; page++) {
    const { data } = await getComments(postUrl, cursor);
    if (!data?.comments?.length) break;

    all.push(...data.comments);
    cursor = data.cursor;
    if (!cursor) break;

    await new Promise((r) => setTimeout(r, 300)); // be polite between calls
  }

  return all;
}

How to scrape Instagram comments in Python

import requests

API_KEY = "YOUR_API_KEY"
BASE = "https://api.sociavault.com/v1/scrape/instagram/comments"

def get_comments(post_url, cursor=None):
    params = {"url": post_url}
    if cursor:
        params["cursor"] = cursor
    res = requests.get(BASE, params=params, headers={"x-api-key": API_KEY})
    return res.json()

def get_all_comments(post_url, max_pages=20):
    comments, cursor = [], None
    for _ in range(max_pages):
        payload = get_comments(post_url, cursor).get("data", {})
        batch = payload.get("comments", [])
        if not batch:
            break
        comments.extend(batch)
        cursor = payload.get("cursor")
        if not cursor:
            break
    return comments

for c in get_all_comments("https://www.instagram.com/p/ABC123/"):
    print(f"@{c['user']['username']}: {c['text']}")

What people actually do with comment data

Sentiment analysis

The most common use: figure out whether a post landed well. Pull a few hundred comments and score them.

from textblob import TextBlob

def sentiment_breakdown(comments):
    tally = {"positive": 0, "negative": 0, "neutral": 0}
    for c in comments:
        polarity = TextBlob(c["text"]).sentiment.polarity
        if polarity > 0.1:
            tally["positive"] += 1
        elif polarity < -0.1:
            tally["negative"] += 1
        else:
            tally["neutral"] += 1
    return tally

comments = get_all_comments("https://www.instagram.com/p/ABC123/")
print(sentiment_breakdown(comments))
# {'positive': 210, 'negative': 38, 'neutral': 95}

A caveat worth stating: off-the-shelf sentiment models struggle with sarcasm, slang, and emoji-only comments. Treat the breakdown as a directional signal, not a verdict.

Pull the questions people are asking

Comment questions are free FAQ and content-idea fuel:

function extractQuestions(comments) {
  const starters = ["how", "what", "where", "when", "why", "can", "does", "is"];
  return comments.filter((c) => {
    const t = c.text.toLowerCase().trim();
    return t.includes("?") || starters.some((s) => t.startsWith(s + " "));
  });
}

Spot low-effort or bot-like comments

You can't see a commenter's follower count from this endpoint, so don't build detection on data you don't have. What you can flag cheaply: generic one-liners and emoji-only comments, which cluster on posts with inflated engagement.

function flagLowEffort(comments) {
  const generic = ["nice", "great post", "love this", "amazing", "wow", "cool"];
  const emojiOnly = /^[\p{Emoji}\s]+$/u;

  const flagged = comments.filter((c) => {
    const t = c.text.toLowerCase().trim();
    return generic.includes(t.replace(/[!.]/g, "")) || emojiOnly.test(c.text);
  });

  return {
    total: comments.length,
    flagged: flagged.length,
    rate: ((flagged.length / comments.length) * 100).toFixed(1) + "%",
  };
}

This is a heuristic, not proof of bots — plenty of real people leave "🔥🔥🔥." Use it to compare posts, not to accuse accounts.

Export to CSV

function toCSV(comments) {
  const header = ["username", "text", "created_at", "verified"];
  const rows = comments.map((c) => [
    c.user.username,
    `"${c.text.replace(/"/g, '""')}"`,
    c.created_at,
    c.user.is_verified ?? false,
  ]);
  return [header.join(","), ...rows.map((r) => r.join(","))].join("\n");
}

Tips for cleaner results

  1. Grab enough for the job. For sentiment, 200+ comments smooths out noise. A dozen tells you little.
  2. Cap your pagination. Always set a page limit so a viral post with 40,000 comments doesn't page forever.
  3. Store the timestamps. created_at lets you track how sentiment shifts as a post ages or gets picked up.
  4. Read optional fields defensively. If you rely on a field that isn't in the sample above, guard it with a fallback and log the raw response once to confirm it's there for your posts.

Getting started

  1. Sign up and grab 50 free credits — no card.
  2. Copy your API key from the dashboard.
  3. Send your first request to /v1/scrape/instagram/comments with any public post URL.

Frequently Asked Questions

How do I scrape comments from an Instagram post?

Send a GET request to /v1/scrape/instagram/comments with the post or reel url and your API key. You get back structured JSON with each comment's text, author username, and timestamp. To pull more than one page, pass the cursor from the previous response.

Can I scrape comments from any Instagram post?

Any public post or reel. Private accounts and their posts can't be accessed — that content isn't publicly visible, so there's no legitimate way to retrieve it.

How many comments can I get, and what does it cost?

Each request costs 1 credit and returns a page of comments. For posts with thousands of comments, you paginate with cursor — each additional page is another 1-credit request. Cap your loop so you only pay for what you need.

Do I get replies to comments?

The endpoint returns comments on the post. Reply threads and per-comment like counts are exposed inconsistently by Instagram, so don't assume they're always present — read those fields defensively and fall back gracefully when they're missing.

Can I get the commenter's profile details?

You get the commenter's username, verified status, user ID, and profile picture URL. For deeper data like their follower count or bio, run their handle through the Instagram profile endpoint separately.

Scraping publicly visible data is generally permissible, but the details matter — see our Instagram scraping legal guide for the nuance (public vs. private data, terms of service, and relevant case law).


Related guides:

Found this helpful?

Share it with others who might benefit

Ready to Try SociaVault?

Start extracting social media data with our powerful API. No credit card required.