Reddit Comment Thread Scraper with Score & Keyword Filters
Scrape Reddit comment threads, nested replies included, with author, score, depth, and moderation flags, filtered by date, score, author, or keyword. Optional parent post record. No API key or login.
Input schema
Input fields, types, required flags, descriptions, defaults, and prefills come from the published definition. Prefill examples are distinct from defaults.
| Field | Type | Required | Description |
|---|---|---|---|
postUrls | array | required | Post URLs, comment URLs, share links or bare post IDs. Accepted forms: full post URL (https://www.reddit.com/r/x/comments/1abc/title/), short URL (https://www.reddit.com/comments/1abc or with ?sort=top), comment URL (.../comments/1abc/title/comment/xyz/), mobile share link (https://www.reddit.com/r/x/s/token), bare post id (1abc123) or t3_1abc123. |
maxComments | integer | optional | Maximum number of comments to scrape from each post (including nested replies). |
commentSort | string | optional | How to sort the comment threads. |
Show all input fields
| Field | Type | Required | Description |
|---|---|---|---|
postUrls | array | required | Post URLs, comment URLs, share links or bare post IDs. Accepted forms: full post URL (https://www.reddit.com/r/x/comments/1abc/title/), short URL (https://www.reddit.com/comments/1abc or with ?sort=top), comment URL (.../comments/1abc/title/comment/xyz/), mobile share link (https://www.reddit.com/r/x/s/token), bare post id (1abc123) or t3_1abc123. |
maxComments | integer | optional | Maximum number of comments to scrape from each post (including nested replies). |
commentSort | string | optional | How to sort the comment threads. |
includePost | boolean | optional | When enabled, the parent post metadata is emitted as the first record of each thread. |
postedAfter | string | optional | Only keep comments created on or after this date (UTC). |
postedBefore | string | optional | Only keep comments created on or before this date (interpreted as the end of that day, UTC). |
minDepth | integer | optional | Only keep comments nested at or deeper than this depth (0 = keep everything, 1 = drop top-level comments and keep only replies, 2 = keep only replies-to-replies and deeper, etc.). Leave empty for no limit. |
maxDepth | integer | optional | Only keep comments nested at or above this depth (0 = top-level comments only, 1 = top-level + first-level replies, etc.). Leave empty for no limit. |
minCommentScore | integer | optional | Only keep comments with a score (upvotes minus downvotes) at or above this value. Leave empty for no limit. |
maxCommentScore | integer | optional | Only keep comments with a score (upvotes minus downvotes) at or below this value. Leave empty for no limit. |
onlyOP | boolean | optional | When enabled, only comments written by the post's original author are kept. |
excludeDeletedRemoved | boolean | optional | When enabled, comments that Reddit marks as deleted or author-deleted are dropped (no real content left to scrape). |
excludeCollapsed | boolean | optional | When enabled, comments Reddit collapses by default (low score or crowd control) are dropped. |
authors | array | optional | Only keep comments written by these Reddit usernames (case-insensitive). Leave empty to keep comments from all authors. |
excludeAuthors | array | optional | Drop comments written by these Reddit usernames (case-insensitive), e.g. bots like AutoModerator. Leave empty to keep comments from all authors. Applied after `authors` (an author listed in both is excluded). |
keywords | array | optional | Only keep comments whose body contains at least one of these keywords or phrases (case-insensitive substring match). Leave empty to keep all comments. |
excludeKeywords | array | optional | Drop comments whose body contains any of these keywords or phrases (case-insensitive substring match), e.g. spam/boilerplate phrases. Applied after `keywords` (a keyword listed in both still excludes the comment). Leave empty to keep all comments. |
minAwards | integer | optional | Only keep comments with at least this many total awards. Leave empty for no limit. |
minCommentLength | integer | optional | Only keep comments whose body text is at least this many characters long. Useful for filtering out one-word/emoji-only noise when building NLP datasets. Leave empty for no limit. |
maxCommentLength | integer | optional | Only keep comments whose body text is at most this many characters long (Reddit's hard cap is 10,000 characters). Leave empty for no limit. |
focusOnTargetComment | boolean | optional | When a `postUrls` item is a specific comment permalink, return that comment plus its ancestor chain (see `commentContext`). Works for deeply nested targets; the target must still be publicly visible. Has no effect on plain post URLs. |
commentContext | integer | optional | How many levels of parent comments above the target comment to include when `focusOnTargetComment` is enabled (matches Reddit's own comment-permalink page). Ignored otherwise. |
Download the published input schema
View complete JSON
{
"properties": {
"authors": {
"description": "Only keep comments written by these Reddit usernames (case-insensitive). Leave empty to keep comments from all authors.",
"editor": "stringList",
"example": [
"spez"
],
"title": "Filter by comment author(s)",
"type": "array"
},
"commentContext": {
"default": 3,
"description": "How many levels of parent comments above the target comment to include when `focusOnTargetComment` is enabled (matches Reddit's own comment-permalink page). Ignored otherwise.",
"maximum": 8,
"minimum": 0,
"prefill": 3,
"title": "Ancestor context depth (mode = focusOnTargetComment)",
"type": "integer"
},
"commentSort": {
"default": "confidence",
"description": "How to sort the comment threads.",
"enum": [
"confidence",
"top",
"new",
"controversial",
"old",
"qa"
],
"enumTitles": [
"Best (Confidence)",
"Top",
"New",
"Controversial",
"Old",
"Q&A"
],
"prefill": "confidence",
"title": "Sort comments by",
"type": "string"
},
"excludeAuthors": {
"description": "Drop comments written by these Reddit usernames (case-insensitive), e.g. bots like AutoModerator. Leave empty to keep comments from all authors. Applied after `authors` (an author listed in both is excluded).",
"editor": "stringList",
"example": [
"AutoModerator"
],
"title": "Exclude comment author(s)",
"type": "array"
},
"excludeCollapsed": {
"default": false,
"description": "When enabled, comments Reddit collapses by default (low score or crowd control) are dropped.",
"prefill": false,
"title": "Exclude collapsed comments",
"type": "boolean"
},
"excludeDeletedRemoved": {
"default": false,
"description": "When enabled, comments that Reddit marks as deleted or author-deleted are dropped (no real content left to scrape).",
"prefill": false,
"title": "Exclude deleted/removed comments",
"type": "boolean"
},
"excludeKeywords": {
"description": "Drop comments whose body contains any of these keywords or phrases (case-insensitive substring match), e.g. spam/boilerplate phrases. Applied after `keywords` (a keyword listed in both still excludes the comment). Leave empty to keep all comments.",
"editor": "stringList",
"example": [
"I am a bot",
"check out my"
],
"title": "Exclude keyword(s) in comment body",
"type": "array"
},
"focusOnTargetComment": {
"default": false,
"description": "When a `postUrls` item is a specific comment permalink, return that comment plus its ancestor chain (see `commentContext`). Works for deeply nested targets; the target must still be publicly visible. Has no effect on plain post URLs.",
"prefill": false,
"title": "Focus on the specific comment in the URL",
"type": "boolean"
},
"includePost": {
"default": false,
"description": "When enabled, the parent post metadata is emitted as the first record of each thread.",
"prefill": false,
"title": "Include the parent post record",
"type": "boolean"
},
"keywords": {
"description": "Only keep comments whose body contains at least one of these keywords or phrases (case-insensitive substring match). Leave empty to keep all comments.",
"editor": "stringList",
"example": [
"thanks",
"great point"
],
"title": "Filter by keyword(s) in comment body",
"type": "array"
},
"maxCommentLength": {
"description": "Only keep comments whose body text is at most this many characters long (Reddit's hard cap is 10,000 characters). Leave empty for no limit.",
"example": 2000,
"maximum": 10000,
"minimum": 0,
"title": "Maximum comment length (characters)",
"type": "integer"
},
"maxCommentScore": {
"description": "Only keep comments with a score (upvotes minus downvotes) at or below this value. Leave empty for no limit.",
"example": 1000,
"maximum": 1000000,
"minimum": -1000000,
"title": "Maximum comment score",
"type": "integer"
},
"maxComments": {
"default": 100,
"description": "Maximum number of comments to scrape from each post (including nested replies).",
"example": 100,
"maximum": 100000,
"minimum": 1,
"prefill": 30,
"title": "Maximum comments per post",
"type": "integer"
},
"maxDepth": {
"description": "Only keep comments nested at or above this depth (0 = top-level comments only, 1 = top-level + first-level replies, etc.). Leave empty for no limit.",
"example": 2,
"maximum": 100,
"minimum": 0,
"title": "Maximum reply depth",
"type": "integer"
},
"minAwards": {
"description": "Only keep comments with at least this many total awards. Leave empty for no limit.",
"example": 1,
"maximum": 1000000,
"minimum": 0,
"title": "Minimum total awards received",
"type": "integer"
},
"minCommentLength": {
"description": "Only keep comments whose body text is at least this many characters long. Useful for filtering out one-word/emoji-only noise when building NLP datasets. Leave empty for no limit.",
"example": 20,
"maximum": 10000,
"minimum": 0,
"title": "Minimum comment length (characters)",
"type": "integer"
},
"minCommentScore": {
"description": "Only keep comments with a score (upvotes minus downvotes) at or above this value. Leave empty for no limit.",
"example": 1,
"maximum": 1000000,
"minimum": -1000000,
"title": "Minimum comment score",
"type": "integer"
},
"minDepth": {
"description": "Only keep comments nested at or deeper than this depth (0 = keep everything, 1 = drop top-level comments and keep only replies, 2 = keep only replies-to-replies and deeper, etc.). Leave empty for no limit.",
"example": 1,
"maximum": 100,
"minimum": 0,
"title": "Minimum reply depth",
"type": "integer"
},
"onlyOP": {
"default": false,
"description": "When enabled, only comments written by the post's original author are kept.",
"prefill": false,
"title": "Only original poster's comments",
"type": "boolean"
},
"postUrls": {
"description": "Post URLs, comment URLs, share links or bare post IDs. Accepted forms: full post URL (https://www.reddit.com/r/x/comments/1abc/title/), short URL (https://www.reddit.com/comments/1abc or with ?sort=top), comment URL (.../comments/1abc/title/comment/xyz/), mobile share link (https://www.reddit.com/r/x/s/token), bare post id (1abc123) or t3_1abc123.",
"editor": "stringList",
"example": [
"https://www.reddit.com/r/programming/comments/1vqukkf/nothing_like_a_monday_morning_github_outage/",
"https://www.reddit.com/comments/1tlh5aj"
],
"minItems": 1,
"prefill": [
"https://www.reddit.com/r/programming/comments/1vqukkf/"
],
"title": "Reddit Post URLs",
"type": "array"
},
"postedAfter": {
"description": "Only keep comments created on or after this date (UTC).",
"editor": "datepicker",
"example": "2025-01-01",
"title": "Comments created on or after (YYYY-MM-DD)",
"type": "string"
},
"postedBefore": {
"description": "Only keep comments created on or before this date (interpreted as the end of that day, UTC).",
"editor": "datepicker",
"example": "2025-12-31",
"title": "Comments created on or before (YYYY-MM-DD)",
"type": "string"
}
},
"required": [
"postUrls"
],
"schemaVersion": 1,
"title": "Reddit Comment Scraper Input",
"type": "object"
}
Output schema
Published result links and storage locations. This schema is not an exhaustive contract for every field in a dataset record.
| Field | Type | Required | Description |
|---|---|---|---|
commentAndPostRecords | string | optional | Dataset containing one record per comment, plus the parent post record when 'Include post' is on. Records are distinguished by the `dataType` field (`comment` or `post`). |
Download the published output schema
View complete JSON
{
"actorOutputSchemaVersion": 1,
"description": "Reddit comment threads, nested replies included, with author, score, depth, tree path, edited/deleted flags, and parent relationships, plus an optional parent post record.",
"properties": {
"commentAndPostRecords": {
"description": "Dataset containing one record per comment, plus the parent post record when 'Include post' is on. Records are distinguished by the `dataType` field (`comment` or `post`).",
"template": "{{links.apiDefaultDatasetUrl}}/items",
"title": "Comment & post records",
"type": "string"
}
},
"title": "Reddit Comment Thread Scraper with Score & Keyword Filters Output"
}
Dataset schema
Published dataset views and any declared field definitions. A view can select only a subset of the returned record fields.
Download the published dataset schema
View complete JSON
{
"actorSpecification": 1,
"views": {
"overview": {
"display": {
"component": "table",
"properties": {}
},
"title": "Reddit Comments",
"transformation": {
"fields": [
"post_title",
"author",
"body",
"score",
"depth",
"is_op",
"permalink",
"created_at"
]
}
}
}
}
Key-value schema
Published key-value store definitions, when provided by this build.
This definition is not present in the default published build. See the published README for available documentation.
Published README
Read the full published documentation
Reddit Comment Thread Scraper with Score & Keyword Filters
Export Reddit comment threads — top-level comments and nested replies, including branches hidden behind "load more replies" and "continue this thread" — as flat, structured rows. Paste post links (or comment links), choose a sort, and get author, text, score, timestamps, reply depth, parent relationships, and moderation/deletion flags. No Reddit account, API key, or login required.
What this actor does
- Full thread reconstruction — follows Reddit's own nested reply loaders and continue-thread pages until
maxCommentsis reached or Reddit stops serving comments to logged-out readers - 6 comment sorts — Best, Top, New, Controversial, Old, and Q&A, matching Reddit's UI
- Comment permalink focus — paste a comment permalink with
focusOnTargetCommentenabled to get that comment plus its ancestor chain, including deeply nested targets - Rich client-side filtering — date window, reply-depth range, score range, author allow/deny, keyword include/exclude, length bounds, award count, OP-only, deleted/removed, collapsed
- Optional parent post record — title, author, score, ratio, timestamp, subreddit, and text/link metadata when
includePostis on - Flexible URL input — post URLs, comment permalinks, share links,
redd.itshort links, bare post IDs - Omit-empty output — a field appears only when Reddit actually exposes a value for it
Output per comment
dataType—"comment"comment_id,comment_name— bare id and Reddit fullname (t1_...)post_id,post_url,post_title,link_id— parent thread contextpermalink— direct comment linkauthor— username ([deleted]when the account is gone)avatarUrl— author avatar image, when availablebody— comment text as readable plain text (block breaks preserved)body_html— comment HTML from Reddit's rendererscore— net score (upvotes minus downvotes) at crawl timecreated_utc,created_at— posted time (epoch and ISO-8601 UTC)edited,edited_at— whether and when the comment was editeddepth— nesting level,0= top-level reply to the postposition— the comment's position among its siblingsparentPositions— the position path from the thread root. The prefix can be incomplete for branches reached through a "continue this thread" page; useparent_idfor exact parentageparent_id,parent_kind— direct parent fullname (t3_...for top-level ort1_...for a reply) and kindis_op— author is the post's original postercollapsed— Reddit collapses the comment by defaultisCommentDeleted,isAuthorDeleted— comment content / author account was deletedcontentType—text,image, orgiphyisHighlighted,hasPaidAward— Reddit highlight/paid-award markers when presenttotal_awards_received— total award count (0 when none)subreddit,subreddit_prefixed,subreddit_id— community contextcrawled_at— when this record was fetched (ISO-8601)source—"html"
A rare deleted-comment placeholder that Reddit no longer gives an id for has no comment_id; its isCommentDeleted/isAuthorDeleted flags and position path still identify it. Its parent_id falls back to the post, so use the position path for its place in the tree.
Fields Reddit does not expose to logged-out readers are omitted: comment author_id, author flair fields, ups/downs, score_hidden, controversiality, distinguished, comment-level stickied/locked/archived, collapsed_reason*, legacy gilded, the award breakdown, and removed_by_category. See Limitations below.
Output per post
Emitted once per post as the first record when includePost is on. Comment filters do not apply to it.
dataType—"post"post_id,post_name,title,author,author_idsubreddit,subreddit_prefixed,subreddit_idurl,permalink— canonical reddit.com linkcontent_url— external destination for link postsdomain,post_type,is_selfcontent,content_html— self-post bodyscore,upvote_ratio,num_commentscreated_utc,created_atlanguageis_nsfw,is_stickied,is_lockedtotal_awards_receivedcrawled_at,source
Post fields that require Reddit's legacy API data (flair, media/gallery URLs, poll data, crosspost counts, removal category, ups/downs, edited timestamp) are not available to logged-out readers and are omitted.
Input
| Field | Type | Default | Description |
|---|---|---|---|
postUrls |
array | – (required) | Post URLs, comment URLs, share links, redd.it short links, or bare post IDs |
maxComments |
integer | 100 |
Maximum comments to keep per post, after filters (1–100,000) |
commentSort |
select | confidence |
confidence (Best), top, new, controversial, old, qa |
includePost |
boolean | false |
Emit the parent post as the first record of each thread |
postedAfter |
date | – | Keep comments created on/after this date (UTC) |
postedBefore |
date | – | Keep comments created on/before this date (UTC; a bare date means end of day) |
minDepth |
integer | – | Keep comments at or deeper than this nesting level (0–100) |
maxDepth |
integer | – | Keep comments at or above this nesting level (0–100) |
minCommentScore |
integer | – | Keep comments with score ≥ this value |
maxCommentScore |
integer | – | Keep comments with score ≤ this value |
onlyOP |
boolean | false |
Keep only the original poster's comments |
excludeDeletedRemoved |
boolean | false |
Drop comments Reddit marks deleted or author-deleted |
excludeCollapsed |
boolean | false |
Drop comments Reddit collapses by default |
authors |
array | – | Keep only these usernames (case-insensitive) |
excludeAuthors |
array | – | Drop these usernames (case-insensitive), e.g. bots |
keywords |
array | – | Keep comments containing at least one keyword/phrase |
excludeKeywords |
array | – | Drop comments containing any keyword/phrase |
minAwards |
integer | – | Keep comments with at least this many total awards |
minCommentLength |
integer | – | Minimum body length in characters (0–10,000) |
maxCommentLength |
integer | – | Maximum body length in characters (0–10,000) |
focusOnTargetComment |
boolean | false |
For a comment permalink, return the target plus its ancestor chain. Works for deeply nested targets; the target must still be publicly visible |
commentContext |
integer | 3 |
Ancestor levels above the target comment when Reddit returns the focus view (0–8) |
Accepted postUrls forms:
- Full post URL:
https://www.reddit.com/r/programming/comments/1abc123/some_title/ - Short URL:
https://www.reddit.com/comments/1abc123(also with?sort=top) - Comment URL:
https://www.reddit.com/r/x/comments/1abc123/title/comment/xyz987/ - Share link:
https://www.reddit.com/r/x/s/<token> - Short link:
https://redd.it/1abc123 - Bare post ID:
1abc123ort3_1abc123 old.reddit.comURLs also work
Malformed items (empty strings, garbage URLs, non-strings) are skipped without failing the run.
Removed inputs: distinguishedFilter, controversialOnly, and excludeStickied were removed because Reddit no longer exposes those markers to logged-out readers. Old saved tasks that still send them keep running; the actor logs one warning and ignores them.
Example: scrape a whole thread sorted by Top
{
"postUrls": ["https://www.reddit.com/r/technology/comments/1abc123/some_title/"],
"maxComments": 500,
"commentSort": "top",
"includePost": true
}
Example: recent, high-signal comments only
{
"postUrls": ["https://www.reddit.com/r/investing/comments/1abc123/some_title/"],
"maxComments": 1000,
"postedAfter": "2026-06-01",
"minCommentScore": 5,
"excludeDeletedRemoved": true
}
Example: NLP dataset — clean comment bodies
{
"postUrls": [
"https://www.reddit.com/r/AskReddit/comments/1abc123/some_title/",
"https://www.reddit.com/r/AskReddit/comments/1def456/other_title/"
],
"maxComments": 5000,
"excludeDeletedRemoved": true,
"excludeAuthors": ["AutoModerator"],
"minCommentLength": 20,
"excludeKeywords": ["I am a bot"]
}
Example: jump straight to one comment
{
"postUrls": ["https://www.reddit.com/r/x/comments/1abc123/title/comment/xyz987/"],
"focusOnTargetComment": true,
"commentContext": 5
}
Use cases
- Sentiment & NLP datasets — build large, structured corpora from real discussions
- Market & product research — read what customers say in the replies, not just the top comment
- Community & moderation analysis — study conversation depth, controversy, and OP engagement
- Launch monitoring — track reaction to announcements across subreddits on a schedule
- Academic & social research — export discussion trees for network and discourse analysis
- Support mining — surface recurring complaints buried deep in a thread
FAQ
Do I need a Reddit account, API key, or login? No. The actor reads Reddit's public pages anonymously and stores no credentials.
Why are some fields missing compared with older versions? Reddit stopped serving its anonymous JSON endpoints in 2026 and does not render author IDs, vote splits, flair, moderator badges, or award details to logged-out readers. The actor emits every field the public pages still expose and omits the rest instead of inventing values.
Does it really capture the whole thread?
Up to maxComments, yes. The actor follows Reddit's nested reply loaders and continue-thread pages until the thread is exhausted or your limit is reached. Reddit's displayed comment count includes deleted and removed comments, so a complete export can be smaller than that number.
What is the difference between parent_id and link_id?
link_id always points to the parent post (t3_...). parent_id points to the comment's direct parent — the post for a top-level comment or another comment for a reply. parent_kind tells you which, and parentPositions gives the position path (with the continue-page caveat above).
How do I scrape just one comment instead of the whole thread?
Paste its permalink into postUrls and enable focusOnTargetComment. You get that comment plus up to commentContext levels of parent comments. The target must still be publicly visible; deleted/removed targets return no records.
Why does a deleted comment still appear?
Deleted comments are part of the thread structure. They carry isCommentDeleted / isAuthorDeleted and usually no body; enable excludeDeletedRemoved to drop them.
What happens for a private subreddit, banned post, or deleted post? The actor returns 0 records for that URL and continues with the other URLs. A status message explains when nothing could be read.
Do I need to configure a proxy? No. The actor selects the network route it needs automatically.
Is this affiliated with Reddit? No. This is an independent third-party actor that reads public web pages; it is not endorsed by or affiliated with Reddit, Inc.
Cost
Measured all-in run cost is roughly $0.72–1.58 per 1,000 comments, depending on thread depth, how many continue-thread branches are loaded, and how many deleted comments Reddit still counts. Small or heavily filtered runs have a higher fixed cost per 1,000. Store pricing is set separately.
Limitations
- Reddit's own displayed comment count includes deleted, removed, and filtered comments that are not served to logged-out readers. The actor reports what is actually served.
- Comment author IDs, vote up/down splits, flair, moderator/admin badges, controversy flags, award names/icons, and removal categories are not exposed anonymously. They are omitted rather than guessed. (The post record still carries the post author's ID when Reddit renders it.)
- Some comments arrive from Reddit's lazy-loading fragments with
body_htmlbut without a plain-textbody(and occasionally without either) even though they are not deleted. Usebody_htmlas a fallback; the omission rate measured in cloud runs was ~15–18%. parentPositionscan lose the continue-target's position for branches loaded through "continue this thread" pages, so paths for those descendants may not match the tree exactly.parent_idremains correct.- Deleted-comment placeholders that Reddit renders without an id may carry the post as
parent_ideven when nested; their position path is the only tree hint for those rows. - Very large threads take longer because every hidden branch is loaded through Reddit's own pagination; there is no branch pruning, so cost scales with the served tree as well as
maxComments. - The parent post record is intentionally thin: media, gallery, poll, crosspost, and flair details are not available on the public post page.
Python quickstart
Use a schema-derived input and the Python standard library to start a run. An Apify account and token are required for execution.
Source build aXR6xVwyCStLrYveu. Documentation is regenerated from the default published build; private drafts are not included.