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

🔥 ~$0.05/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)
- **Categories:**
- **Stats:** 1 total users, 1 monthly users, 86.2% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.04 / 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?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# 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 and optionally HTML, score, controversiality, 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. When the
listing you chose runs out before `maxItems`, the Actor continues through the
target's other sorts and time windows, without duplicates, until the budget is
met 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 | `expandComments` | `maxCommentsPerPost: 0` for no cap |

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.
Without `expandComments`, a post comes with the comments Reddit shows on first
load. With it, every "load more comments" batch and every "continue this
thread" branch is collected, at any nesting depth. A thread with several
thousand comments takes 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. |

### 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",
  "expandComments": true
}
```

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; once its listing is exhausted, the other sorts and windows follow. A value the selected mode does not offer falls back to the nearest one: `rising` becomes `hot` in search, 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: the run walks only listings that can honour it, so in a feed `hot`, `new`, and `rising` are skipped and the windowed `top` leads instead. |
| `maxItems` | integer | `50` | Maximum posts, comments, or subreddits to save in the list modes. `0` means everything Reddit offers for the target. Past one listing's size, the Actor continues through the target's other sorts and time windows, without duplicates. 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. |
| `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` means no cap. |
| `commentSort` | enum | `confidence` | `confidence` (Reddit's "best"), `top`, `new`, `controversial`, `old`, or `qa`. |
| `expandComments` | boolean | `false` | Collect the complete thread, including every "load more comments" branch. |
| `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",
  "expandComments": true,
  "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",
  "expandComments": false
}
```

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. `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 the source.

#### 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`.

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
for that listing.

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

Reddit serves at most about 1,000 items per listing, and about 500 for a
`top` or `controversial` window. To reach a larger `maxItems`, the Actor
walks the target's other listings in turn: for a subreddit or user, `new`,
`hot`, and every `top` and `controversial` window; for a search, `new` and
every window of `relevance`, `top`, `comments`, and `hot`. Items already saved
are skipped, so each post appears once. The listing you chose with `sort` and
`time` always goes first, so the leading items follow your choice.

On a large subreddit this reaches about 5,000 unique posts; on a small one it
is the whole subreddit. A `time` narrower than `all` is kept as a filter on
every listing walked, so a `top` of the week stays within the week, and feeds
that cannot be filtered by time are left out. Subreddit discovery has a single
listing.

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.

#### 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 keeps whole branches, so a saved reply
always has its parent saved too. Without `expandComments`, a post comes with
what Reddit shows on first load, which is up to a few hundred comments on a
large thread. With `expandComments`, the Actor collects every branch to the
last reply, including branches Reddit only shows behind "continue this
thread", with no limit on depth.

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.

#### Speed and volume

Small runs finish in seconds. Reddit limits how fast content can be read, so
very large runs slow down rather than fail; the Actor manages this by itself.
The Apify proxy is on by default and helps large runs finish sooner. It can be
switched off in `proxyConfiguration`.

#### 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 (e.g. relevance becomes hot in a subreddit listing).

## `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.

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

Basic: core fields, images and video. Detailed: adds the HTML body of each post and comment.

## `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 for no limit.

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

Order of the comment tree.

## `expandComments` (type: `boolean`):

Collect the complete thread: every 'load more comments' batch and every 'continue this thread' branch, down to the last reply. Without it, you get the comments Reddit shows on first load.

## `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",
  "expandComments": false,
  "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/kiipwBykyuvI5moti/openapi.json
