How to Scrape Instagram Comments (2026)
- Instagram serves public comments through its GraphQL
queryendpoint underedge_media_to_parent_comment, not in the post HTML. The official Graph API returns comments only on posts you own, so any public or competitor post needs a scraper. - In my July 2026 tests a logged-out request returned only the first ~12 comments and an
end_cursorbefore the rest sat behind a login wall, and a plain Pythonrequestscall was refused at the TLS layer with HTTP 429. - The GraphQL
doc_idthat selects the post query rotates: Instagram deprecated8845758582119845around June 2026 and moved to27128499623469141, which silently breaks any scraper that hardcodes it. Instaloader tracks the current value for you. - For volume I send one GET to a managed comment scraper that handles the fingerprint, proxies, cursor paging, and
doc_id, then returns each comment (author, text, likes, timestamp, nested replies) as JSON.
I tried to scrape Instagram comments the obvious way first: one requests.get against a public post’s comment endpoint. It came back 429 with an empty body, and when I opened the same post in a logged-out browser, only the first handful of comments rendered before a login wall covered the rest. That gap is what any honest guide on how to scrape Instagram comments has to deal with, because the comments Instagram shows a person in the app are not the comments it hands a script.
Below is what I ran in July 2026, the responses Instagram returned, and the code that actually pulled full comment threads: the Instaloader library for a no-login Python route, the raw GraphQL endpoint underneath it, and a scraper API for volume. Every comment object here carries the same core fields - the author’s username, the comment text, the like count, the timestamp, and the nested replies - and the whole job is getting those fields back past the login gate.
What does an Instagram comment contain?
An Instagram comment contains five fields worth extracting: the comment text, the author’s username, the like count, the timestamp, and any nested replies attached to it. A comment scraper is only as useful as the fields it returns intact, and reply nesting is the one that separates a usable thread from a flat dump.
Here is the shape of a single parsed comment, the target every method in this guide returns:
- Author: the commenter’s username and profile reference, the anchor for any audience or lead work.
- Text: the comment body itself, the input for sentiment and keyword analysis.
- Likes: the like count on the comment, which sorts high-signal reactions from noise.
- Timestamp: when the comment was posted, needed to track how a thread moves after a launch.
- Replies: the answers nested under a parent comment, each with its own author, text, likes, and timestamp.
Those fields sit in Instagram’s data, but not where a simple page scrape can reach them. The next thing to understand is why the official route hands back none of them for a post you do not own.
Why won’t the official Instagram API return public comments?
The official Instagram Graph API will not return public comments because it reads comments only on media owned by the Business or Creator account you have connected. Meta’s Instagram Platform comments reference returns the comments on your own posts and nothing outside them, so a competitor’s post or any public account you do not manage is out of reach through the API entirely.
Even on your own posts the ceiling is low. The Graph API holds each app to 200 calls per user per hour on a rolling window, and every request counts against it whether it succeeds or not. Each comments call returns one page, and paging through replies spends more of that budget, so authorized comment moderation on a busy business account runs into the limit fast.
That leaves the public web surface, where the comments actually render. Instagram loads them through its GraphQL query endpoint, and the post HTML no longer ships the thread inside it. So scraping the web endpoint is the route to any public comment data, and the rest of this guide is the code for calling it, starting with Python.
How do you scrape Instagram comments with Python?
You scrape Instagram comments with Python by calling the GraphQL query endpoint that Instagram’s web app uses, and the practical way to do it is the Instaloader library, which handles the comment paging and the rotating query identifier for you. A hand-written requests call fails before it reaches the data, so it is worth seeing that failure first.
Here is the naive call I ran, the one that returned 429 so you can recognize it in your own logs:
import requests, json
# doc_id selects the GraphQL query; it rotates every few weeks.
data = {"variables": json.dumps({"shortcode": "Cymq6lEt6Vx"}), "doc_id": "27128499623469141"}
r = requests.post("https://www.instagram.com/graphql/query", data=data, timeout=20)
print(r.status_code) # -> 429
print(len(r.content)) # -> 0
The block sits at the TLS handshake, below the headers, which is why setting a browser User-Agent changes nothing. Matching a real browser’s fingerprint with a client like curl_cffi clears the 429, and I walk through that fingerprint fix in detail in my guide on scraping Instagram with Python. For comments specifically, the library route skips most of that work.
Scraping comments with Instaloader
Instaloader pulls public comments without a login by walking a Post object, and it exposes exactly the fields defined above. Install it with pip3 install instaloader, then iterate post.get_comments():
import instaloader
L = instaloader.Instaloader(download_pictures=False, download_videos=False)
post = instaloader.Post.from_shortcode(L.context, "Cymq6lEt6Vx")
for c in post.get_comments():
print(c.owner.username, "-", c.text)
print(" likes:", c.likes_count, "at", c.created_at_utc)
for reply in c.answers: # nested replies under this comment
print(" reply:", reply.owner.username, "-", reply.text)
Each PostComment carries owner (a profile with .username), text, likes_count, created_at_utc, and an answers iterator that yields the nested replies, so one loop returns author, text, likes, timestamp, and reply threads together. Instaloader downloads “pictures and videos along with their captions and other metadata,” per the project site, and it is MIT-licensed and free. Login is optional and the anonymous path works for moderate volume, then Instagram starts answering with 401 or 429, which is the shared ceiling covered in the next section.
Paging the comment thread and its replies
Under the hood, the comment thread is paginated, and this is the part a raw scraper has to handle itself. The GraphQL response carries comments in edge_media_to_parent_comment, and the first request returns only about a dozen of them plus a page_info block with an end_cursor and a has_next_page flag. You pass that end_cursor back to fetch roughly fifty more per page, and each parent comment’s replies live in its own edge_threaded_comments with a separate cursor, so a full thread is many requests, not one.
The identifier that selects the query, the doc_id, is the fragile part. Instagram rotates it every few weeks, and it deprecated the post doc_id 8845758582119845 around June 2026, replacing it with 27128499623469141. A scraper that hardcodes an old value keeps running and returns a well-formed but empty response, so an empty 200 is a rotation signal, not an empty thread. Tracking that rotation by hand is the maintenance most teams eventually hand off, and it is tied to the blocking problem next.
How do you scrape Instagram comments without getting blocked?
You avoid the block by changing the two things that trigger it, the IP reputation and the request rate, then slowing down. These are the levers that moved the result in my testing, roughly in order of impact:
- Use residential or mobile IPs. Datacenter and cloud ranges are refused on sight. A residential IP presents as an ordinary home connection, which is what the comment endpoints expect.
- Stay slow and under the ceiling. A single residential IP is capped near 200 requests per hour before
429s start, per the Scrapfly engineering team, and deep comment paging burns through that budget quickly. Space requests a few seconds apart. - Hold a sticky session per IP. Rotating on every request looks robotic. Keeping one IP for a short session mimics a real browser reading a thread.
- Watch for the rotated
doc_id. Treat an empty200as a signal to re-read the current identifier, not as proof the post has no comments. - Read public data only. A logged-in bot moving fast draws a checkpoint on the account, and private accounts return nothing without an approved session.
The honest tradeoff is maintenance. Doing all of this yourself means renting a residential proxy pool, rotating it on sticky sessions, re-reading the doc_id on a schedule, and adapting the parser when the response shape shifts, which is a standing project once you pass a few thousand comments. I go deeper on the warning signs in my guide on avoiding Instagram scraping blocks. Once that upkeep costs more than it saves, the managed route is the alternative.
How do you scrape Instagram comments at scale with a scraper API?
You scrape Instagram comments at scale by sending one HTTP request to a scraper API that handles the fingerprint, residential proxies, cursor paging, and doc_id tracking on its side, then returns the parsed comment thread as JSON. This removes the part of the Python routes above that breaks, so you stop chasing 429s and rotated identifiers and get author, text, likes, timestamp, and nested replies back as structured data.
The request is a single GET with the post URL and your API key. In my runs against ChocoData, the same shape that returns a profile returns a comment thread by swapping the resource path:
curl "https://chocodata.com/api/v1/instagram/comments?url=https://www.instagram.com/p/POST_ID/&api_key=$CHOCO_API_KEY"
The Python version returns parsed JSON you can drop straight into a pipeline, with each comment already carrying the five fields:
import requests, os
resp = requests.get(
"https://chocodata.com/api/v1/instagram/comments",
params={
"url": "https://www.instagram.com/p/POST_ID/",
"api_key": os.environ["CHOCO_API_KEY"],
},
timeout=30,
)
for c in resp.json()["comments"]:
print(c["username"], c["text"], c["like_count"], c["reply_count"], c["timestamp"])
This returned the comment text, the commenter’s username, the like count, the reply count, and timestamps without a proxy, a fingerprint library, or a login on my side, and the replies came back nested under each parent rather than flattened. The endpoint is authenticated, so a call with no key returns a not-found response, which is expected. The free tier covers 1,000 requests to start, and the same pattern points at other targets through dedicated endpoints, which I rank head to head in my best Instagram scrapers in 2026 roundup.
For a one-off pull of a single post, the Instaloader script above is free and enough. Once you need every comment across many posts on a schedule, with replies intact, the paging and doc_id upkeep is what the API removes.
Is it legal to scrape Instagram comments?
Scraping public Instagram comments sits in a contested area, so treat the platform rules and privacy law as two separate questions. On the platform side, Instagram’s robots.txt states that “collection of data on Instagram through automated means is prohibited unless you have express written permission,” and it disallows comment paths such as /*/comments/ for the major crawlers, pointing to Meta’s automated data collection terms.
The legal frame around public scraping shifted in 2024. A US federal court in Meta Platforms v. Bright Data held in January 2024 that Meta’s terms do not bar scraping of public, logged-off data, because a logged-out scraper never agrees to those terms. That ruling is specific to public data and does not reach private accounts or content behind the login wall.
Privacy law is the constraint that does not go away. Comment authors and their text are personal data under regimes like the GDPR and CCPA, so a lawful basis, retention limits, and data-subject rights attach the moment you store them. None of this is legal advice: scrape only public comments, respect the platform’s rate limits, and handle any personal data you keep under the law that applies to you.
Sources
- Meta for Developers - Instagram Platform comments reference (Graph API reads comments only on owned media) - https://developers.facebook.com/docs/instagram-platform/instagram-graph-api/reference/ig-media/comments
- Meta for Developers - Graph API rate limiting (200 calls per user per hour) - https://developers.facebook.com/docs/graph-api/overview/rate-limiting/
- Instaloader - project documentation (Post.get_comments, PostComment fields, metadata) - https://instaloader.github.io/
- Instaloader - Pull Request #2706 (June 2026 doc_id deprecation, 8845758582119845 to 27128499623469141) - https://github.com/instaloader/instaloader/pull/2706
- Scrapfly - How to Scrape Instagram in 2026 (comment structure, ~200 requests/hour/IP anonymous ceiling) - https://scrapfly.io/blog/posts/how-to-scrape-instagram
- Instagram robots.txt (automated collection prohibited, /*/comments/ disallowed) - https://www.instagram.com/robots.txt
- CourtListener - Meta Platforms Inc. v. Bright Data Ltd. (January 2024 ruling on public, logged-off data) - https://www.courtlistener.com/docket/63830778/meta-platforms-inc-v-bright-data-ltd/
FAQ
Can you scrape Instagram comments without logging in?
You can read a thin slice of a public post's comments logged out, then Instagram gates the rest. In my July 2026 tests a logged-out request returned only the first dozen or so comments and an end_cursor, and following that cursor from a datacenter IP returned HTTP 429 with an empty body. Residential IPs and a slow cadence extend how far you get, but deep comment threads and reply chains are where anonymous access throttles first. Private accounts return nothing without an approved logged-in session.
How do you scrape the replies nested under a comment?
Replies live under each parent comment, in edge_threaded_comments in the raw GraphQL response or in the answers iterator on an Instaloader PostComment. Each reply carries its own author, text, like count, and timestamp, and its own pagination, so a deep chain needs a separate follow-up request per parent. This is the field cheaper tools most often flatten or truncate, so it is worth checking that whatever route you use returns replies attached to their parent rather than as a flat list.
Why does my Instagram comment scraper suddenly return nothing?
An Instagram comment scraper that returned data yesterday and returns an empty 200 today has almost always hit a rotated GraphQL doc_id. Instagram changes those identifiers every few weeks as an anti-scraping measure, and it deprecated the widely used post doc_id 8845758582119845 around June 2026. A hardcoded value keeps returning a well-formed but empty response, so treat an empty 200 as a rotation signal and re-read the current doc_id off a live request in your browser's network tab, or use a library or API that tracks it.
How do you export scraped Instagram comments to CSV?
Collect each comment into a row of fields (username, text, like count, timestamp), build a list of those rows, and write it with pandas: pd.DataFrame(rows).to_csv('comments.csv', index=False). If you are using Instaloader, read comment.owner.username, comment.text, comment.likes_count, and comment.created_at_utc into each row inside the loop. A scraper API that returns parsed JSON drops into the same DataFrame step without the paging code.