# Reddit Scraper - Posts, Comments, Subreddits & Users (`kaix/reddit-scraper`) Actor

🔥 ~$0.02/1K results 🔥 Scrape Reddit without an account. Search posts, pull a subreddit or user feed, fetch full comment trees by URL, or discover communities. Clean structured JSON, no browser, no proxy required.

- **URL**: https://apify.com/kaix/reddit-scraper.md
- **Developed by:** [Kai](https://apify.com/kaix) (community)
- **Features:** Limited permissions
- **Stats:** 29 total users, 22 monthly users, 99.6% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.02 / 1,000 results

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.
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

## Reddit Scraper

Collect Reddit posts, complete comment threads, and subreddit profiles as
structured data. Search by keyword, read a subreddit or user feed, fetch known
posts by URL, or discover communities. Each result is one item in the default
dataset. No Reddit account or API key is needed.

### Use cases

- Brand and product monitoring: find every post and comment that mentions a name, and track score and sentiment over time.
- Market and audience research: read what a community asks, recommends, and complains about.
- Trend and news tracking: pull the top posts of the day, week, or month from the subreddits that matter to you.
- Discussion datasets: build corpora of posts and threaded comments for NLP, classification, or model evaluation.
- Competitive analysis: follow a company's subreddit, its spokespeople, and the threads about its launches.
- Community discovery: find the subreddits for a topic, ranked by size and activity.
- Content research: collect image galleries, hosted video links, and the reactions to them.

### Post, comment, and subreddit data

- Post identity, subreddit and subscriber count, author and flair, title, body in Markdown and optionally HTML, outbound link and domain.
- Post signals: score, upvote ratio, comment, crosspost, and award counts, posting and edit times, moderation state, and status flags such as locked, stickied, archived, spoiler, or over-18.
- Post media: thumbnail with size, preview images, full-size gallery images in order, and Reddit-hosted video with MP4, HLS, and DASH links, duration, dimensions, and an audio flag.
- Comments: author and flair, text in Markdown, score, awards, posting and edit times, depth, permalink, submitter and moderator marks, collapse state, and the post and parent ids that place each comment in its thread.
- Subreddits: names, title, public and full descriptions, subscriber and active user counts, type, submission rules, language, category, icons and banner, and creation time.

### Choose how to collect

| What you have | Set `mode` to | Required input |
|---|---|---|
| A topic or keyword | `search` | `keyword`, plus `subreddit` to stay inside one community |
| A community name | `subreddit` | `subreddit` |
| A username | `user` | `username`, plus `userContent` for posts or comments |
| Post URLs or ids | `post` | `postUrls` |
| A theme to find communities for | `community` | `keyword` |

`sort`, `time`, `maxItems`, and `includeNsfw` shape the list modes. The
leading results follow your `sort` and `time`; later results can be in a
different order. Results do not repeat. The run stops when it reaches
`maxItems` or Reddit has nothing left. Post mode keeps the supplied order and
always includes the comment thread.

### Add comment threads

| Add | Turn on | Control with |
|---|---|---|
| A post's comments | `includeComments` (always on in `post` mode) | `maxCommentsPerPost` and `commentSort` |
| The complete thread, down to the last reply | `maxCommentsPerPost: 0` | `commentSort` |

Each comment is saved as its own item right after its post, with the post id,
the parent id, and the depth, so a thread of any size fits and keeps its shape.
The Actor follows every "load more" and "continue this thread" branch, at any
nesting depth, until `maxCommentsPerPost` is met. With `maxCommentsPerPost: 0`
it collects the whole thread, and the comments reach the dataset while they
load, so a stopped run keeps what it had loaded. A thread with eighteen
thousand comments takes about three minutes. The run log and status message
report progress every ten seconds.

### Choose the data depth

| Mode | Use it when | Added data |
|---|---|---|
| `basic` (default) | You need the core fields and the smallest records. | Every field except the HTML bodies. Images and video are included. |
| `detailed` | You need the rendered HTML as well. | Everything in `basic`, plus `selftextHtml` on posts and `bodyHtml` on comments in a user's comment feed. |

### Collect posts

In Apify Console, select Input, paste an example, and select Run. Open the
default Dataset when the run finishes.

Search Reddit for a topic:

```json
{
  "mode": "search",
  "keyword": "large language models",
  "sort": "top",
  "time": "week",
  "maxItems": 100
}
```

Search inside one subreddit:

```json
{
  "mode": "search",
  "keyword": "fine tuning",
  "subreddit": "MachineLearning",
  "sort": "relevance",
  "maxItems": 50
}
```

Read the top posts of a subreddit this month:

```json
{
  "mode": "subreddit",
  "subreddit": "MachineLearning",
  "sort": "top",
  "time": "month",
  "maxItems": 200
}
```

Monitor new posts with their comments:

```json
{
  "mode": "subreddit",
  "subreddit": "startups",
  "sort": "new",
  "maxItems": 50,
  "includeComments": true,
  "maxCommentsPerPost": 100
}
```

Read a user's posts, or their comments:

```json
{
  "mode": "user",
  "username": "spez",
  "userContent": "submitted",
  "sort": "new",
  "maxItems": 50
}
```

```json
{
  "mode": "user",
  "username": "spez",
  "userContent": "comments",
  "maxItems": 100
}
```

Fetch known posts with their complete threads:

```json
{
  "mode": "post",
  "postUrls": [
    "https://www.reddit.com/r/AskReddit/comments/1w4h5e6/doctors_of_reddit_whats_something_patients/",
    "https://redd.it/1w415xd",
    "t3_1w10tax",
    "1vv2nkh"
  ],
  "maxCommentsPerPost": 0,
  "commentSort": "top"
}
```

Discover subreddits for a theme:

```json
{
  "mode": "community",
  "keyword": "machine learning",
  "maxItems": 25
}
```

### Input

Pick one `mode` and fill its target field. `keyword` drives `search` and
`community`, `subreddit` drives `subreddit` and narrows `search`, `username`
drives `user`, and `postUrls` drives `post`. A missing target stops the run with
a clear message before anything is collected. Fields that do not apply to the
selected mode are ignored.

| Field | Type | Default | Behaviour |
|---|---|---:|---|
| `mode` | enum | `search` | `search`, `subreddit`, `user`, `post`, or `community`. |
| `keyword` | string | empty | Search terms for `search` and `community`. Reddit search syntax such as quoted phrases works. |
| `subreddit` | string | empty | Community name, with or without `r/`. The target of `subreddit` mode and an optional filter for `search`. |
| `username` | string | empty | Reddit username, with or without `u/`. The target of `user` mode. |
| `postUrls` | string\[] | `[]` | Targets of `post` mode: full post URLs, `redd.it` short links, `t3_` fullnames, or bare ids. A post listed more than once is saved once. Unparseable entries are skipped and counted in the log. |
| `sort` | enum | `hot` | `relevance`, `hot`, `top`, `new`, `rising`, or `comments`. The leading results follow this sort; later results can be in a different order. A value the selected mode does not offer falls back to the nearest one: `rising` becomes `hot` in search and in a subreddit, and `relevance` or `comments` become `hot` in a feed. |
| `time` | enum | `all` | `hour`, `day`, `week`, `month`, `year`, or `all`. Applies to `top` and `controversial` feeds and to search. A window narrower than `all` is a filter: every result falls within it. In a subreddit or user feed, `top` for that window then leads in place of `hot`, `new`, or `rising`. |
| `maxItems` | integer | `50` | Maximum posts, comments, or subreddits to save in the list modes. `0` means everything Reddit offers for the target. Results do not repeat. Not used in `post` mode, where the URL list sets the size. |
| `userContent` | enum | `submitted` | `submitted` for a user's posts or `comments` for their comments. Used by `user` mode. |
| `includeNsfw` | boolean | `true` | Keep posts and subreddits marked over-18. Search results and the newest posts of a subreddit do not include over-18 posts, whatever this is set to. |
| `depth` | enum | `basic` | `basic` or `detailed`. See the depth table above. |
| `includeComments` | boolean | `false` | Save each post's comments as their own items, right after the post, in the list modes. Always on in `post` mode. |
| `maxCommentsPerPost` | integer | `50` | Hard cap on comments per post, counting nested replies. `0` collects the complete thread, down to the last reply. |
| `commentSort` | enum | `confidence` | `confidence` (Reddit's "best"), `top`, `new`, `controversial`, `old`, or `qa`. |
| `proxyConfiguration` | object | `{"useApifyProxy":true}` | Connection settings for the run. Apify proxy is on by default. Set `useApifyProxy` to `false` to run without one. |

#### Complete input examples

<details>
<summary>View examples that cover every input field</summary>

Post mode with every comment option and detailed depth:

```json
{
  "mode": "post",
  "postUrls": ["https://www.reddit.com/r/AskReddit/comments/1w4h5e6/doctors_of_reddit_whats_something_patients/"],
  "depth": "detailed",
  "includeComments": true,
  "maxCommentsPerPost": 0,
  "commentSort": "top",
  "includeNsfw": true,
  "proxyConfiguration": { "useApifyProxy": true }
}
```

Search with every list option:

```json
{
  "mode": "search",
  "keyword": "\"open source\" model",
  "subreddit": "MachineLearning",
  "sort": "top",
  "time": "year",
  "maxItems": 500,
  "includeNsfw": false,
  "depth": "basic",
  "includeComments": true,
  "maxCommentsPerPost": 20,
  "commentSort": "confidence"
}
```

User comments and community discovery:

```json
{
  "mode": "user",
  "username": "u/spez",
  "userContent": "comments",
  "sort": "new",
  "maxItems": 200
}
```

```json
{
  "mode": "community",
  "keyword": "photography",
  "maxItems": 100,
  "includeNsfw": false
}
```

</details>

### Output

The default dataset holds one object per collected item, and every item has a
`type` of `post`, `comment`, or `subreddit`. `search`, `subreddit`, `user` with
`submitted`, and `post` mode save posts. When comments are requested, each
comment follows its post as its own item, and its `linkId`, `parentId`, and
`depth` place it in the thread. `user` with `comments` saves comments.
`community` saves subreddits. Download the dataset as JSON, CSV, Excel, XML, or
HTML from the Apify Console or the API. The Console offers a Posts view and a
Comments view of the same dataset.

#### Output at a glance

| Group | Main post fields |
|---|---|
| Identity | `type`, `id`, `fullName`, `permalink`, `url`, `domain`, `isSelf`, `postHint` |
| Community | `subreddit`, `subredditPrefixed`, `subredditId`, `subredditType`, `subredditSubscribers` |
| Author | `author`, `authorFullName`, `authorFlairText`, `authorFlairType`, `authorFlairCssClass`, `authorPremium` |
| Content | `title`, `selftext`, `selftextHtml` |
| Signals | `score`, `upvoteRatio`, `numComments`, `numCrossposts`, `viewCount`, `totalAwardsReceived` |
| Time | `createdUtc`, `created`, `editedUtc`, `edited`, `scrapedAt` |
| Status | `over18`, `spoiler`, `stickied`, `locked`, `archived`, `quarantine`, `contestMode`, `isOriginalContent`, `distinguished`, `removedByCategory` |
| Flair | `linkFlairText`, `linkFlairType`, `linkFlairCssClass`, `linkFlairBackgroundColor`, `linkFlairTextColor` |
| Media | `thumbnail`, `thumbnailWidth`, `thumbnailHeight`, `previewImages`, `galleryImages`, `video`, `isVideo` |
| Crossposts | `crosspostParentId` |

#### Root field types

| JSON type | Post fields |
|---|---|
| string | `type`, `id`, `fullName`, `subreddit`, `subredditPrefixed`, `title`, `permalink`, `scrapedAt` |
| string or `null` | `subredditId`, `subredditType`, `author`, `authorFullName`, `authorFlairText`, `authorFlairType`, `authorFlairCssClass`, `url`, `domain`, `selftext`, `selftextHtml`, `created`, `distinguished`, `removedByCategory`, `linkFlairText`, `linkFlairType`, `linkFlairCssClass`, `linkFlairBackgroundColor`, `linkFlairTextColor`, `thumbnail`, `postHint`, `crosspostParentId` |
| number or `null` | `subredditSubscribers`, `score`, `upvoteRatio`, `numComments`, `numCrossposts`, `viewCount`, `totalAwardsReceived`, `createdUtc`, `editedUtc`, `thumbnailWidth`, `thumbnailHeight` |
| boolean | `authorPremium`, `isSelf`, `edited`, `over18`, `spoiler`, `stickied`, `locked`, `isVideo`, `isOriginalContent`, `contestMode`, `archived`, `quarantine` |
| object\[] | `previewImages`, `galleryImages` |
| object or `null` | `video` |

These unchanged fields are from a recorded r/MachineLearning search result.
This selected example is not a saved dataset item:

```json
{
  "type": "post",
  "id": "1vv2nkh",
  "fullName": "t3_1vv2nkh",
  "subredditPrefixed": "r/MachineLearning",
  "subredditSubscribers": 3069244,
  "title": "I developed my own quantized LLM from scratch, trained on 30B tokens, deploys in 60 MB [R]",
  "author": "Final-Data-1410",
  "isSelf": true,
  "score": 349,
  "upvoteRatio": 0.97,
  "numComments": 53,
  "numCrossposts": 1,
  "created": "2026-08-22T04:39:18.000Z",
  "edited": true,
  "linkFlairText": "Research",
  "scrapedAt": "2026-09-02T08:50:22.558Z"
}
```

#### Selected post fields

<details>
<summary>View the post field example</summary>

This recorded example shows every root key of a text post. The body is
shortened. This is not a saved dataset item.

```json
{
  "type": "post",
  "id": "1vv2nkh",
  "fullName": "t3_1vv2nkh",
  "subreddit": "MachineLearning",
  "subredditPrefixed": "r/MachineLearning",
  "subredditId": "t5_2r3gv",
  "subredditType": "public",
  "subredditSubscribers": 3069244,
  "title": "I developed my own quantized LLM from scratch, trained on 30B tokens, deploys in 60 MB [R]",
  "author": "Final-Data-1410",
  "authorFullName": "t2_2b9ps7jz27",
  "authorFlairText": null,
  "authorFlairType": "text",
  "authorFlairCssClass": null,
  "authorPremium": false,
  "permalink": "https://www.reddit.com/r/MachineLearning/comments/1vv2nkh/i_developed_my_own_quantized_llm_from_scratch/",
  "url": "https://www.reddit.com/r/MachineLearning/comments/1vv2nkh/i_developed_my_own_quantized_llm_from_scratch/",
  "domain": "self.MachineLearning",
  "isSelf": true,
  "selftext": "I trained a 250M parameter model from scratch on 30B tokens of fineweb. It’s quantized to under 2 bits so the whole deployment is 60 MB and it needs about 80 MB of RAM to run. Runs around 400 tok/s on a normal laptop CPU, no GPU needed. [...]",
  "selftextHtml": null,
  "score": 349,
  "upvoteRatio": 0.97,
  "numComments": 53,
  "numCrossposts": 1,
  "viewCount": null,
  "totalAwardsReceived": 0,
  "createdUtc": 1787373558,
  "created": "2026-08-22T04:39:18.000Z",
  "editedUtc": 1787399854,
  "edited": true,
  "over18": false,
  "spoiler": false,
  "stickied": false,
  "locked": false,
  "isVideo": false,
  "isOriginalContent": false,
  "contestMode": false,
  "archived": false,
  "quarantine": false,
  "distinguished": null,
  "removedByCategory": null,
  "linkFlairText": "Research",
  "linkFlairType": "text",
  "linkFlairCssClass": "three",
  "linkFlairBackgroundColor": "#f1f10e",
  "linkFlairTextColor": "dark",
  "thumbnail": "self",
  "thumbnailWidth": null,
  "thumbnailHeight": null,
  "postHint": "self",
  "previewImages": [
    {
      "url": "https://external-preview.redd.it/0yDR7EF5YMkUWu62jbSVy7cz5rmO0ctIyTTIeT0oyO8.png?auto=webp&s=9f0c41004d2648d202f33a11f3145062cb2419c9",
      "width": 1200,
      "height": 600
    }
  ],
  "galleryImages": [],
  "video": null,
  "crosspostParentId": null,
  "scrapedAt": "2026-09-02T08:50:22.558Z"
}
```

</details>

`selftextHtml` is `null` at `basic` depth and holds the rendered HTML at
`detailed` depth. Search results and the newest posts of a subreddit have no
HTML at either depth. `viewCount` is `null` on almost every post, since Reddit
shows it only to the author. `thumbnail` is a URL for link and media posts and
a keyword such as `self` or `default` otherwise.

#### Media fields

Gallery posts list their images in gallery order in `galleryImages`. These
values are from a recorded r/pics gallery, shortened to two of its six images:

```json
{
  "type": "post",
  "id": "1w2q0yb",
  "title": "Food served to US service men & women.",
  "subredditPrefixed": "r/pics",
  "url": "https://www.reddit.com/gallery/1w2q0yb",
  "score": 37841,
  "numComments": 5038,
  "thumbnail": "https://preview.redd.it/4bg1ylvg7kmh1.jpg?width=140&height=78&auto=webp&s=cb10bf62e0355643df0e974b33d2c8c3003bbedf",
  "thumbnailWidth": 140,
  "thumbnailHeight": 78,
  "previewImages": [],
  "galleryImages": [
    {
      "url": "https://preview.redd.it/4bg1ylvg7kmh1.jpg?width=858&format=pjpg&auto=webp&s=85579f28acaabf8f8e8694559f45e7cd336d7703",
      "width": 858,
      "height": 483
    },
    {
      "url": "https://preview.redd.it/662iplvg7kmh1.jpg?width=638&format=pjpg&auto=webp&s=8a5c5cdb0eb9c903d868e771fb7ed8633be382fa",
      "width": 638,
      "height": 841
    }
  ],
  "video": null
}
```

Reddit-hosted videos fill `video`. These values are from a recorded r/aww post:

```json
{
  "type": "post",
  "id": "1w4tl99",
  "title": "Caught a baby groundhog eating wild berries.",
  "subredditPrefixed": "r/aww",
  "postHint": "hosted:video",
  "isVideo": true,
  "url": "https://v.redd.it/f2terttiuzmh1",
  "previewImages": [
    {
      "url": "https://external-preview.redd.it/dHpudDBnc2l1em1oMXXW2jQ0ep1LXLIwd3_hyHbgZT-OYNyI9K11VXITQ2JR.png?format=pjpg&auto=webp&s=4b86979347b53b93dcbc5988f63df18016fc8351",
      "width": 405,
      "height": 720
    }
  ],
  "video": {
    "url": "https://v.redd.it/f2terttiuzmh1/CMAF_1080.mp4?source=fallback",
    "hlsUrl": "https://v.redd.it/f2terttiuzmh1/HLSPlaylist.m3u8?a=1790931095%2CM2MwM2MwZjZiZGRiZjk2ZTg1NjYwNjQwZDE3YzYzODg0Yzk4NTU0YmY4NGEyNWM2NGZmMGUzMTk0ZjFjYTA1Yg%3D%3D&v=1&f=sd",
    "dashUrl": "https://v.redd.it/f2terttiuzmh1/DASHPlaylist.mpd?a=1790931095%2CZGZhOTFjZjkzZTY5MDBmZTkzYTkwYTA5ZjA0MTQwMzVjMmE1MjczMWY2ZGEzM2UyYWIzNDMyYTczMmE5MDBkZA%3D%3D&v=1&f=sd",
    "durationSeconds": 26,
    "width": 1080,
    "height": 1920,
    "hasAudio": true,
    "isGif": false
  }
}
```

The `video.url` is a direct MP4. HLS and DASH playlists carry a signed
`a=` parameter and expire after a while, so download or play them soon after
the run. Videos embedded from other sites, such as YouTube, are link posts:
`isVideo` is `false` and `url` points at that site.

#### Comment fields

Every comment is its own dataset item with the same shape: after its post when
comments are requested in a post mode, and as the results of `user` mode with
`userContent: "comments"`.

| JSON type | Comment fields |
|---|---|
| string | `type`, `id`, `fullName`, `parentId`, `linkId`, `subreddit`, `permalink` |
| string or `null` | `subredditId`, `author`, `authorFullName`, `authorFlairText`, `body`, `bodyHtml`, `created`, `distinguished`, `collapsedReasonCode` |
| number or `null` | `depth`, `score`, `controversiality`, `totalAwardsReceived`, `createdUtc`, `editedUtc` |
| boolean | `authorPremium`, `edited`, `isSubmitter`, `stickied`, `scoreHidden`, `collapsed` |

`linkId` is the fullname (`t3_`) of the post the comment belongs to. `parentId`
is that post fullname for a top-level comment and the parent comment's fullname
(`t1_`) for a reply. `depth` is `0` at the top and one more at each level.
Comments are saved parent first, so the post and the parent of every comment
appear earlier in the dataset. In a user's comment feed no thread is saved and
Reddit reports no level, so `depth` is `null` there. `distinguished` marks `moderator` or `admin`
comments. `scoreHidden` is `true` while Reddit hides a fresh comment's score.
`collapsed` and `collapsedReasonCode` reflect Reddit's default folding, for
example `LOW_SCORE`. The comments of a thread come from a Reddit source without
controversiality, premium status or HTML, so on them `controversiality` and
`bodyHtml` are `null` and `authorPremium` is `false`. A user's comment feed
carries all three.

These unchanged values are a recorded `user` mode item, one comment by the
account `spez`:

```json
{
  "type": "comment",
  "id": "p1wosm9",
  "fullName": "t1_p1wosm9",
  "parentId": "t1_p1wd2fi",
  "linkId": "t3_1vgbkge",
  "subreddit": "u_spez",
  "subredditId": "t5_3k30p",
  "author": "spez",
  "authorFullName": "t2_1w72",
  "authorFlairText": null,
  "authorPremium": true,
  "body": "That was the thinking. Otherwise it would be easier to just replicate the UI. ",
  "bodyHtml": null,
  "score": 9,
  "controversiality": 0,
  "totalAwardsReceived": 0,
  "createdUtc": 1785954710,
  "created": "2026-08-05T18:31:50.000Z",
  "editedUtc": null,
  "edited": false,
  "depth": 0,
  "permalink": "https://www.reddit.com/user/spez/comments/1vgbkge/modernizing_reddits_infrastructure_with_you/p1wosm9/",
  "isSubmitter": true,
  "stickied": false,
  "distinguished": "admin",
  "scoreHidden": false,
  "collapsed": false,
  "collapsedReasonCode": null
}
```

#### A post with its thread

<details>
<summary>View a post followed by its comments</summary>

This is a recorded r/MachineLearning post with selected fields, followed by
the first two of its comments: a top-level comment and a reply to it. Each is
a separate dataset item with the full field set; only a few fields are shown.

```json
[
  {
    "type": "post",
    "id": "1w10tax",
    "fullName": "t3_1w10tax",
    "subredditPrefixed": "r/MachineLearning",
    "title": "I implemented a very tiny image generation model (latent flow transformer) on a RP2350 microcontroller - it can generate 128x128 images of faces [P]",
    "author": "cpldcpu",
    "permalink": "https://www.reddit.com/r/MachineLearning/comments/1w10tax/i_implemented_a_very_tiny_image_generation_model/",
    "url": "https://www.reddit.com/gallery/1w10tax",
    "isSelf": false,
    "score": 567,
    "upvoteRatio": 0.99,
    "numComments": 33,
    "created": "2026-08-28T19:48:27.000Z",
    "linkFlairText": "Project"
  },
  {
    "type": "comment",
    "id": "p6i5z6o",
    "fullName": "t1_p6i5z6o",
    "linkId": "t3_1w10tax",
    "parentId": "t3_1w10tax",
    "depth": 0,
    "author": "Necessary-Put-2245",
    "body": "What did you use for creating the diagrams?",
    "score": 3
  },
  {
    "type": "comment",
    "id": "p6jl85w",
    "fullName": "t1_p6jl85w",
    "linkId": "t3_1w10tax",
    "parentId": "t1_p6i5z6o",
    "depth": 1,
    "author": "Two_Two_Five_",
    "body": "yeah i had the same question they look great!",
    "score": 0
  }
]
```

To rebuild the tree, attach each comment to the item whose `fullName` equals
its `parentId`. Items arrive parent first, so one pass is enough:

```js
const byFullName = new Map(items.map((item) => [item.fullName, { ...item, replies: [] }]));
for (const item of byFullName.values()) {
  if (item.type === "comment") {
    byFullName.get(item.parentId)?.replies.push(item);
  }
}
const threads = [...byFullName.values()].filter((item) => item.type === "post");
```

</details>

#### Subreddit fields

`community` mode saves one item per subreddit.

| JSON type | Subreddit fields |
|---|---|
| string | `type`, `id`, `fullName`, `name`, `namePrefixed`, `url`, `scrapedAt` |
| string or `null` | `title`, `publicDescription`, `description`, `subredditType`, `submissionType`, `lang`, `advertiserCategory`, `communityIcon`, `iconImg`, `bannerBackgroundImage`, `headerTitle`, `created` |
| number or `null` | `subscribers`, `activeUserCount`, `createdUtc` |
| boolean | `over18`, `quarantine`, `wikiEnabled` |

`publicDescription` is the short blurb shown in search results.
`description` is the full sidebar text in Markdown. `submissionType` is `any`,
`link`, or `self`. `activeUserCount` is `null` when Reddit does not report it.

These unchanged values are a recorded `community` result, with the sidebar text
shortened:

```json
{
  "type": "subreddit",
  "id": "2r3gv",
  "fullName": "t5_2r3gv",
  "name": "MachineLearning",
  "namePrefixed": "r/MachineLearning",
  "title": "Machine Learning",
  "publicDescription": "Beginners -> /r/mlquestions or /r/learnmachinelearning , AGI -> /r/singularity, career advices -> /r/cscareerquestions, datasets -> r/datasets",
  "description": "**[Rules For Posts](https://www.reddit.com/r/MachineLearning/about/rules/)** [...]",
  "subscribers": 3069244,
  "activeUserCount": null,
  "over18": false,
  "quarantine": false,
  "subredditType": "public",
  "submissionType": "any",
  "wikiEnabled": true,
  "lang": "en",
  "advertiserCategory": "Technology",
  "communityIcon": "https://styles.redditmedia.com/t5_2r3gv/styles/communityIcon_kilpomt3l5c51.png?width=256&s=671205393c49c1a601b16e1ba19c2a25fb0f0d22",
  "iconImg": null,
  "bannerBackgroundImage": null,
  "headerTitle": null,
  "createdUtc": 1248878084,
  "created": "2009-07-29T14:34:44.000Z",
  "url": "https://www.reddit.com/r/MachineLearning/",
  "scrapedAt": "2026-09-02T08:51:33.926Z"
}
```

#### Nested field reference

<details>
<summary>View child fields for nested objects and arrays</summary>

| Parent field | Child field types |
|---|---|
| `previewImages[]` | `url`: string; `width`, `height`: number or `null` |
| `galleryImages[]` | `url`: string; `width`, `height`: number or `null`. Items keep gallery order. |
| `video` | `url`: string; `hlsUrl`, `dashUrl`: string or `null`; `durationSeconds`, `width`, `height`: number or `null`; `hasAudio`, `isGif`: boolean |

</details>

Timestamps come in two forms: `createdUtc` and `editedUtc` are Unix seconds,
`created` is ISO 8601. `scrapedAt` on posts and subreddits is the collection
time in ISO 8601. Markdown bodies keep Reddit's raw characters; they are not
HTML-escaped.

### Collection behaviour

#### Search and feed results

Each post appears once. The leading items follow your `sort` and `time`;
later items can be in a different order. Large `maxItems` values can return
about 5,000 unique posts from a big subreddit; on a small one the run can
return the whole subreddit. A `time` narrower than `all` keeps every item
within that window, so a `top` of the week stays within the week. Subreddit
discovery returns at most about 1,000 subreddits.

Search matching and order follow Reddit and can change between runs.
`includeNsfw: false` leaves out posts and subreddits marked over-18. They do
not count toward `maxItems`, so the run still fills the budget from the rest.
A valid search with no matches produces an empty dataset.

Posts from search and from the newest posts of a subreddit lack a few values.
`selftextHtml` and `totalAwardsReceived` are `null` at every depth, and
`authorPremium` and `isOriginalContent` are `false`. A video's `hasAudio` is
`true`, so a silent video can read as having sound.

#### Comment threads

Each comment is its own dataset item, so a thread of any size is saved in
full: a post with twenty thousand comments yields one post item and about
twenty thousand comment items. `maxCommentsPerPost` is a hard cap that counts
nested replies. `0` removes it. The cap fills level by level in the chosen sort
order: top-level comments first, then their replies. A saved reply always has its
parent saved too. The Actor collects every branch Reddit lists, with no limit
on depth, until the cap is met.

Every mode saves its items while it runs, whether posts, comments or
subreddits. Items from several Reddit requests are combined into writes of at
least 100 items, except the last, so a small response never causes a small
write. A stopped run keeps everything already written. It loses the items
still waiting for the next write, and the threads that loaded ahead of an
unfinished thread, because threads are saved in order.

The Actor saves a thread's comments while they load, with no cap or with a cap
at least as large as the post's comment count. A comment always comes after its
parent, but the order is not depth-first: a page of replies can follow later
top-level comments. Use `parentId` and `depth` to rebuild the tree. With a
smaller cap, the Actor saves a post's comments when that post has finished
loading, because which comments the cap keeps is known only then. On threads of many thousand
comments Reddit stops listing top-level comments after about 30 pages, so the
thread can hold fewer comments than `numComments` even after the deleted ones
are counted out.

A fully collected thread can hold slightly fewer comments than the post's
`numComments`. Reddit counts comments that were later deleted by their author,
removed by moderators, or filtered as spam, and no longer serves them. A
deleted or removed comment that still has replies is still saved so the
replies keep their parent: its `author` is `[deleted]`, its `authorFullName` is
`null`, and its `body` is `[deleted]` or `[removed]`.

#### Users, subreddits, and posts by id

Only public content is reachable. A run aimed at a private, quarantined, or
banned subreddit, or at a subreddit or user that does not exist, stops at once
with a status message that says so, before anything is collected. In `post`
mode an unreachable post is skipped with a log line that gives the reason, and
the run continues with the rest. If Reddit refuses to load every comment thread
the run asks for, the run ends with an error and does not report success with no
comments. If it refuses only some, the run finishes and logs a warning.

#### Speed and volume

Small runs finish in seconds. Very large runs take longer. The Apify proxy is
on by default. If you switch it off in `proxyConfiguration`, large runs can
take longer.

#### Content changes

Scores, comment counts, and flair change over time, and content can be edited
or removed after collection. `scrapedAt` records when each item was read.
Media URLs on `preview.redd.it` and `v.redd.it` can expire; store the files if
you need them later.

# Actor input Schema

## `mode` (type: `string`):

What to scrape. Each mode uses one target selector below.

## `keyword` (type: `string`):

Search terms. Used by <code>search</code> and <code>community</code> modes. In search mode, add a subreddit below to restrict results to that community.

## `subreddit` (type: `string`):

Community name, with or without the <code>r/</code> prefix. Target for <code>subreddit</code> mode; optional filter for <code>search</code> mode.

## `username` (type: `string`):

Reddit username, with or without the <code>u/</code> prefix. Target for <code>user</code> mode.

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

Target for <code>post</code> mode. Accepts full post URLs, redd.it short links, <code>t3\_</code> fullnames, or bare post IDs.

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

How to order the leading results. Once that listing is exhausted, the other sorts and time windows follow until Max items is reached. Unsupported values fall back to the nearest one (for example, relevance and rising become hot in a subreddit).

## `time` (type: `string`):

Time window for <code>top</code>, <code>controversial</code>, and search results. A window narrower than all time is a filter: <code>hot</code>, <code>new</code>, and <code>rising</code> cannot be filtered by time, so a feed then starts with the windowed <code>top</code> instead.

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

Maximum posts, comments, or subreddits to collect in the list modes. Set to 0 for everything Reddit offers. Past the size of one listing, the Actor continues through the target's other sorts and time windows, without duplicates. Not used in <code>post</code> mode, where the URL list sets the size.

## `userContent` (type: `string`):

For <code>user</code> mode: fetch the user's submitted posts or their comments.

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

Include posts and subreddits marked over-18. Search results and the newest posts of a subreddit do not include over-18 posts, whatever this is set to.

## `depth` (type: `string`):

Basic: core fields, images and video. Detailed: adds the HTML body of each post, and of each comment in a user's comment feed.

## `includeComments` (type: `boolean`):

Save each post's comments as their own records, right after the post. Always on for <code>post</code> mode results.

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

Cap on comments saved per post, counting nested replies. Set to 0 to collect the complete thread, down to the last reply; its comments are then saved while they load. With a cap below the post's comment count, the Actor keeps top-level comments first and saves them when the post finishes loading.

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

Order of the comment tree.

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

Proxy settings for the run. Apify proxy is on by default and helps large runs finish sooner. Switch it off to run without one.

## Actor input object example

```json
{
  "mode": "search",
  "keyword": "large language models",
  "subreddit": "MachineLearning",
  "postUrls": [],
  "sort": "hot",
  "time": "all",
  "maxItems": 50,
  "userContent": "submitted",
  "includeNsfw": true,
  "depth": "basic",
  "includeComments": false,
  "maxCommentsPerPost": 50,
  "commentSort": "confidence",
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `items` (type: `string`):

All collected posts, comments, and subreddits as JSON. Filter on the type field. Append ?format=csv or ?format=xlsx to download a spreadsheet.

## `posts` (type: `string`):

The posts table view: type, subreddit, title, author, score, upvote ratio, comment count, posting time, and URL.

## `comments` (type: `string`):

The comments table view: type, post, parent, depth, author, score, text, posting time, and URL.

# 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 = {
    "keyword": "large language models",
    "subreddit": "MachineLearning",
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("kaix/reddit-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 = {
    "keyword": "large language models",
    "subreddit": "MachineLearning",
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("kaix/reddit-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 '{
  "keyword": "large language models",
  "subreddit": "MachineLearning",
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call kaix/reddit-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,kaix/reddit-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/EaGW59j6AMSrqjrBK/builds/zg9jDSBHPsCc774cn/openapi.json
