# Reddit Data Scraper: Posts & Comments, $1.90/1K Posts (`actorforgehq/reddit-data-scraper`) Actor

Input: subreddit names, search keywords, usernames or Reddit URLs. Output: posts as JSON with title, author, score, comment count, body, link and date, plus the first 25 or so comments of a post page. $1.90 per 1,000 posts, $0.75 per 1,000 comments, no start fee.

- **URL**: https://apify.com/actorforgehq/reddit-data-scraper.md
- **Developed by:** [Sluicebox](https://apify.com/actorforgehq) (community)
- **Categories:** Social media, Automation, Developer tools
- **Features:** Limited permissions
- **Stats:** 13 total users, 2 monthly users, 97.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.90 / 1,000 posts

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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 Data Scraper: Posts, Comments, Users, Monitoring

Scrape **Reddit** as clean JSON: subreddit feeds (hot, new, top, rising), posts with their comments,
keyword search, and the two things most scrapers get wrong: **user post and comment history** and
**date-range filtering**. It reads the same public pages Reddit serves to a logged-out visitor's
browser, over plain HTTP with no page rendering and no scrolling, at a fixed pace that stays inside
the request quota Reddit itself announces for these pages.

### What you get

The dataset contains **two kinds of item**, tagged by a `type` field: posts and comments.
Comments link back to their post via `postId`, and comments from a post page link to their parent
comment via `parentId`, so you can rebuild the thread tree.

**Post:**

```json
{
  "type": "post",
  "id": "1mk2qwe",
  "subreddit": "explainlikeimfive",
  "author": "curious_user",
  "title": "ELI5: Why does ice float on water?",
  "score": 1543,
  "numComments": 214,
  "url": "https://i.redd.it/example.jpg",
  "permalink": "https://www.reddit.com/r/explainlikeimfive/comments/1mk2qwe/eli5_why_does_ice_float_on_water/",
  "domain": "i.redd.it",
  "createdUtc": "2026-08-07T14:32:05.000Z",
  "nsfw": false,
  "spoiler": false,
  "stickied": false,
  "flair": "Physics"
}
```

`selfText` (the body of a text post, as plain text) comes with feed rows, user history rows and
the post's own page. For link posts `url` is the external link. For text posts it is omitted
rather than filled with the post's own address. Search rows are lighter, see
[Limits](#limits-honest).

**Comment:**

```json
{
  "type": "comment",
  "id": "n8x1abc",
  "postId": "1mk2qwe",
  "subreddit": "explainlikeimfive",
  "author": "helpful_redditor",
  "score": 87,
  "body": "Water is one of the few substances that expands when it freezes.",
  "createdUtc": "2026-08-07T15:01:44.000Z",
  "permalink": "https://www.reddit.com/r/explainlikeimfive/comments/1mk2qwe/eli5_why_does_ice_float_on_water/n8x1abc/",
  "depth": 2,
  "parentId": "t1_n8wzdef"
}
```

Two honesty details worth knowing before you buy:

- When Reddit hides a comment's score, `score` is **absent**. We do not substitute a fake `0`.
- Deleted authors come through as `[deleted]`, exactly as Reddit shows them.

### What it can scrape

| Mode | Input | What comes out |
|---|---|---|
| **Subreddit feeds** | `subreddits` + `listingSort` (hot/new/top/rising) | Posts, paginated up to Reddit's own feed ceiling of about 1,000 posts |
| **Posts with comments** | a post URL in `startUrls` | The post itself (with `selfText`) plus the comments Reddit shows on the post page, with depths and parent links (see [Limits](#limits-honest)) |
| **Search** | `searches`, optionally scoped with `searchCommunityName` | Matching posts, newest first, global or within one subreddit, up to about 400 per query |
| **User history** | `users` | The user's public posts and comments, mixed, paginated |
| **Monitoring** | any of the above on an Apify Schedule | Frequent small runs cost exactly what they scrape: there is **no per-run start fee** |

Date filters (`postedAfter` / `postedBefore` for posts, `commentedAfter` / `commentedBefore` for
comments) apply to every mode.

### Use cases

- **Brand and keyword monitoring.** Run a search or a subreddit feed on a schedule. Because
  billing has no start fee, a 25-post check costs 25 posts at $0.0019 = **$0.0475, every time,
  with no per-run surcharge**. Many scrapers in this niche charge a start fee per run (sometimes
  multiplied by memory GB), which quietly dominates the bill in exactly this scenario.
- **User research.** Pull a redditor's public post and comment history in one run, a mode that
  is notoriously unreliable across this niche.
- **Period analysis and backtesting.** `postedAfter` + `postedBefore` give you a real date window,
  not just Reddit's "past week" or "past month" presets.
- **Community research.** Feed sorts (hot/new/top/rising) with time filters, NSFW filtering
  off by default and flair captured per post.
- **LLM and dataset building.** Clean, typed JSON with stable ids, ISO timestamps and thread
  structure (`postId`, `parentId`, `depth`), ready to reassemble into conversation trees.

### Input

| Field | Type | Description |
|---|---|---|
| `startUrls` | array | Reddit URLs of any supported kind: subreddit feed, post, user page, or search page (`www.reddit.com` and `old.reddit.com` links both work). Mode is detected from each URL. |
| `subreddits` | string\[] | Subreddit names without `r/`, e.g. `["programming"]`. |
| `listingSort` | string | `hot` (default), `new`, `top`, `rising`. |
| `time` | string | Time window for `top` listings and search: `hour`, `day`, `week`, `month`, `year`, `all` (default). |
| `searches` | string\[] | Search queries. Alias: `searchTerms`, so inputs written for other popular Reddit scrapers work unchanged. |
| `searchCommunityName` | string | Restrict search to one subreddit. Alias: `withinCommunity`. |
| `users` | string\[] | Usernames without `u/`. Scrapes their public post and comment history. |
| `postedAfter` / `postedBefore` | string | ISO date bounds for posts. `postedAfter` also stops paging early in `new` feeds, search and user history (see Limits). |
| `commentedAfter` / `commentedBefore` | string | ISO date bounds for comments. |
| `includeNSFW` | boolean | Default `false`. NSFW posts are skipped unless you opt in. |
| `maxItems` | integer | Max items per subreddit, search query or user history (default 100, hard cap 1,000, about Reddit's own feed ceiling). A feed or profile request returns about 25 items and a search request about 7, so 1,000 feed posts take about 42 requests. Paging stops after 60 pages per source, so a search query gives at most about 400 posts. Does not limit post URLs: a post URL always delivers the post plus the comments shown on its page. |
| `onlyNew` | boolean | Default `false`. When on, each run skips items this Actor already gave you from the same sources in earlier runs, so they are not saved or charged again. See [Only new since last run](#only-new-since-last-run). |
| `proxyConfiguration` | object | Defaults to Apify **residential proxies** with no country pin, which is the supported configuration. |

### How to use it

1. Click **Try for free** or **Start** on this page.
2. Enter subreddit names, search queries or usernames, or paste Reddit URLs directly into
   **Start URLs** (feeds, posts, user pages and search pages are all recognized).
3. Optionally set the sort, the time window, date bounds and **Max items**.
4. Run it. Posts and comments land in the dataset as separate typed rows. Export them as JSON,
   CSV or Excel, or pull them over the API.

To run it on a schedule, use Apify **Schedules**. This Actor's pricing has no per-run start fee,
so scheduled monitoring costs the same per item as one big run. To call it from code, use the
[Apify API](https://docs.apify.com/api/v2) or an official client.

### Only new since last run

Turn on **Only new since last run** (`onlyNew: true`) for scheduled monitoring. Here "new" means
"not delivered to you before from the same source", not "newer by time": on a `hot` or `top`
feed an older post you have not received yet still counts as new.

- Items you already got from a source are not written to the dataset and not charged. A post
  that shows up in two of your sources (say a crosspost, or a feed plus its direct URL) can still
  come once per source.
- `maxItems` counts new items only. Paging stops at the first page where every item was already
  delivered to you, and never goes past 60 pages per source, the same ceiling as without the
  flag. A quiet source costs one request and no item charges.
- The list of delivered items lives in a named key-value store in your own Apify account, called
  `reddit-only-new-` plus a code made from your sources (the run log shows the exact name). Each
  set of sources (subreddits with their sort and time window, searches, users, URLs) gets its own
  store, so adding or removing a source starts a fresh list for the whole set. Filters (dates,
  NSFW, `maxItems`) do not. The store does not expire. The latest 2,000 items per source are
  remembered.
- The list is saved once, at the end of the run. If a run is aborted or times out, the list stays
  as it was, so the next run delivers again what that run had delivered, rather than losing it.
  For the same reason, do not let two runs with the same input overlap.
- If your max cost limit stops a run, items it saved but could not charge come again next time.
- To start over, delete that store in Apify Console under **Storage**, **Key-value stores**.

### Rate-limit etiquette (built in, with numbers)

This Actor is deliberately polite to Reddit's servers, and the numbers are fixed in code, not
left to chance:

- **At most 20 requests per minute**, with a hard 3-second spacing between requests. Reddit
  announces a quota of 225 requests per 10 minutes for these pages in its `x-ratelimit-*`
  headers. A 3-second spacing means at most 200 requests per 10 minutes, so the Actor stays
  inside that quota by design.
- A feed or profile request carries about 25 items, a search request about 7 posts, and a post
  page carries the post with its first comments. A 1,000-post feed is about 42 requests, roughly
  two minutes.
- On an HTTP `429` the Actor **waits 30 seconds**, opens a fresh session and retries. On any other
  transient refusal it backs off (3 s, then 6 s, then 9 s), rotates to a fresh proxy session and
  retries, at most 3 times. A post that does not exist (HTTP `404`) is **not** retried at all: the
  task fails immediately with the reason in the log.
- If five tasks in a row (a source, or one page of it) fail after all retries, the run stops
  instead of grinding through the rest of the plan.

The Actor stays inside what Reddit's own throttling tolerates, by design, and imposes no undue
burden on the target servers.

### Limits (honest)

- **About 1,000 posts per feed is Reddit's ceiling, not ours.** Reddit serves at most about 1,000
  posts for any listing, and no scraper can page past that. `maxItems` is capped there.
- **A post URL gives the comments shown on the post page, not the whole thread.** That is about
  25 comments: in a big thread the top 25, in a smaller one fewer top comments together with the
  replies Reddit shows under them. Comments Reddit loads only when you click "more replies" or
  scroll further are not fetched in this version.
- **Search rows are lighter than feed rows.** Reddit's search page shows no post body and no
  link target, so search rows have no `selfText`, `url`, `domain` or `flair`. Scrape the
  subreddit feed or the post URL when you need those.
- **Text comes as plain text.** Post and comment bodies keep their paragraphs, list items and
  code blocks, but not formatting such as bold, italics or link targets.
- **User-history comments arrive flat.** A profile shows neither the thread position nor the
  parent of a comment, so those comments have `depth: 0` and no `parentId`. `postId` is still
  filled in. Comments scraped from a post's own page carry the tree structure.
- **Date filters are applied after fetching, with one shortcut.** Reddit has no server-side
  date-range filter, so `postedAfter`/`postedBefore` filter items after each page is read. In
  feeds that run newest first (`listingSort: "new"`, search, and user history when both
  `postedAfter` and `commentedAfter` are set) the Actor stops paging at the first item older than
  the bound, so a narrow window costs one or two pages instead of the whole feed. Pinned posts are
  ignored for that decision. `hot`, `top` and `rising` are not chronological, so they are read in
  full and filtered. When a date bound is set, items whose timestamp cannot be read are dropped
  rather than guessed.
- **A subreddit or user that does not exist, or is banned, gives an empty result.** Reddit
  serves such pages as ordinary pages with no posts, so that source ends with 0 items and no
  charge, not with an error. Check the spelling if a source comes back empty.
- **No login, no captcha solving.** The Actor reads only what Reddit serves to any logged-out
  visitor. It opens a session the way a browser does, completes Reddit's lightweight automated
  visitor check (a short script every visitor's browser runs), and then reads public pages with
  that session. If the check ever changes shape, feeds, search and profiles keep working through
  a lighter page that needs no check, while post pages may fail until the Actor is updated. If
  Reddit refuses a session, the Actor opens a new one from a fresh address. It fails honestly
  rather than fabricate data.
- If more than 5% of tasks in a run fail, the run log says so loudly. A run in which every
  fetch failed is marked **failed**, not "succeeded with 0 items". A run whose filters
  legitimately match nothing (say, a strict date window) still succeeds, with an empty dataset
  and an empty bill.

### Pricing

Pay-per-event, two events, nothing else:

| Event | Rate |
|---|---|
| `post-scraped` | **$1.90 per 1,000** ($0.0019 each) |
| `comment-scraped` | **$0.75 per 1,000** ($0.00075 each) |
| Run start | **free, there is no start event** |

A comment costs less than half a post, while most competitors bill a comment at the full post
price.

**What you get for $1:** about 526 posts, or about 1,333 comments, or any mix. Platform usage
(compute, proxy) is charged to us, not to you, so the per-item price above is the whole bill.
There is no subscription, no minimum charge and no per-gigabyte start fee, so billing is
strictly proportional to data delivered. Autonomous AI agents can call this Actor through the
**Apify MCP server** and pay per item. Rates are as of the last README update. The Store page
stays authoritative.

### FAQ

**Do I need a Reddit account or an API key?**
No. The Actor reads only publicly available data as a logged-out visitor, never logs in, and
does not use Reddit's developer API or anyone's credentials.

**Why is there no start fee? What's the catch?**
No catch. It is a deliberate design choice. Start fees (especially per-GB ones) punish the
monitoring scenario: frequent small runs. We want scheduled monitoring to be priced exactly like
one big run, per item delivered.

**Does it use a browser?**
Not for the data. Pages are fetched over plain HTTP and read straight from the HTML Reddit
serves, with no rendering and no scrolling. That keeps it fast, cheap and free of the
render-and-scroll timeouts that browser-based scrapers show by the tens of thousands per month
in their public run statistics. A real Chrome is kept on standby only as a last resort for
opening a session, when no page gives one over plain HTTP.

**I'm migrating from another Reddit scraper. Do I have to rewrite my input?**
Probably not. The common field names are accepted directly (`searchTerms`, `withinCommunity`),
and `startUrls` takes the same URL-list format other actors use.

**Can I get every comment of a huge thread?**
Not in this version. A post URL gives the post and the comments Reddit shows on the post page:
about 25 comments with their full tree position (in a big thread, the top 25). Comments Reddit
loads only on "more replies" or on scroll are not fetched.

**Are NSFW posts included?**
Only if you set `includeNSFW: true`. The default is off.

**Where is my data stored?**
In your run's dataset on your Apify account, under your control. The Actor keeps no copy of
scraped data outside your run and sends nothing to any third party.
With **Only new since last run** on, it also keeps the ids of items it already gave you in a
key-value store in your account (see above), so you can delete them at any time.

### Other Actors

- [Lazada Reviews Scraper](https://apify.com/actorforgehq/lazada-reviews-scraper): product
  reviews, per-aspect ratings and buyer media from all six Lazada marketplaces.
- [Wildberries Scraper](https://apify.com/actorforgehq/wildberries-scraper): products, prices
  and reviews from Wildberries as clean, schema-validated JSON.
- [Avito Real Estate Scraper](https://apify.com/actorforgehq/avito-real-estate-scraper): full
  property inventory from Avito with freshness stamps.

Missing a Reddit surface you need? Open an issue from the Actor's page and tell us.

### Legal

This is an **unofficial** scraper. It is not affiliated with, endorsed by, or connected to
Reddit, Inc. in any way, and it does not use Reddit's developer API. It reads only data that
Reddit serves publicly to any logged-out visitor: no login, no credentials, no captcha solving.
Website terms of service are a contractual matter between a website and its visitors, not
criminal law. You are responsible for ensuring that your use of the collected data complies with
the laws that apply to you and with Reddit's terms. Post and comment content belongs to its
authors.

### Personal data

Reddit is a pseudonymous platform: the `author` field contains the public username a person
chose to publish under, not their real identity. This Actor collects no emails, no real names,
no private messages, and performs no deanonymization. It stores exactly what any visitor sees
on a public page. Scraped data lives only in your own Apify dataset and is not retained by us
outside your run. If the data you collect contains personal data under the laws of your
jurisdiction (for example the GDPR), you act as its controller: it is your responsibility to
have a legal basis for processing and to honor data-subject requests, including deletion.

### Changelog

See the repository `CHANGELOG.md`.

# Actor input Schema

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

Direct Reddit links: a subreddit listing (https://www.reddit.com/r/programming/ or .../top), a post (.../comments/<id>/...), a user profile (https://www.reddit.com/user/<name>/), or a search results page. old.reddit.com links are accepted too. The kind of source is detected from the link itself.

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

Subreddit names (without r/) to list, using the sort order and time filter below.

## `listingSort` (type: `string`):

Sort order for subreddit listings.

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

Applies to "Top" listings and to search. Ignored otherwise.

## `searches` (type: `array`):

Keyword queries to run against Reddit's search. Combine with "Restrict search to subreddit" to scope a query to one community. Alias: searchTerms.

## `searchCommunityName` (type: `string`):

When set, every query in "Search keywords" is scoped to this one subreddit instead of searching all of Reddit. Alias: withinCommunity.

## `users` (type: `array`):

Usernames (without u/) whose post and comment history to pull.

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

Keep only posts submitted on or after this date. In new feeds, search and user history (with commentedAfter too) paging stops at the first older post, so a narrow window is cheap; hot, top and rising are read in full and filtered.

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

Keep only posts submitted on or before this date.

## `commentedAfter` (type: `string`):

Keep only comments made on or after this date.

## `commentedBefore` (type: `string`):

Keep only comments made on or before this date.

## `includeNSFW` (type: `boolean`):

Off by default: NSFW posts are skipped unless you explicitly turn this on.

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

How many items to keep per subreddit / search query / user (posts and comments counted together for users). Reddit's own listings stop paginating around 1,000 items regardless of this setting. A feed or profile request fetches about 25 items and a search request about 7, so 1,000 feed posts take about 42 requests. Paging stops after 60 pages per source, so a search query gives at most about 400 posts.

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

Skip items this Actor already gave you in earlier runs with the same sources: they are not saved again and not charged. New means not delivered to you before, not newer by time, so an older post you have not received yet still counts. With this on, Max items counts new items only, and paging stops at the first page where everything was already delivered. The list of delivered items is kept in a key-value store in your own Apify account, named reddit-only-new- plus a code made from your sources (the run log shows the exact name). Adding or removing a source starts a fresh list. It does not expire. To start over, delete that store in Storage.

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

Residential proxies are the default and the supported configuration. No country pin is needed. The run rotates proxy sessions and visitor cookies whenever Reddit refuses one.

## Actor input object example

```json
{
  "startUrls": [
    {
      "url": "https://www.reddit.com/r/programming/"
    }
  ],
  "subreddits": [
    "programming"
  ],
  "listingSort": "hot",
  "time": "all",
  "searches": [
    "large language models"
  ],
  "searchCommunityName": "programming",
  "users": [
    "spez"
  ],
  "postedAfter": "2026-01-01",
  "postedBefore": "2026-06-01",
  "commentedAfter": "2026-01-01",
  "commentedBefore": "2026-06-01",
  "includeNSFW": false,
  "maxItems": 100,
  "onlyNew": false,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# Actor output Schema

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

One row per scraped post: title, author, score, comment count, flair, NSFW/stickied flags and the self-text of text posts (not in search results).

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

Individual comments: body, score, depth in the thread, and (for comments from a post page) the fullname of the immediate parent. Join back to posts by postId.

# 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 = {
    "subreddits": [
        "programming"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("actorforgehq/reddit-data-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 = { "subreddits": ["programming"] }

# Run the Actor and wait for it to finish
run = client.actor("actorforgehq/reddit-data-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 '{
  "subreddits": [
    "programming"
  ]
}' |
apify call actorforgehq/reddit-data-scraper --silent --output-dataset

```

## MCP server setup

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