~ / guides / How to Scrape Reddit Comments

How to Scrape Reddit Comments

BS
Ben Shaw
Reddit data engineer · about the author
the short version
  • A post's comments are a nested tree, and the deep replies hide behind load more comments links the page only fills in on demand. Pulling the visible ones is easy; pulling all of them is the real work.
  • The official route is PRAW: submission.comments.replace_more(limit=None) expands every load-more, then .list() flattens the tree into every comment with its score, author, and body.
  • replace_more(limit=None) fires one extra API call per collapsed branch. On a 5,000-comment thread that is hundreds of calls against the 100-queries-per-minute ceiling, so large threads are slow.
  • A managed scraper API returns the whole comment tree as JSON in one request, no app registration. I tested it against an r/Python post in June 2026 and got 6 comments back with real scores.

I wanted every comment on a busy r/Python thread, scores and all, in a CSV. The first thing I learned is that “scrape Reddit comments” is two different jobs wearing one name. Pulling the comments you can see on the page is trivial. Pulling the ones folded away behind load more comments and continue this thread, with the reply structure intact, is where every method either works or quietly drops half your data.

This guide on how to scrape Reddit comments covers both. I will show the official PRAW route I tested in June 2026, the one trick that gets the full tree instead of the first slice, why the no-credentials shortcut returns a 403, and how to flatten the result into a CSV.

What makes scraping Reddit comments difficult?

Scraping Reddit comments is difficult because a comment thread is a nested tree, not a flat list, and Reddit only sends part of it on the first load. Each post has top-level comments, each of those has replies, and each reply can have its own replies. Past a certain depth or count, Reddit collapses branches into load more comments and continue this thread placeholders that the page fetches separately when you click them.

That structure creates two failure modes. The first is the soft miss: a script reads the visible comments, reports success, and silently skips everything behind a placeholder, which on a large thread can be most of the comments. The second is the hard block: the obvious shortcut of hitting the public https://www.reddit.com/r/<sub>/comments/<id>.json view returns an HTTP 403 from a datacenter IP. I re-ran that request from a cloud server in June 2026 and got the same 403 HTML page served by snooserv (Reddit’s web edge) that I document in my guide on how to scrape Reddit without getting blocked, and the User-Agent does not change the outcome.

So a working comment scraper has to do two things the naive version skips: authenticate through a route Reddit actually answers, and expand the collapsed branches so you get the whole tree. The official API handles both, and PRAW wraps it in a few lines.

How do you scrape Reddit comments with Python and PRAW?

You scrape Reddit comments with Python by authenticating through the official API and reading the submission’s comment forest, and PRAW (the Python Reddit API Wrapper) is the standard way to do it. PRAW handles the OAuth token, the pagination, and the comment-tree objects, so the scraping code stays short.

First, register a script app at https://www.reddit.com/prefs/apps to get a client_id and client_secret. A read-only instance needs those plus a descriptive user agent:

import praw

reddit = praw.Reddit(
    client_id="YOUR_ID",
    client_secret="YOUR_SECRET",
    user_agent="reddit-comment-scraper/1.0 by u/yourname",
)

submission = reddit.submission(id="1uf89x0")   # the post id from its URL

for comment in submission.comments:
    print(comment.score, comment.author, "-", comment.body[:80])

That prints the top-level comments with each one’s score (net upvotes), author, and body. The fields you get per comment are the ones worth collecting: comment.body, comment.score, comment.author, comment.created_utc, comment.id, and comment.parent_id. The parent_id is what lets you rebuild the reply structure later, because it points at the comment or post a reply hangs under.

The catch is that submission.comments only gives you the comments Reddit sent on the first load. The replies tucked behind load more comments are not in there yet. They arrive as MoreComments placeholder objects, and expanding them is the next step.

How do you get every comment, including “load more comments”?

You get every comment by calling submission.comments.replace_more(limit=None) before you read the thread, which expands every MoreComments placeholder into the real comments it stands in for. Without it, submission.comments.list() stops at the placeholders and you lose the deep replies. The PRAW comment-extraction tutorial documents this as the standard pattern.

submission = reddit.submission(id="1uf89x0")

# Expand every "load more comments" / "continue this thread" branch.
submission.comments.replace_more(limit=None)

# .list() flattens the whole tree into one list, across all depths.
all_comments = submission.comments.list()
print(len(all_comments), "comments total")

for c in all_comments:
    print(c.score, c.author, c.parent_id, "-", c.body[:60])

The limit=None is the part that matters and the part that costs you: each MoreComments object PRAW replaces is a separate API request, so on a thread with a few hundred collapsed branches replace_more(limit=None) quietly fires a few hundred calls. Reddit’s free tier allows 100 queries per minute per client averaged over a 10-minute window, so a 5,000-comment thread can take minutes and eat your whole rate budget. If you only need the first couple of layers, pass a number, for example replace_more(limit=8), and accept that the deepest replies stay collapsed. submission.comments.list() returns a flat list, while iterating submission.comments directly keeps the nested replies structure if you would rather walk the tree yourself.

PRAW does not lift the rate ceiling, because it is the official API underneath. That is the honest trade: the comments come back clean and structured, but a thread with thousands of replies is slow, and you maintain the app registration and token refresh. When that becomes the bottleneck, the question is how to get the same tree without the credentials and the per-branch round trips.

How do you scrape Reddit comments without the official API?

You scrape Reddit comments without the official API by sending the post to a managed scraper API that returns the full comment tree as JSON, which sidesteps both the OAuth registration and the 403 you hit on the public .json route. In my June 2026 testing, a single call to ChocoData’s Reddit endpoint returned a post’s comments already expanded and nested, with real scores, no app to register.

The request is a plain GET with the subreddit, the post id, and your api key:

curl "https://api.chocodata.com/api/v1/reddit/post?subreddit=python&post_id=1uf89x0&api_key=$CHOCO_API_KEY"

The Python version returns the post object plus a comments array, where each comment carries its replies inline:

import requests

resp = requests.get(
    "https://api.chocodata.com/api/v1/reddit/post",
    params={"subreddit": "python", "post_id": "1uf89x0", "api_key": "YOUR_CHOCO_API_KEY"},
    timeout=30,
)
data = resp.json()

print(data["comments_returned"], "comments returned")
for c in data["comments"]:
    print(c["score"], c["author"]["username"], "-", (c["body"] or "")[:70])

When I ran that against an r/Python post (id 1uf89x0) in June 2026, it returned 6 comments with real scores, the top one sitting at a score of 19, each with its body, author, depth, and a replies list for nested answers. The response shape is stable: every comment has id, parent_id, depth, score, author, body, created, permalink, and replies, so you do not call replace_more or chase placeholders yourself. The proxy rotation, the anti-bot handling, and the thread expansion happen on the server side. You can get an API key on the ChocoData sign-up page and drop it into the snippet above. If you want a single comment and its descendant replies rather than the whole thread, ChocoData’s comment endpoint takes a comment permalink and returns just that subtree.

The reddit post endpoint returns the full comment tree as JSON, with each comment's score, author, and depth.

This route trades the free official tier for a managed one, so it is the cheaper path only once your volume or the per-branch call cost outweighs your time. For a one-off pull of a single thread, PRAW within the free tier is fine. For comments across many posts on a schedule, offloading the expansion and the blocking is usually worth it, and the best Reddit scrapers in 2026 roundup compares the managed options head to head. Either way, once the comments are in memory the last step is the same: getting them out to a file.

How do you export scraped Reddit comments to CSV?

You export scraped Reddit comments to CSV by flattening the nested tree into rows and writing them with pandas. The one wrinkle is the tree: a comment’s replies live inside it, so a recursive walk turns the structure into a flat table without losing the parent_id and depth that record who replied to whom.

import requests
import pandas as pd

def flatten(comments, rows):
    for c in comments:
        rows.append({
            "id": c["id"],
            "parent_id": c["parent_id"],
            "depth": c["depth"],
            "score": c["score"],
            "author": c["author"]["username"],
            "body": c["body"],
            "created": c["created"],
        })
        flatten(c["replies"], rows)   # recurse into nested replies
    return rows

data = requests.get(
    "https://api.chocodata.com/api/v1/reddit/post",
    params={"subreddit": "python", "post_id": "1uf89x0", "api_key": "YOUR_CHOCO_API_KEY"},
    timeout=30,
).json()

df = pd.DataFrame(flatten(data["comments"], []))
df.to_csv("reddit_comments.csv", index=False)
print(df[["score", "author", "depth"]].head())

The same flatten step works on the PRAW output, except PRAW’s submission.comments.list() already returns a flat list, so you skip the recursion and map each comment object’s attributes straight into a row. Keeping parent_id and depth in the CSV matters more than it looks: without them you can still count comments and rank by score, but you cannot reconstruct a conversation, which is usually the reason you wanted the comments in the first place. From there it loads into any sentiment or topic analysis the same as any other text dataset.

Scraping publicly visible Reddit comments is generally treated as lawful in the United States, on the same footing as scraping other public pages. The Ninth Circuit’s ruling in hiQ v. LinkedIn held that scraping public data likely does not violate the Computer Fraud and Abuse Act, and the EFF’s summary of the hiQ decision walks through the reasoning that a public site has “erected no gates” to bypass.

That is the baseline, not the whole picture. Reddit’s User Agreement prohibits automated access without permission, so the terms are a contract question separate from the CFAA one. Comment text can also contain personal data, which carries its own obligations under regimes like the GDPR if you store it, and logged-in or private content is a different matter entirely.

I walk through robots.txt, the User Agreement, and the case law in is scraping Reddit legal, which is the place to settle the question before you collect at scale.

Pulling comments is one piece of the bigger job. For the full workflow across posts, subreddits, and search results, the complete guide to scraping Reddit ties the endpoints together.

FAQ

Can you scrape Reddit comments without the API?

You can, but the easy path is closed. The public .json view of a comment thread returns an HTTP 403 from a datacenter IP, so a plain requests.get fails before it parses anything. The working options without the official API are residential proxies plus your own parser, or a managed scraper API that returns the comment tree as JSON for you.

How do I scrape all comments from a Reddit post, not just the top ones?

Call submission.comments.replace_more(limit=None) in PRAW before reading the thread. That replaces every MoreComments placeholder (the load more comments and continue this thread links) with the real comments, then submission.comments.list() returns the full flattened list across all depths. Each replace_more call costs an extra API request, so set a limit on very large threads if speed matters.

What fields can you get from a Reddit comment?

Per comment you can pull the comment body text, the score (net upvotes), the author username, the created timestamp, the comment id, and the parent_id that lets you rebuild the reply tree. PRAW exposes these as comment.body, comment.score, comment.author, and comment.created_utc.

Does scraping Reddit comments need OAuth credentials?

The official API and PRAW need OAuth credentials: a client id, a client secret, and a registered script app. As of 2025 every app needs pre-approval, including personal ones. A managed scraper API skips all of that. You send one request with a single api_key and get the parsed comments back.

Is scraping Reddit comments legal?

Scraping publicly visible Reddit comments is generally treated as lawful in the US after hiQ v. LinkedIn, which held that scraping public data likely does not violate the CFAA. Reddit's User Agreement still restricts automated access without permission, and personal data carries its own obligations. I cover the detail in my guide on whether scraping Reddit is legal.

BS
Ben Shaw
I've built Reddit data pipelines for years. On redditscraperapi.com I run Reddit scraping methods against live pages and publish what actually holds up.