# Reddit Scraper: Posts, Comments & Change Monitor (`luminar/reddit-scraper-changefeed-private-v1`) Actor

Scrape Reddit posts, search results, subreddits, public user activity, and nested comments without a Reddit account or API key. Export stable IDs with clear coverage, or rerun the same scope to receive NEW, UPDATED, and safely confirmed ENDED changes.

- **URL**: https://apify.com/luminar/reddit-scraper-changefeed-private-v1.md
- **Developed by:** [Luka](https://apify.com/luminar) (community)
- **Categories:** Business, Marketing, Social media
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.89 / 1,000 post or search results

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

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

## What's an Apify Actor?

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

Use this **Reddit scraper** to collect public Reddit posts and comments as clean, relationship-aware dataset rows. For a **Reddit thread scraper** run, provide an exact Reddit URL; you can also start from a subreddit or search phrase, then choose a current snapshot or a safe changefeed for repeat monitoring.

The Actor keeps stable Reddit IDs, post-to-comment relationships, per-target coverage, and explicit `NEW`, `UPDATED`, `UNCHANGED`, or cautiously proven `ENDED` states. It uses a fixed Apify Residential route because Datacenter access is not reliable enough for this product; there is no buyer proxy setup to configure.

### 🚀 Start in 60 seconds

1. Open **Input** and keep the prefilled subreddit, or replace it with your own community name without `r/`.
2. Leave **Records to return** on **Posts only — fastest**.
3. Choose a small **Maximum posts per target** such as `25`.
4. Click **Start**. Open the **Posts and comments** dataset when the run finishes.

```json
{
  "subreddits": ["apify", "webscraping"],
  "maxItemsPerTarget": 25,
  "recordMode": "posts"
}
```

For one exact conversation, paste its URL instead:

```json
{
  "startUrls": [
    { "url": "https://www.reddit.com/r/apify/comments/1vxgygp/weekly_wild_ideas/" }
  ],
  "recordMode": "postsAndComments",
  "maxCommentsPerPost": 100,
  "maxDepth": 10
}
```

### 🎯 Choose the right workflow

| What you need | Input to use | Important behavior |
|---|---|---|
| Recent community posts | `subreddits` | Enter names without `r/` |
| Results for phrases | `searchQueries` | Search queries override subreddit inputs |
| One thread, search page, community, or public user page | `startUrls` | URLs override every other target method |
| Posts only | `recordMode: "posts"` | Fastest and lowest-cost path |
| Comments or full threads | `comments` or `postsAndComments` | Opens selected threads and adds comment rows |
| Only repeat-run changes | `stateMode: "changes"` | First comparable run emits observed rows as `NEW` |

Choose only one target method. The fixed precedence is:

`startUrls` → `searchQueries` → `subreddits`

### 📦 What you get

Every dataset row has `recordType: "post"` or `recordType: "comment"`.

- Post rows include title, body, author, subreddit, score, comment count, flair, links, timestamps, and exposed media metadata.
- Comment rows include body, author, score, root `postId`, `parentId`, and zero-based reply `depth`.
- Every row carries stable source and record keys, collection time, `coverageStatus`, warnings, and change fields.
- `matchedTargetKeys` and `matchedTargetChanges` preserve attribution when the same Reddit record matches more than one requested target.

Use the **Overview** dataset view for daily review and the **Full export** view for JSON, CSV, integrations, and audits. `RUN_SUMMARY` contains per-target coverage, row counts, charge totals, and run-wide warnings.

```json
{
  "recordType": "post",
  "redditId": "t3_example",
  "subreddit": "apify",
  "title": "Example post",
  "permalink": "https://www.reddit.com/r/apify/comments/example/",
  "coverageStatus": "COMPLETE",
  "changeStatus": null
}
```

### 🎛️ Input guide

**Targets.** Use `subreddits`, `searchQueries`, or `startUrls`. A direct thread returns at most one post plus requested comments. `maxItemsPerTarget` limits posts independently for every subreddit, search, or public user target.

**Comments.** `recordMode` is the canonical switch. Comments-only still reads the parent post so relationships remain valid. `maxCommentsPerPost` and `maxDepth` bound traversal; reaching either ceiling produces `CAPPED` coverage when more replies may remain.

**Sorting and time.** Reddit supports different controls on different target types. The time window mainly applies to Search and Top. Direct-thread comment sorting preserves New and Top; other selections map to Reddit Best and are reported in warnings.

**Filters and metadata.** Exclude exposed NSFW labels or exact flair labels, and keep or remove source-exposed media/link metadata. The Actor does not download media files.

**Limits.** Page, request, runtime, and projected-charge ceilings stop a broad run before it grows unexpectedly. A reached limit is visible as `CAPPED` or `PARTIAL`; it is never silently presented as complete.

#### Repeat runs & monitoring

Set `stateMode` to `changes`, keep the same target and row-affecting settings, and rerun sequentially with the same `stateNamespace`.

- The first run in Changes mode emits observed rows as `NEW`; this creates the comparable baseline.
- Later comparable run: new rows are `NEW`, meaningful edits are `UPDATED`, and identical rows stay internal by default.
- Enable `includeUnchanged` only when you need audit rows. Delivered `UNCHANGED` rows are free.
- `ENDED` is emitted only after comparable complete snapshots. Capped, partial, or failed runs do not prove disappearance.

Do not run the same monitor in parallel. Use a different namespace for each independent monitor, even when the Reddit target is identical.

### 💰 Pricing

This Actor uses pay per event. Platform usage is included in the configured event prices.

| Event | Price | When it is charged |
|---|---:|---|
| Verified source start | `$0.005` | Once, only after at least one target returns useful verified rows or a source-confirmed exhausted empty window |
| Successfully checked monitoring target | `$0.0067` | Per trustworthy, state-compatible target in Changes mode |
| Delivered post row | `$0.00099` | Per useful post or search-result row |
| Delivered comment row | `$0.00049` | Per useful comment row |

Examples:

- 25 Current post rows: `$0.005 + 25 × $0.00099 = $0.02975`
- One post and 100 comments: `$0.005 + $0.00099 + 100 × $0.00049 = $0.05499`
- One Changes target with 3 changed post rows: `$0.005 + $0.0067 + 3 × $0.00099 = $0.01467`

Invalid input is rejected before source work. Blocked or failed access, a run with no usable target collection, and an unverified empty result do not receive the start charge. Failed, skipped, unverified, or incompatible monitoring targets also do not receive the monitoring-target charge. Coverage rows, summaries, warnings, duplicate candidates, failed targets, and unchanged observations are free.

### ✅ Coverage you can trust

Every requested target receives a coverage result:

- `COMPLETE` — the configured target window was exhausted without a known cap.
- `CAPPED` — a result, page, comment, depth, reply, request, runtime, or charge ceiling was reached.
- `PARTIAL` — useful rows were delivered, but part of the requested work could not be completed.
- `EMPTY` — the selected source window returned zero rows and the Actor corroborated that empty result.
- `FAILED` — the Actor could not obtain trustworthy rows or verify an empty result.

`COMPLETE` describes the configured bounded window, not all historical Reddit content. Coverage and warnings remain available even when the dataset has no paid result rows.

### 🔌 API and automation

Run the Actor with the Apify API by replacing `ACTOR_ID` and `APIFY_TOKEN`:

```bash
curl -X POST \
  "https://api.apify.com/v2/acts/ACTOR_ID/runs?token=APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"subreddits":["apify"],"maxItemsPerTarget":25,"recordMode":"posts"}'
```

The default dataset is available as JSON, CSV, Excel, XML, RSS, or JSONL through Apify dataset endpoints. Save a stable Changes input as an Apify Task before adding a schedule, and keep executions sequential for the same monitor.

### ❓ FAQ and support

**Do I need a Reddit account, API key, or proxy settings?**\
No. The Actor uses a managed, fixed Residential-only route. Datacenter is intentionally not offered because it did not meet the reliability bar for this product.

**Why did I receive posts but no comments?**\
Check `recordMode`. The default is Posts only. Choose Comments only or Posts and comments, then review the comment and depth limits.

**Why did my first Changes run label everything NEW?**\
That run creates the comparison baseline. Repeat the same scope sequentially to receive later changes.

**Why are there fewer rows than my limit?**\
The limit is a maximum, not a promise. Review `coverageStatus`, `coverageWarnings`, and `RUN_SUMMARY` to distinguish a complete smaller window from a capped, partial, empty, or failed target.

For support, share the Apify run ID, the input with sensitive values removed, the affected target, and the relevant coverage warning.

# Actor input Schema

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

Enter community names without r/, for example apify. Use this for recent posts from one or more communities. Ignored when Search queries or Start URLs are provided.

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

Search public Reddit content for each phrase. When provided, Subreddits are ignored unless the subreddit is part of a Start URL.

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

Add public Reddit thread, subreddit, search, or user-activity URLs. When any URL is provided, Search queries and Subreddits are ignored.

## `maxItemsPerTarget` (type: `integer`):

Stops after this many posts for each subreddit, search, or user target. A direct thread returns at most one post plus any requested comments.

## `recordMode` (type: `string`):

Choose posts, comments, or both. Selecting comments adds thread requests and can increase the number of paid rows. Comments keep their post and parent relationships.

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

Used only for Comments only or Posts and comments. Stops each thread at this many comment rows and reports CAPPED if more may remain.

## `maxDepth` (type: `integer`):

Used only when comments are requested. Depth 0 is a top-level comment; depth 1 is its direct reply.

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

Applies to supported listing and search targets. For direct-thread comments, New and Top are preserved; other choices use Reddit Best and the run reports that mapping.

## `timeWindow` (type: `string`):

Applied only where Reddit supports a time window, mainly Search and Top. It has no effect on unsupported target types.

## `excludeNsfw` (type: `boolean`):

Removes posts publicly marked over-18 when Reddit exposes that flag for the selected target.

## `excludeFlairs` (type: `array`):

Remove posts whose exposed flair exactly matches one of these labels, ignoring letter case.

## `includeMediaMetadata` (type: `boolean`):

Keeps media and outbound-link metadata already present in Reddit's response. The Actor does not download media files.

## `stateMode` (type: `string`):

Current returns every observed row. Changes compares with the previous successful run of the same target scope and namespace. The first Changes run seeds state and returns observed rows as NEW.

## `stateNamespace` (type: `string`):

Keep the same value for consecutive runs of one monitor. Use a different short name when the same Actor maintains an independent monitor.

## `includeUnchanged` (type: `boolean`):

Used only in Changes mode. Off keeps recurring datasets small. When enabled, UNCHANGED rows are delivered without a post or comment result charge.

## `maxPagesPerTarget` (type: `integer`):

Stops pagination at this many source pages. If another page remains, coverage is CAPPED instead of COMPLETE.

## `maxRequests` (type: `integer`):

Run-wide ceiling across listings, threads, reply expansion, and bounded fallbacks. Lower values can produce CAPPED or PARTIAL coverage.

## `maxRuntimeSecs` (type: `integer`):

Stops additional source work after this many seconds. A stopped target is reported honestly in the coverage summary.

## `maxCostUsd` (type: `number`):

Buyer-charge guard for this run. The Actor reserves enough event capacity before starting broad source work and stops before exceeding this limit.

## Actor input object example

```json
{
  "subreddits": [
    "apify"
  ],
  "searchQueries": [
    "Apify Actors",
    "web scraping"
  ],
  "maxItemsPerTarget": 25,
  "recordMode": "posts",
  "maxCommentsPerPost": 100,
  "maxDepth": 10,
  "sort": "new",
  "timeWindow": "week",
  "excludeNsfw": true,
  "includeMediaMetadata": true,
  "stateMode": "current",
  "stateNamespace": "default",
  "includeUnchanged": false,
  "maxPagesPerTarget": 10,
  "maxRequests": 80,
  "maxRuntimeSecs": 480,
  "maxCostUsd": 1
}
```

# Actor output Schema

## `dataset` (type: `string`):

Open the Overview dataset view for the main post, comment, change, link, engagement, and coverage fields.

## `fullExport` (type: `string`):

Open the Full export dataset view when an API, CSV export, or audit needs every normalized Reddit field.

## `runSummary` (type: `string`):

Open RUN\_SUMMARY for per-target coverage, delivered row counts, warnings, and the final event-charge breakdown.

# 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": [
        "apify"
    ],
    "maxItemsPerTarget": 25
};

// Run the Actor and wait for it to finish
const run = await client.actor("luminar/reddit-scraper-changefeed-private-v1").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": ["apify"],
    "maxItemsPerTarget": 25,
}

# Run the Actor and wait for it to finish
run = client.actor("luminar/reddit-scraper-changefeed-private-v1").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": [
    "apify"
  ],
  "maxItemsPerTarget": 25
}' |
apify call luminar/reddit-scraper-changefeed-private-v1 --silent --output-dataset

```

## MCP server setup

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

```

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/vb5QRFqgbJsRIGYJ4/builds/8gxPxKKSHGhKcEkwH/openapi.json
