# Reddit Comments Scraper (`nice_dev/reddit-comments-scraper`) Actor

Scrape Reddit comments: full threads, keyword search inside comments, community feeds and user history. Export to JSON, CSV or Excel.

- **URL**: https://apify.com/nice_dev/reddit-comments-scraper.md
- **Developed by:** [Nice Dev](https://apify.com/nice_dev) (community)
- **Categories:** Social media, Marketing, MCP servers
- **Stats:** 2 total users, 2 monthly users, 58.8% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.32 / 1,000 comments

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

### 💬 What is Reddit Comments Scraper?

**Reddit Comments Scraper** extracts **comments from [Reddit](https://www.reddit.com)**: the whole **text**, the **score**, the **author**, the **date**, the **post** it belongs to, how deep it sits in its thread — one row per comment, ready for a spreadsheet, a database or a model. It works as a **Reddit API alternative** for comments: run it on a schedule, call it from your code, or plug it into Make, Zapier or n8n.

Give it the **posts** whose threads you want, a **community** to follow, a **search term**, or a **user**: the sources add up in one run. Click **Start** and download the rows in JSON, CSV or Excel. No login, nothing to install.

### 📋 What data can you extract from Reddit comments?

Every row carries the same **229 fields**, whatever route found it, so a CSV export stays square: a field a row does not have is `null`, never missing. The `dataType` column says what the row is — `comment`, or `post` when you ask for the posts too.

| Category | What you get |
| --- | --- |
| 💬 **Comment** | the whole text, as text and as HTML, its link and its length — "Exactly. Unit tests are done to literally test the modules…" |
| 🧵 **Thread** | the post it belongs to (title, author, link), the comment it answers and how deep it sits: the whole thread in a spreadsheet |
| 👤 **Author** | username, flair, badges, and whether they wrote the post |
| 👍 **Votes** | score, awards, controversy — `61`, `0`, `0` |
| 🕒 **Dates** | written, edited — `2026-09-18T23:19:53.000Z`, what the date filters read |
| 📈 **Computed for you** | age in hours and score per hour |
| 🧭 **How it was found** | thread, link to one comment, live feed of a community, history of a user or search — and the source it came from |

Every field, with what it holds, is listed in the **Output** section below.

### ✅ Why use Reddit Comments Scraper?

- 🧵 **Whole threads by default**: a post's thread is read to its end, replies included, and the comments Reddit folds behind "load more comments" are unfolded too. `postId`, `parentId`, `parentKind`, `depth` and `replyCount` rebuild the tree in any spreadsheet.
- 🧩 **Five ways in, one run**: post links (or a link to one comment), a community's live comments, the threads of its posts, a search term, a user's history — they add up. No mode to choose, no second run to launch.
- 🎯 **Filters made for comments**: date of the comment (not of the post), words, authors, score, only the post author's comments, and the top-level comments the post author did or did not answer — to find the questions that stayed open.
- 🧹 **No dead weight**: deleted and removed comments are neither written nor charged by default, and a row a filter drops is never charged as a comment.
- 🔔 **Monitoring built in**: tick **New comments only**, schedule the Actor, and each run returns — and charges — only the comments it has never delivered, on every source.
- 🔌 API, scheduling, webhooks and integrations (Make, Zapier, n8n, Google Sheets, Slack, Airtable…), plus JSON/CSV/Excel export, through the Apify platform.

### 🚀 How to scrape Reddit comments

1. Create a free Apify account.
2. Open **Reddit Comments Scraper** and paste the posts you want in **Post and comment links**, or name a community in **Communities** (for example `programming`), or type a **Search term**, or give a user in **Users**.
3. Or paste your own Reddit URLs into **Any Reddit URLs**: a post, a link to one comment, a community, a user profile or a search page. A text file with one link per line loads a long list at once.
4. Empty **Search term** in the Console unless a search is what you want: the form opens with `web scraping` in it (an example), and that search would run next to your other sources, charged like any comment.
5. Set **Maximum results** (100 by default, 0 = no limit), then click **Start**.
6. Download the dataset in JSON, CSV, Excel or through the API.

### 💰 How much does it cost to scrape Reddit comments?

This Actor uses **pay per event** pricing: you pay for the rows you get, and only for those. The Pricing tab shows the price of each event:

- `comment-scraped`: one event per comment row written. A comment that a filter drops, a deleted or removed comment (by default) and a comment already delivered (**New comments only**) are never written, so never charged.
- `post-scraped`: one event per post row, only when you tick **Include posts**.
- A small start fee per run.
- `filter-check`: the filters you set (`strictSearch` included) are applied by the Actor on every comment it reads. Only a comment the filters DROP is charged a small filter fee, on every source (threads, comment search, community feeds, account histories): a comment they keep costs its comment row alone. The filters every run applies — adult content left out and deleted or removed comments left out — are free, and so is **Minimum comments of a post**, which only spares the reading of a thread.
- `residential-fallback`: if Reddit turns away the default connection, the run moves to another one on its own, and each request answered that way is charged a small fee — a request Reddit refuses is never charged. If Reddit refuses every connection the run can use without ever answering, it stops within a few minutes instead of retrying for a long time, and returns what it already saved.

Platform usage (compute, proxy) is included in the price.

### ⚙️ Input

Follow the live comments of a community (empty **Search term** in the Console first):

```json
{
    "subreddits": ["programming"],
    "maxItems": 200
}
```

The threads of the latest posts of two communities, only the questions their authors never answered, only what is recent (empty **Search term** in the Console first):

```json
{
    "subreddits": ["programming", "rust"],
    "subredditSource": "postThreads",
    "maxThreadsPerSubreddit": 25,
    "minComments": 5,
    "opReplyFilter": "unanswered",
    "postedAfter": "7 days",
    "maxItemsPerQuery": 500
}
```

Search inside the comments of all Reddit, three orders added together:

```json
{
    "query": "rust async",
    "searchSorts": ["new", "relevance", "top"],
    "includeKeywords": ["benchmark"],
    "maxItems": 500
}
```

Or your own posts, with the post rows next to their comments (empty **Search term** in the Console first):

```json
{
    "postUrls": [
        "https://www.reddit.com/r/programming/comments/1wjjqc7/",
        "t3_1wjjqc7"
    ],
    "includePosts": true,
    "maxCommentDepth": 3
}
```

Only the new comments, every day (empty **Search term** in the Console first):

```json
{
    "subreddits": ["rust"],
    "onlyNew": true,
    "stateKey": "rust-daily"
}
```

Give at least one source: a post, a Reddit URL, a community, a search term or a user. With none, the Actor runs an example instead of stopping — a search for "web scraping", at most 20 rows — and says so in the log, unless `authors` or `opReplyFilter` is set: such a run is refused, with a message that asks for a source.

| Field | Notes |
| --- | --- |
| `postUrls` | Posts whose comments you want, one per line: a post link, for example `https://www.reddit.com/r/programming/comments/1wjjqc7/`, a short redd.it link, a bare post id such as `t3_1wjjqc7`, or the link of a single comment (that comment and its replies). Each post is read as a full thread. |
| `startUrls` | Any Reddit URL, or a text file with one link per line: a post, a community such as `https://www.reddit.com/r/programming/`, a user profile such as `https://www.reddit.com/user/spez/` or a search page such as `https://www.reddit.com/search?q=tokio` (read as the same comment search as the `query` field: its term, its community and the order it carries; a page that asks for posts or people is read as a comment search all the same). |
| `query`, `searchQueries`, `strictSearch` | Search INSIDE the comments of all Reddit for a word or phrase, for example `rust async`. `searchQueries` adds more terms, one search each; every term is searched in every community (max 500 searches per run, search pages pasted in `startUrls` included). `strictSearch` keeps a comment only if its text holds the whole phrase, case and accents ignored — Reddit also matches the words apart; a comment without the phrase is dropped like any filtered comment. |
| `searchSorts` | Reddit stops one comment search after about 250 results for each term and order. Tick several orders to get more: their results are added together and each comment is kept once. The orders are `new`, `relevance` and `top`. |
| `subreddits` | Community names without the r/ prefix, for example `programming`. With a search term, each community is searched on its own (max 500 searches per run). Without one, the comments are read the way the next row says. |
| `subredditSource`, `maxThreadsPerSubreddit` | Only for communities given without a search term. `latestComments` reads the live comment feed of each community (Reddit keeps about the last 1,000); `postThreads` opens the threads of the posts picked by the post sort and the time window, and reads them in full, up to `maxThreadsPerSubreddit` posts per community. |
| `sort` | Which posts of a community have their thread read: `new`, `hot`, `top`, `rising` or `controversial`. A sort carried by a pasted community URL wins over this setting. |
| `timeFilter` | The window of the Top and Controversial post sorts: `hour`, `day`, `week`, `month`, `year` or `all`. To keep the comments of a given period, use the date filters below. |
| `minComments` | With the threads of posts: skip the posts that have fewer comments than this, before their thread is read. 0 keeps every post. |
| `usernames` | Accounts whose comment history you want, newest first (Reddit keeps about the last 1,000), without the u/ prefix, for example `spez`. |
| `maxItems`, `maxItemsPerQuery` | Stop after this many rows for the whole run, post rows included, and for each source separately — a post, a search term in each community, a community, a user, a pasted URL. 0 means no limit. |
| `maxCommentsPerPost`, `maxCommentDepth` | Highest number of comments read from one post, replies included (0, the default, reads the whole thread), and how deep into the replies to go. |
| `commentSort` | The order a post's comments are read in: `top`, `confidence`, `new`, `controversial`, `old` or `qa` (for AMAs). It decides which comments you get when a cap cuts the thread short: the first ones in the order Reddit shows them — with `new`, the top-level comments newest first, each followed by its replies, so a recent reply under an old comment comes after that comment, not before the next one. |
| `expandMoreComments`, `includePosts` | Whether to unfold the comments Reddit folds behind "load more comments" (on by default: one more request per group of about 10 of them, up to 200 per post), and whether to add one row per post whose thread is read, next to its comments (off by default, charged on its own). |
| `postedAfter`, `postedBefore` | The date of the COMMENT, not of its post: a date such as `2026-09-01`, or a period before now such as `7 days` or `24 hours`. |
| `includeKeywords`, `excludeKeywords` | Keep a comment only if its text holds one of these words, or drop it if it holds one. Case, accents and invisible characters are ignored, so `benchmark` matches a text written any way. |
| `authors`, `excludeAuthors` | Keep only the comments written by these accounts, for example `spez`, or drop the ones written by them, for example `AutoModerator`. |
| `includeNsfw`, `skipRemoved`, `onlySubmitter` | Adult content is left out by default. Deleted and removed comments are dropped by default: they carry no text and are neither written nor charged. Only the post's author keeps the comments written by the author of the post they belong to. |
| `opReplyFilter` | Judged on the top-level comments of a thread: `answered` keeps those the post's author replied to, `unanswered` keeps those that got no reply from them, `any` judges nothing. It needs a thread, so a live feed, a history, a comment search or a link to one comment cannot be judged and is left out of such a run. |
| `minScore` | Keep only the comments whose score reaches this number, for example `1`. 0, the default, turns the filter off: comments scored below 0 are kept too. |
| `onlyNew`, `stateKey`, `resetState` | Monitoring: only the comments never delivered under this memory key, for example `rust-daily`. `resetState` forgets the memory once. |
| `customLabels` | Free key/value pairs copied onto every row, so rows from several runs can be told apart in one spreadsheet. |
| `proxyConfiguration`, `maxConcurrency`, `maxRequestsPerMinute`, `minRequestIntervalMs`, `maxRequestRetries`, `debugLog` | Advanced. The proxy is included in the price: leave the default (the residential proxy is not available). The four numbers pace the run, and the last switch makes the log verbose. |

### 📦 Output

One row per comment, and one per post when you tick **Include posts**. Here is a real comment row, shortened to the columns most people use — the run returns all 229:

```json
{
    "dataType": "comment",
    "id": "pao4nll",
    "fullId": "t1_pao4nll",
    "url": "https://www.reddit.com/r/programming/comments/1wk4lod/comment/pao4nll/",
    "postUrl": "https://www.reddit.com/r/programming/comments/1wk4lod/",
    "body": "Exactly. Unit tests are done to literally test the modules and interfaces of our code in units and make sure that they function as intended with external dependencies patched or mocked out. Not for some arbitrary feeling of random unit tests granting confidence to code changes.",
    "postTitle": "\"the main purpose of unit tests is keeping your code from being violently sh*t on by others\"",
    "postAuthor": "mareek",
    "author": "Glizzy_Cannon",
    "isSubmitter": false,
    "subreddit": "programming",
    "subredditPrefixed": "r/programming",
    "score": 61,
    "totalAwards": 0,
    "ageHours": 300.67,
    "scorePerHour": 0.2,
    "wordCount": 46,
    "isNsfw": false,
    "postId": "1wk4lod",
    "parentId": "t1_panzho7",
    "parentKind": "comment",
    "depth": 1,
    "images": [],
    "publishedAt": "2026-09-18T23:19:53.000Z",
    "isEdited": false,
    "sourceUrl": "https://www.reddit.com/r/programming/comments/1wk4lod/",
    "source": "thread",
    "scrapedAt": "2026-10-01T12:00:00.000Z"
}
```

You can download the dataset in various formats such as JSON, HTML, CSV or Excel.

#### All 229 fields

| Fields | What they give you |
| --- | --- |
| `dataType`, `id`, `fullId`, `url`, `permalink`, `postUrl`, `postId`, `parentId`, `parentKind`, `depth`, `replyCount` | **Identity and links.** The row kind (comment, or post with Include posts), its Reddit ids and the pages a human can open. A comment also carries the post it belongs to, the comment it answers, how deep it sits and how many direct replies it got (empty when the comment was read outside its thread). |
| `title`, `body`, `bodyHtml`, `postTitle`, `postAuthor`, `postScore`, `titleLength`, `bodyLength`, `wordCount` | **Text.** The whole text of the comment as written on Reddit (Markdown, line breaks and code indentation kept) and as HTML, with its length and word count — and the title, author and score of its post, so a row reads on its own. |
| `author`, `authorId`, `authorFlair`, `authorFlairBackgroundColor`, `authorFlairCssClass`, `authorFlairTextColor`, `authorFlairType`, `authorFlairRichtext`, `authorFlairTemplateId`, `authorPremium`, `authorPatreonFlair`, `authorIsBlocked`, `isSubmitter`, `distinguished` | **Author.** Who wrote it, their account id, their flair in that community and the badges Reddit shows next to the name. `isSubmitter` is true when the author of the comment is the author of the post. |
| `subreddit`, `subredditPrefixed`, `subredditId`, `subredditSubscribers`, `subredditType` | **Community of the row.** Which community the row comes from, its id, its member count at the time of the run and whether it is public or restricted. |
| `score`, `upVotes`, `downVotes`, `upvoteRatio`, `scoreHidden`, `numComments`, `numCrossposts`, `numDuplicates`, `controversiality`, `viewCount` | **Votes.** The score and the votes Reddit publishes. A fresh comment in a community that hides scores has no score yet (`scoreHidden` says so) rather than the 1 Reddit puts in its place. |
| `totalAwards`, `gilded`, `awards`, `allAwardings`, `topAwardedType` | **Awards.** How many awards the row got, which ones, and the raw award objects for anything else. |
| `aiAnalysis`, `aiLabelMatches`, `ageHours`, `scorePerHour`, `commentsPerHour`, `engagementTotal`, `commentToScoreRatio`, `outboundUrlHost` | **Computed for you.** Figures Reddit does not give: the age in hours and the score per hour of the comment. |
| `flair`, `flairBackgroundColor`, `flairCssClass`, `flairTextColor`, `flairType`, `flairRichtext`, `flairTemplateId`, `postType`, `contentCategories`, `discussionType`, `suggestedSort` | **Flair and kind of post.** What the post of a thread row is: text, link, image, gallery, video or poll, and its flair with its colours. |
| `isNsfw`, `isSpoiler`, `isStickied`, `isLocked`, `isArchived`, `isHidden`, `isQuarantined`, `isOriginalContent`, `isSelf`, `isVideo`, `isGallery`, `isMeta`, `isCrosspostable`, `isRobotIndexable`, `isCreatedFromAdsUi`, `contestMode`, `allowLiveComments`, `mediaOnly`, `sendReplies`, `noFollow`, `pinned`, `collapsed` | **Flags.** Every true or false Reddit publishes on a row: adult content, pinned, locked, archived, collapsed, and the rest. |
| `collapsedReason`, `unrepliableReason`, `removedByCategory`, `removalReason`, `banReason`, `bannedBy`, `approvedBy`, `modNote`, `modReasonTitle`, `whitelistStatus`, `treatmentTags` | **Moderation.** Why a row was collapsed, removed or banned, and by whom, when Reddit says so publicly. |
| `domain`, `linkUrl`, `urlOverriddenByDest`, `thumbnail`, `images`, `galleryCount`, `videoUrl`, `videoDuration`, `hasMedia`, `mediaType`, `media`, `secureMedia`, `mediaMetadata`, `galleryData`, `preview`, `postHint`, `crosspostParent` | **Link and media of the post.** On a post row: the outbound link and its domain, the thumbnail, every image, the hosted video, and the raw media objects. Empty on a comment. |
| `publishedAt`, `editedAt`, `isEdited`, `createdAt` | **Dates.** When it was written and when it was last edited, always ISO 8601 in UTC. These are the dates the date filters read. |
| `source`, `sourceUrl`, `searchTerm`, `rank`, `customLabels`, `scrapedAt` | **How it was found.** `source` says which route read the comment (`thread`, `permalink`, `communityFeed`, `user` or `search`), `sourceUrl` the source it came from, `searchTerm` the term of a search, your own labels, and the moment it was read. |
| `name`, `displayName`, `description`, `descriptionHtml`, `publicDescription`, `publicDescriptionHtml`, `membersCount`, `communityIcon`, `bannerImage`, `headerTitle`, `primaryColor`, `lang`, `advertiserCategory`, `submissionType`, `submitText`, `suggestedCommentSort`, `allowImages`, `allowVideos`, `allowVideogifs`, `allowGalleries`, `allowPolls`, `allowDiscovery`, `spoilersEnabled`, `linkFlairEnabled`, `originalContentTagEnabled`, `wikiEnabled`, `restrictPosting`, `restrictCommenting`, `acceptFollowers`, `allOriginalContent`, `commentScoreHideMins`, `rules`, `ruleDetails`, `wikiPageNames`, `wikiPages`, `stickiedPosts`, `similarCommunities`, `username`, `totalKarma`, `linkKarma`, `commentKarma`, `awardeeKarma`, `awarderKarma`, `isGold`, `isMod`, `isEmployee`, `isVerified`, `hasVerifiedEmail`, `hideFromRobots`, `iconImg`, `snoovatarImg`, `profileTitle`, `profileDescription`, `headerImg`, `keyColor`, `bannerBackgroundImage`, `mobileBannerImage`, `emojisEnabled`, `previousNames`, `isSuspended`, `trophies`, `profileUrl`, `bio`, `isCakeDay`, `hasSubscribed`, `allowPredictions`, `allowPredictionContributors`, `allowPredictionsTournament`, `allowTalks`, `allowedMediaInComments`, `commentContributionSettings`, `bannerBackgroundColor`, `bannerSize`, `headerSize`, `iconSize`, `emojisCustomSize`, `collapseDeletedComments`, `communityReviewed`, `disableContributorRequests`, `freeFormReports`, `hasMenuWidget`, `hideAds`, `isCrosspostableSubreddit`, `publicTraffic`, `shouldArchivePosts`, `shouldShowMediaInCommentsSetting`, `showMedia`, `showMediaPreview`, `userFlairEnabledInSr`, `canAssignLinkFlair`, `canAssignUserFlair`, `linkFlairPosition`, `userFlairPosition`, `submitLinkLabel`, `submitTextLabel`, `submitTextHtml` | **Community and profile columns.** The columns that describe a whole community (description, members, icons, settings, rules) or a user profile (karma, badges, bio). A row of this Actor is a comment or a post, so they stay `null`: they are there so that every row, here and in other Reddit tools, keeps the same shape. |

### 💡 Tips

#### How to get more results

Reddit stops any single list at about 1,000 comments, and a comment search at about 250 per term and order, whatever the cap you set. To go past it, split the run: several communities instead of one, several search terms, the three search orders added together, the threads of posts (`postThreads`) instead of the live feed, or a longer `postedAfter` window read in several runs. Set **Maximum results** (100 by default, 0 = no limit) to take everything a source has.

#### How to reduce costs

You pay per comment, so the levers are the caps (`maxItems`, `maxItemsPerQuery`, `maxCommentsPerPost`), the filters — a comment a filter drops costs only its small filter fee, a comment it keeps costs none — and `onlyNew` for recurring runs, which never charges the same comment twice. The post rows are charged on their own: leave **Include posts** off when you only want the comments.

#### The comment tree, in a spreadsheet

Each comment is a row of its own. `postId` says which post it belongs to, `parentId` and `parentKind` say what it answers (the post itself, or another comment), `depth` says how deep it sits and `replyCount` how many direct replies it got. Sort by `postId` then by the order the rows came in and you have the thread as it reads on the site. Use `maxCommentDepth` to keep only the top-level answers, and `maxCommentsPerPost` to bound the cost of a very busy thread. A comment read in a live feed or in a user's history comes without its thread: its `depth` is 0 when it answers the post and empty otherwise, and its `replyCount` is empty.

#### Which comments the post author answered

Set **Replies from the post's author** (`opReplyFilter`) to `unanswered` to list the questions that stayed open, or to `answered` to read how an author reacted. The Actor reads each thread to its full depth to judge, whatever `maxCommentDepth` says, and writes the top-level comments only; `maxCommentsPerPost` then caps the rows written.

#### Monitoring: only the new comments

Tick **New comments only** (`onlyNew`) and schedule the Actor. The first run returns everything; each later run skips what it has already delivered: those rows are not saved and not charged. The memory lives in a named key-value store of your account (`reddit-comments-scraper-seen`, up to 150,000 rows per key) and is only updated with rows that really reached the dataset, so a failed run never hides anything. Give each schedule its own `stateKey` — two schedules sharing a key would hide each other's rows — and tick `resetState` once to start over. A community's comment feed and a user's history come newest first: with `onlyNew` the run stops once it meets 200 rows in a row that you already have (fewer when **Maximum results** is lower, or when the earlier runs delivered fewer rows from that source: 80 % of that number, met anywhere in the run). A comment your filters dropped is remembered too, on every source (same filters, same `stateKey`): the next runs neither check nor charge it again. Not remembered, because they can pass later or for another term: a comment dropped for its score, for `postedBefore`, by `strictSearch` or by the answered-by-the-author filter. The threads of posts are always read in full, so a comment written under an old post is found.

#### Filter by the date of the comment

`postedAfter` and `postedBefore` take a date (`2026-09-01`, the whole day included, UTC) or a period before now (`7 days`, `2 weeks`, `1 month`, `24 hours`). They read the date of the COMMENT, so an old post still gives its recent comments; a comment without a date is dropped as soon as a bound is set. Filtered-out comments are not saved and do not count in `maxItems` (the filter fee, see Pricing), and the run summary says how many there were. With `postedAfter`, a comment feed or a history stops at the first page that is entirely too old, and so does a comment search read in the `new` order, on the date each result prints on the search page; the `relevance` and `top` orders are not sorted by date and are read to their end. One recent comment on a page is enough to keep reading.

### 🔌 Integrations and API

Call the Actor through the Apify API, the JavaScript or Python clients, or connect it with integrations and webhooks (Make, Zapier, n8n, Google Sheets, Slack, Airtable…). The dataset can be fetched as JSON or CSV from any tool.

### 🤖 Use with AI agents (MCP)

AI agents (Claude, ChatGPT, Cursor…) can find and run this Actor through the [Apify MCP server](https://mcp.apify.com), billed to their Apify account like any run. It returns one item per Reddit comment (`dataType` says `comment`, or `post` with Include posts). Actor id: `nice_dev/reddit-comments-scraper`; MCP server with this Actor only: `https://mcp.apify.com/?tools=fetch-actor-details,nice_dev/reddit-comments-scraper`.

Smallest input, for a cheap first call (empty **Search term** in the Console first; a call through the API or MCP does not carry it):

```json
{
    "subreddits": ["programming"],
    "maxItems": 10
}
```

Key output fields: `dataType`, `url`, `body`, `author`, `subreddit`, `score`, `publishedAt`, `postTitle`, `postId`, `depth` and `source`.

Cost: one event per comment (and per post row with Include posts), plus a small start fee; the filters you set, the fallback connection and the posts cost extra as the pricing section above says. Cap each call with `maxItems` and, through the API, with the run option `maxTotalChargeUsd`.

### ❓ FAQ

#### Is it legal to scrape Reddit comments?

The Actor only reads what Reddit shows publicly to any anonymous visitor. It logs in to nothing, uses no account, and solves no captcha. Results contain content written by people under a pseudonym — personal data protected by GDPR: do not store it without a legitimate reason, and keep in mind that a pseudonym can still identify someone. You are responsible for using the data in compliance with Reddit's Terms of Use, its User Agreement and applicable law — in particular if you train a model on it or republish it. This Actor is not affiliated with Reddit.

#### Does it need a login or a proxy?

No login and no account. The proxy is included in the price: leave the default setting (the residential proxy is not available). A request Reddit turns away moves the run to a new IP with a new session and is tried again at once, without using up its retries (10 times at most per request), as long as the run has an IP left: 8 in all with the default setting, 10 with your own proxies. Past them, a run whose last connection never got an answer stops, with what it already saved; a connection that worked and is refused later, or a run without a proxy, waits before each retry: 5 seconds, doubled at each try, 255 seconds at most.

#### Is the data safe to open in Excel or to show on a web page?

Comments are what the authors wrote, copied as they are. A text can begin with `-`, `+`, `=` or `@`: Excel and Google Sheets may read such a cell of a CSV file as a formula or as a number. The Actor leaves the text as it is, so that the JSON and the API give the real value — when you open a CSV, import these columns as text. Every URL column holds an http(s) address or `null`, never anything a browser would execute. `bodyHtml` holds the site's own HTML, which the Actor does not sanitize: escape every field like any text written by a stranger before you put it on a web page.

#### Known limitations

- Reddit stops any single list at about 1,000 comments and a comment search at about 250 per term and order. The **Tips** section says how to go past it.
- The thread of a very busy post is cut by `maxCommentsPerPost` and `maxCommentDepth`. The "load more comments" placeholders are followed with **Unfold hidden comments**, up to 200 requests (about 1,000 folded comments) per post, as long as the post's own time allows; "continue this thread" links are not.
- A comment found in a live feed or a history comes without its thread: no reply count, and no depth unless it answers the post. The post's score and the community's member count are empty on such a comment.
- A comment found by a search comes without its thread too (no reply count, and no depth unless it answers the post), but with its post's title, link, author and score — empty when Reddit has removed the post since.
- `onlyNew` remembers row ids, not their content: a comment whose score changed is not returned again.
- Two runs sharing the same `stateKey` at the same time may both return the same new comment.
- A deleted account is written as `[deleted]` and a removed text as `[removed]`: that is what Reddit itself serves. They are dropped by default.

**A run that reaches its timeout** stops itself about 45 seconds before it: no new page is asked, what it read is saved and, with `onlyNew`, remembered, and the run ends *Succeeded* with "Stopped before the run's timeout". Resurrect it to go on from there, or give the next run a longer timeout (Run options).

**A run the platform stops without warning** (out of memory)

- Resurrect it: it goes on from where it stood at most a minute before the stop. What it had read since is read again, and the rows already saved are skipped: none is delivered or charged twice, and `maxItems` still counts them.
- With `onlyNew`, the memory is saved after each write (at most once every 2 seconds): resurrect the stopped run and the rows it had saved meanwhile join the memory; leave it stopped for good, and the next run may return the rows of its last 2 seconds once more.

#### Something doesn't work?

The last line of the log counts the comments saved, the comments filtered out, the posts no longer on Reddit (deleted while the run was reading them) and the requests that failed after every retry. Those requests and the deleted posts are listed, with the reason, in the `FAILED_REQUESTS` record of the run's key-value store. A run that saved nothing and had failed requests fails, and its last message gives the cause. A source Reddit does not serve — a private, members-only, quarantined or banned community, a suspended account — is skipped with a warning in the log, never a failed request: the run goes on with the other sources and the last line says "1 source of the input not found (see the log)" ("2 sources…" and so on); when it is the only source the run ends *Succeeded* and its last message names the cause. Without a proxy, when Reddit refuses the Actor's own address, the last message says so and asks you to keep the proxy on instead of "run it again". A community name that does not exist is answered by Reddit with an empty page: the run then ends *Succeeded* with "No comments found — 0 comments saved — …" ("No new or matching comments" when your filters or **New comments only** left nothing), so check the spelling.

If Reddit changes its pages, you are told instead of paying for blank rows. A page that counts rows but gives none the Actor can read is an error (listed in `FAILED_REQUESTS`), never a quiet "No comments found". If the first 20 rows read all lack their author, their community, their publication date or, for a comment, its text (or, for a post, its score), the run saves nothing more, stops and fails, and its last message names the missing field: at most those first rows are charged. A comment that `postedAfter` / `postedBefore` drops because it has no date at all counts among those 20.

### 🛟 Support

Open an issue in the **Issues** tab with a link to your run: the run log and the `FAILED_REQUESTS` record of the key-value store show exactly which URLs failed and why.

# Actor input Schema

## `postUrls` (type: `array`):

Reddit posts whose comments you want, one per line: a post link (`https://www.reddit.com/r/AskReddit/comments/1wrj0s2/`), a short `redd.it` link, a bare post id (`t3_1wrj0s2` or `1wrj0s2`), or the link of a single comment (that comment and its replies). Each post is read as a full thread, replies included. Max 1 000 links.

## `startUrls` (type: `array`):

Paste any Reddit URL, or upload a text file with one link per line to load a long list at once: a post (its comments), a community (`https://www.reddit.com/r/programming/`), a user profile (`https://www.reddit.com/user/spez/`) or a search page (its search term is searched in the comments). Max 1 000 URLs.

## `query` (type: `string`):

Search INSIDE the comments of all Reddit for this word or phrase, for example `web scraping` or `rust async`. Combined with Communities, it searches inside those communities only.

## `searchQueries` (type: `array`):

Several search terms in one run, added to the single Search term above. Every term is crossed with every community, up to 500 searches per run.

## `searchSorts` (type: `array`):

Reddit stops one comment search after about 250 results for each term and order. Tick several orders to get more: their results are added together and each comment is kept once, which gives up to about 750 comments per term with the three orders (578 comments on a very active term: the orders share many).

## `strictSearch` (type: `boolean`):

Require the whole search term to appear in the comment as written, instead of letting Reddit match on separate words.

## `subreddits` (type: `array`):

Community names without the r/ prefix, for example `programming` or `MachineLearning`. With a search term, each community is searched on its own (max 500 searches per run). Without one, the comments are read the way "Comments of a community" below says.

## `subredditSource` (type: `string`):

Only used for Communities given without a search term. Latest comments reads the live comment feed of each community (Reddit keeps about the last 1 000). Comments of its posts opens the threads of the posts picked by Sort and Time window and reads them in full.

## `sort` (type: `string`):

Only used with "Comments of its posts": which posts of each community have their thread read. A sort carried by a pasted community URL wins over this setting.

## `timeFilter` (type: `string`):

Only used with "Comments of its posts" and the Top or Controversial post sort. To keep comments of a given period, use the Posted after / Posted before filters below.

## `maxThreadsPerSubreddit` (type: `integer`):

Only used with "Comments of its posts": how many posts of each community have their thread read.

## `minComments` (type: `integer`):

Only used with "Comments of its posts": skip posts with fewer comments than this. 0 keeps every post.

## `usernames` (type: `array`):

Reddit usernames without the u/ prefix: the comment history of each account, newest first (Reddit keeps about the last 1 000). For example `spez` — `u/spez` and a full profile URL are accepted too.

## `maxItems` (type: `integer`):

Stop the run after this many rows in total, comments and post rows together. 0 means no limit.

## `maxItemsPerQuery` (type: `integer`):

Cap applied to each source separately — a post, a search term in each community, a community, a user, a pasted URL — so one busy source cannot eat the whole run. 0 means no per-source cap.

## `maxCommentsPerPost` (type: `integer`):

Highest number of comments collected from a single post, replies included. 0, the default, reads the whole thread.

## `maxCommentDepth` (type: `integer`):

How deep into the reply chains to go. 1 keeps only top-level comments; 10, the default, follows almost every thread to the end.

## `expandMoreComments` (type: `boolean`):

Also read the comments Reddit folds behind « load more comments », so a big thread comes out complete (one more request per group of about 10 of them, up to 200 per post, within the comments per post above). Turn off for a faster run that skips the folded part.

## `commentSort` (type: `string`):

Order in which a post's comments are read. This decides which comments you get when Maximum comments per post or Maximum results cuts the thread short. Q\&A suits AMAs.

## `includePosts` (type: `boolean`):

Add one row for each post whose thread is read, next to its comments (dataType post). Every comment already carries its post's title, author, score and link, so leave this off unless you want the post rows themselves. Post rows count in Maximum results and are charged on their own (post-scraped, see Pricing).

## `includeNsfw` (type: `boolean`):

Keep comments from communities and posts marked NSFW. Off by default.

## `postedAfter` (type: `string`):

Keep only comments written after this moment, read from the comment's own creation date. Absolute (2026-09-01 or a full ISO timestamp) or relative (7 days, 24 hours, 2 weeks). A date such as `2026-09-01`, or a period before now such as `7 days` or `24 hours`.

## `postedBefore` (type: `string`):

Keep only comments written before this moment, read from the comment's own creation date. Same formats as Posted after; a date alone keeps that whole day (UTC): `2026-09-30` keeps what was written on the 30th. A period before now such as `2 days` counts back from the start of the run.

## `includeKeywords` (type: `array`):

Keep a comment only if its text contains at least one of these words. Case is ignored. For example `benchmark`.

## `excludeKeywords` (type: `array`):

Drop a comment when its text contains any of these words. Case is ignored. For example `giveaway` or `upvote this`.

## `minScore` (type: `integer`):

Keep only comments whose score reaches this number. 0, the default, turns the filter off: comments scored below 0 are kept too. Set 1 to keep only what scores 1 or more; scores can be negative.

## `authors` (type: `array`):

Keep only comments written by these usernames, without the u/ prefix. Leave empty to keep every author. For example `spez`.

## `excludeAuthors` (type: `array`):

Usernames to skip, without the u/ prefix. Useful to drop bots and moderators. For example `AutoModerator`.

## `onlySubmitter` (type: `boolean`):

Keep only the comments written by the author of the post they belong to, for example the answers of an AMA host.

## `opReplyFilter` (type: `string`):

Judged on top-level comments, from the replies of the thread: Answered keeps the top-level comments the author of the post replied to, Not answered keeps those that got no reply from them. Useful to find questions that stayed open.

## `skipRemoved` (type: `boolean`):

Drop comments whose text is \[deleted] or \[removed]: they carry no text, so they are not saved and not charged (a free filter, no extra charge). Turn off to keep them with their metadata.

## `onlyNew` (type: `boolean`):

Return only comments this Actor has never returned before under the same state key, whatever the source. Ideal for a scheduled run that should not repeat yesterday's comments. For a feed or a search read newest first, the run stops early once it meets a long run of comments already returned.

## `stateKey` (type: `string`):

Name of the memory used by New comments only. Use a different key for each monitoring job so they do not share their history. For example `rust-daily`.

## `resetState` (type: `boolean`):

Wipe the memory of this state key before starting, so everything counts as new again.

## `customLabels` (type: `object`):

Free key/value pairs copied onto every row, for example {"campaign": "q4-launch"}. Handy to tell several scheduled runs apart in one dataset.

## `proxyConfiguration` (type: `object`):

Leave as is. The Actor picks the right network itself and falls back automatically when a request is refused. The residential proxy is not available in this Actor: a run that asks for it stops at once.

## `maxConcurrency` (type: `integer`):

How many requests run at the same time. The pace of a run is set by "Min delay between requests" (2500 ms by default, one request every 2.5 seconds whatever this number is): raising this one alone does not make a run finish sooner, lower the delay too. More requests at the same time increase the chance of being throttled.

## `maxRequestsPerMinute` (type: `integer`):

Overall speed limit. The default is a comfortable pace; lower it if you run several jobs against Reddit at once.

## `minRequestIntervalMs` (type: `integer`):

Smallest gap between two requests, in milliseconds. Unlike the per-minute rate, this spreads the requests evenly instead of letting them go out in a burst. 2500 = Reddit's sustained quota of 24 requests a minute; 0 = no gap.

## `maxRequestRetries` (type: `integer`):

How many times a failed request is tried again before the Actor gives up on it. A request Reddit turns away is first tried again on a new IP without using up these retries, up to 10 times, as long as the run has an IP left (8 in all with the default proxy, 10 with your own).

## `debugLog` (type: `boolean`):

Write every request and every filtered comment to the log. Turn on only when something looks wrong.

## Actor input object example

```json
{
  "postUrls": [],
  "startUrls": [],
  "query": "web scraping",
  "searchQueries": [],
  "searchSorts": [
    "new",
    "relevance",
    "top"
  ],
  "strictSearch": false,
  "subreddits": [],
  "subredditSource": "latestComments",
  "sort": "new",
  "timeFilter": "all",
  "maxThreadsPerSubreddit": 25,
  "minComments": 0,
  "usernames": [],
  "maxItems": 100,
  "maxItemsPerQuery": 0,
  "maxCommentsPerPost": 0,
  "maxCommentDepth": 10,
  "expandMoreComments": true,
  "commentSort": "top",
  "includePosts": false,
  "includeNsfw": false,
  "includeKeywords": [],
  "excludeKeywords": [],
  "minScore": 0,
  "authors": [],
  "excludeAuthors": [],
  "onlySubmitter": false,
  "opReplyFilter": "any",
  "skipRemoved": true,
  "onlyNew": false,
  "stateKey": "default",
  "resetState": false,
  "customLabels": {},
  "proxyConfiguration": {
    "useApifyProxy": true
  },
  "maxConcurrency": 2,
  "maxRequestsPerMinute": 24,
  "minRequestIntervalMs": 2500,
  "maxRequestRetries": 5,
  "debugLog": false
}
```

# Actor output Schema

## `results` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "query": "web scraping",
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("nice_dev/reddit-comments-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "query": "web scraping",
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("nice_dev/reddit-comments-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "query": "web scraping",
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call nice_dev/reddit-comments-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,nice_dev/reddit-comments-scraper"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/XDGbtCnTEwbnkvAhd/builds/Te08eD9cO1hOJxwua/openapi.json
