# Reddit Customer Research & Competitor Insights (`fascinating_lentil/reddit-posts-comments-scraper`) Actor

Analyze licensed Reddit CSV/JSON exports for US SaaS and brand research. Find pain-point phrases, feature-request cues and competitor mentions; audit supplied threads and compare snapshots. Get five CSV reports, JSON and a summary. No live scraping, proxy or AI calls.

- **URL**: https://apify.com/fascinating_lentil/reddit-posts-comments-scraper.md
- **Developed by:** [Md Jakaria Mirza](https://apify.com/fascinating_lentil) (community)
- **Categories:** Automation, Other
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 1,000 post analyzeds

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 Customer Research & Competitor Insights

Turn a properly licensed Reddit CSV/JSON export into customer research and competitor insights for US SaaS teams, product agencies and brand analysts. Review pain-point phrases, feature-request cues, literal competitor mentions, supplied thread relationships and snapshot differences — without a live Reddit request or an external AI bill.

**This is an authorized export analyzer, not a live Reddit scraper.** The former subreddit/search/post-URL collector is retired in this redesign. No API key or proxy is used because nothing is fetched from Reddit. Synthetic demos contain developer-invented text, brands, IDs and communities; they are not customer evidence or source access.

### What you get

- **Textual review cues:** transparent English phrase rules for pain points, feature requests, recommendation questions, switching phrases, pricing questions and question marks. These are prompts for human review, not sentiment scores or validated buying intent.
- **Product and brand mention comparisons:** case-insensitive literal terms, counted once per selected record. Supplied-sample shares are not market share, geographic reach or unique people.
- **Thread evidence audit:** missing parents, parent cycles, cross-thread links, unknown roots and capped selections stay explicit. Comment counts are compared with the supplied root snapshot, not presented as proof of full thread coverage.
- **Snapshot comparisons:** score, declared-comment-count and redacted-text differences for matching IDs within your selected time window. Different metric bases are not compared; scores may be fuzzed. No live growth or causation is inferred.
- **Deletion and privacy safeguards:** latest supplied tombstones and explicit removal keys suppress records and their supplied history; removed roots suppress supplied comments. Author/profile columns are dropped and common email, contact, credential, user-reference and URL patterns are redacted.
- **Five formula-guarded CSV reports, JSON evidence and a readable summary.** Each report records its source/synthetic scope. Multiple supplied observations are included in one unique-record analysis, not treated as separate results.

US refers to the intended research audience, **not a claim that the supplied authors are in the United States**. No personal profiles, outreach, AI model training, live alerts or monitoring schedule are provided.

### Choose the right mode

#### 1. Preview a clearly labeled synthetic demo

```json
{
  "mode": "demo",
  "demoPreset": "us-saas-research",
  "watchTerms": ["AtlasApp", "NimbusApp"]
}
```

The three presets are:

| Preset | Research preview |
| --- | --- |
| `us-saas-research` | Invented pain-point, feature-request and recommendation phrases |
| `us-brand-comparison` | Invented per-record mentions of AtlasApp and NimbusApp |
| `us-thread-quality` | Invented missing parents, cycles, stale times and snapshot changes |

Demo saves **zero dataset rows** and requests **zero post/comment result events**. Open **REPORT**, **REPORT.md** and the CSV artifacts in the run's output/key-value store; an empty dataset is intentional. Each run charges **$0.001 Actor Start**, with platform usage included under the current pricing. Demo is not a free run or a live-data sample.

Leave import text/provider blank and rights confirmation unchecked in demo. Nonempty import fields are rejected, not silently ignored.

#### 2. Analyze an authorized CSV or JSON export

Use this route only if your export **and derived analysis through this service** are properly licensed. Commercial Reddit use may require a separate agreement or written approval; public visibility, an API account, another scraper's output and checking a box do not establish those rights. See [Reddit Developer Terms](https://redditinc.com/policies/developer-terms) and [Data API Terms](https://redditinc.com/policies/data-api-terms).

Select `mode: "import"`, identify your authorized provider in `importSource`, and set `authorizedData: true` **only when you actually have the required rights**. Paste exactly one nonempty `importJson` array-as-text or `importCsv` string. The form marks the payload fields secret; API clients must use Apify's supported secret-input encryption if they require encrypted storage. Normalized outputs and downloads are not anonymized or automatically encrypted by that input setting.

Example observation structure below is **invented developer text, not Reddit content**:

```json
[
  {
    "recordType": "post",
    "postId": "demo001",
    "subreddit": "synthetic_saas_lab",
    "title": "What tool can replace AtlasApp?",
    "bodyText": "I wish we had better exports.",
    "createdAt": "2026-10-10T00:00:00Z",
    "observedAt": "2026-10-12T00:00:00Z",
    "score": 20,
    "declaredCommentCount": 1,
    "isNSFW": false
  },
  {
    "recordType": "comment",
    "postId": "demo001",
    "commentId": "reply01",
    "bodyText": "Please add CSV exports.",
    "createdAt": "2026-10-11T00:00:00Z",
    "observedAt": "2026-10-12T00:00:00Z",
    "parentCommentId": null,
    "score": -2,
    "isNSFW": false
  }
]
```

For JSON API input, `importJson` is a **string**, e.g. `JSON.stringify(observations)`, not a nested array. JSON input or CSV rows must be flat observations; raw nested Reddit Listing objects are not accepted. The Actor does not download files, resolve URLs, call APIs or open the supplied record IDs.

#### Export columns

| Field | Meaning and validation |
| --- | --- |
| `recordType` | Required `post` or `comment`; alias `dataType` |
| `postId` | Required lowercase base36 string, 1–20 characters; optional `t3_` prefix |
| `commentId` | Required for comments; optional `t1_` prefix. `id` can replace the type-specific ID, but do not supply both |
| `title` / `bodyText` | Active posts need a title; active comments need body. Body aliases: `body`, `selfText`. Maximum title 1,000 and body 20,000 characters |
| `createdAt` / `observedAt` | Required UTC date or Z timestamp; aliases `postedDate` / `scrapedAt`. Impossible/future dates and creation after observation are rejected |
| `subreddit` | Optional 3–21-character name, not URL; aliases `subredditName`, `communityName` |
| `score` or `upvotes` | Optional integer; negative scores remain negative. Supply one metric, never both; basis is retained |
| `declaredCommentCount` | Optional nonnegative post metric; aliases `totalComments`, `numberOfComments`. Unknown does not become zero |
| `parentCommentId` | Comment ID, or explicit null/empty for a known root comment. Omitting the field means unknown parent, not depth zero |
| `parentId` | Alternative parent fullname `t1_comment` or a matching `t3_post`; never combine with `parentCommentId` |
| `isNSFW` | Boolean; alias `over18`. Default filtering requires a safe flag or a safe supplied root for comments |
| `contentState` | `active`, `deleted` or `removed`. Latest deleted/removed states suppress the ID and earlier supplied content |

Do not provide duplicate aliases for one column. Missing optional metrics remain null. Numeric IDs, fractional scores, ranges, formulae and `12K` shorthand are rejected. Unsupported columns are dropped, not exported. Account deletion in an author column is not treated as deletion of the post itself.

CSV example, also invented:

```csv
recordType,postId,commentId,title,bodyText,createdAt,observedAt,parentCommentId,score,declaredCommentCount,isNSFW
post,demo001,,What tool can replace AtlasApp?,I wish we had exports.,2026-10-10,2026-10-12,,20,1,false
comment,demo001,reply01,,Please add CSV exports.,2026-10-11,2026-10-12,,-2,,false
```

### Selection, comparisons and data quality

Imports are bounded to **2 MiB**, **1,000 observations**, **40 columns per observation** and **500 unique IDs**. `maxRecords` defaults to 100, ranges 1–500 and applies to posts and comments **combined**. Full validation occurs before selection or saving; a conflicting same-ID/same-time observation beyond the cap still fails the input.

The whole stored Actor INPUT is additionally limited to **6 MiB including JSON/encryption overhead** and must be valid UTF-8 JSON, not JSON5. It is read as bytes through the current run's public Apify storage client, parsed natively and decrypted with Apify's official secret-input helper when configured. No arbitrary URL or alternative source is consulted. This avoids the large-export memory overhead of the local JSON5 input parser while retaining supported secret-input encryption.

Identical normalized snapshots are deduplicated. Several different times for an ID become one saved record containing its observations. The latest supplied text drives cues and mentions. For changes, the earliest earlier observation within `comparisonWindowDays` (default 30; 1–365) is compared with the latest one. No history is persisted across runs.

Selection uses first-seen eligible IDs. Caps and spending limits can omit comments, parents or roots; check `RUN_STATUS` and `QUALITY.csv` before interpreting results. A parent depth is derived only from supplied relationships. Ratios use comments having an observation at the root's supplied time and that root's declared count; an unknown/zero denominator or overcount is not repaired with invented values. Stale comment times are flagged.

A question without saved comment records means only **no comments in this saved selection**. It is not a live unanswered thread, an outreach opportunity or a current trend.

### Outputs and CSV research workflow

| Artifact | Use |
| --- | --- |
| `RUN_STATUS` | Mode, saved/omitted/excluded counts, complete/partial status, confirmed event counters and delivery warnings |
| `REPORT` | Selected normalized records, supplied snapshots and full analysis JSON |
| `INSIGHTS.csv` | Phrase cues, literal mentions, redacted excerpts and supplied parent depth |
| `THREADS.csv` | Saved thread evidence, time/count mismatches and question-without-supplied-comment flag |
| `MENTIONS.csv` | Per-record literal term comparisons in the supplied selection |
| `CHANGES.csv` | Bounded supplied score/count/text comparisons with limitations |
| `QUALITY.csv` | Missing evidence and relationship-quality warnings |
| `REPORT.md` | Readable summary with synthetic/source and method labels |

First check status/provenance, then review quality warnings. Filter insights by phrase cue and read the authorized original context separately where your license permits. Compare mentions only within a consistent supplied selection; join snapshot differences by `recordKey` and inspect the from/to times and metric basis. Treat these outputs as research aids for human review, not decision-ready customer intent or a representative market sample.

CSV cells with formula-like prefixes are escaped, including negative-value cells. Some spreadsheet programs therefore read negative metrics as text; explicitly convert trusted numeric columns if needed. The full dataset stores numeric scores. Protect every exported file under your source license.

### Fees and spending controls

Pricing is **$2/1,000 confirmed saved imported post/comment analyses plus $0.001 automatic Actor Start per run**. Platform usage is included under this pricing. There is **no additional dataset-item fee or tier discount**. Always check the current Apify pricing panel before starting.

The retained event IDs are `post-scraped` and `comment-scraped`, but their meaning here is **a confirmed saved imported analysis**, not live collection. One unique ID includes its supplied observations and research reports. Duplicates, omitted IDs and separate report files do not create extra result events.

| Successfully saved selection | Listed fee, including start |
| --- | --- |
| Synthetic demo: zero saved imported records | $0.001 |
| One imported post or comment | $0.003 |
| 100 imported posts/comments combined | $0.201 |

The **$0.003 minimum spending-cap setting** is the lowest allowed user budget setting, **not an additional fee or minimum actual charge**. A demo still charges only $0.001. A cap of $0.003 permits the start event and one saved imported analysis; further eligible records are omitted with an explicit partial status.

The runtime checks both the configured spending cap and SDK limits before each save, including an explicit zero-dollar cap. Private/non-PPE runs save without inventing custom event fees. If a save/charge acknowledgment is uncertain, there is **no automatic retry**: the run fails, warns that a row or charge may already have persisted, and reports only confirmed rows. A report-write failure also fails the run; previously saved records may still retain applicable fees. A spending-cap stop has an explicit partial-output status.

No source/proxy/AI charges are requested by this runtime. The automatic start event is controlled by Apify and is not waived by an empty dataset or failed import. Validation failures can therefore still incur the start fee. Successfully saved records can retain their applicable fees if a later save/report step fails.

The release uses **256 MB** with a **90-second default timeout**. Eleven approved private cloud checks verified the three demo presets, JSON/CSV imports, encrypted near-2 MiB histories and actual start/result/cap billing. Maximum observed cloud memory was about 48.3 MiB. A 500-record/1,000-observation case was checked locally, not at that scale in the cloud. These checks do not guarantee availability or profit for every workload, failed run or future platform change.

### Privacy, source rights and retention

Do not submit private messages, private/quarantined-community content, sexually explicit content, credentials, personal profiles or data for surveillance, sensitive-group targeting or harassment. Use only material licensed for this processing and any resulting reports. This Actor's rights confirmation **does not verify or grant authorization**.

Author/profile columns are not retained. Common identifying/credential patterns in title/body/provider labels are redacted, but pattern matching is **not complete anonymization**: names, unusual identifiers, post IDs or context may still identify someone. Review and minimize inputs before uploading. The original input can remain in the run's key-value store according to Apify storage settings; dropping output columns does not erase INPUT.

Latest supplied deletion markers and `removedRecordKeys` suppress this run's records and their supplied history. The Actor does not detect fresh Reddit deletions or delete older runs, downloaded files or prior reports. Maintain your license-required deletion feed and promptly purge affected historical copies yourself. See [Reddit Public Content Policy](https://support.reddithelp.com/hc/en-us/articles/26410290525844-Public-Content-Policy).

Independent tool; not affiliated with or endorsed by Reddit. Apache-2.0 covers this code, **not rights to imported content**.

### Migration from the old collector

Remove old subreddit, keyword, URL, sort, pagination, proxy and API/AI credential fields. They are rejected with `LEGACY_INPUT_RETIRED`, before any Reddit lookup or result write. Choose an explicit synthetic demo or migrate to an authorized export. Do not expect old saved tasks or live-collection workflows to continue.

This scope change is disclosed rather than presented as a working live scraper. No duplicate Actor/account, transferred reputation, fake runs or artificial reviews are part of this upgrade.

### Local verification

Node.js 20 or newer:

```text
npm ci
npm test
npm run lint
npm run audit:prod
apify validate-schema
```

No source collection is required by those tests. Release gates additionally check compiled Actor reports, synthetic labeling, result/budget acknowledgments, memory headroom and measured economics on an explicitly approved staging build. Local success alone is not a cloud reliability or legal-compliance guarantee.

# Actor input Schema

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

Choose explicitly. Legacy subreddit/search/post URL/proxy inputs receive a migration error. Demos save zero dataset rows and request no post/comment result events, but platform usage and any configured start fee still apply.

## `demoPreset` (type: `string`):

Invented posts, comments, brands and subreddit. Not source access, real customers, location or verified intent. US describes the workflow audience, not users' locations. Ignored in import mode.

## `importJson` (type: `string`):

Import only. Paste a flat JSON ARRAY as text; exactly one nonempty JSON/CSV import. Maximum 2 MiB, 1,000 observations and 500 unique IDs. Required: recordType, postId, createdAt, observedAt, plus title for posts or commentId/bodyText for comments. See README. No URLs or files are fetched.

## `importCsv` (type: `string`):

Import only. Maximum 2 MiB, 1,000 data rows, 40 columns and 500 unique IDs. Quoted multiline fields supported. Use UTC dates/timestamps and integer scores without formulae, ranges or shorthand. Supply safe isNSFW flags or a safe root post for comments. Never submit private messages, credentials or personal profiles.

## `importSource` (type: `string`):

Required for import; 1–160 characters. Identify your licensed export. Do not put names, contact information or secrets here. A source label is not proof of rights, accuracy, freshness or a Reddit commercial agreement. Leave blank in demo.

## `authorizedData` (type: `boolean`):

Required TRUE for import only; never auto-confirmed by an example. Public visibility, a Reddit/API account, another scraper's output or this checkbox do not grant permission. Your license must cover commercial processing here and applicable redistribution, privacy and deletion requirements. Leave unchecked for demo.

## `maxRecords` (type: `integer`):

Combined post/comment cap, not per subreddit or observation. First-seen eligible IDs are selected after full validation. Each saved ID includes its supplied snapshots. Omitted records are not saved or billed; caps can leave incomplete parent/thread evidence. Demo uses this cap for preview only and has zero dataset rows.

## `watchTerms` (type: `array`):

Optional: up to 10 unique non-sensitive literal terms, 2–40 characters each. Case-insensitive boundary matching in the latest supplied text. Counts are per selected record, not market share, sentiment, unique people or verified buying intent. Do not enter names, usernames, contacts or sensitive-group labels.

## `comparisonWindowDays` (type: `integer`):

Compare the earliest earlier observation within this gap to the latest observation for the same ID. Score deltas require a matching supplied metric basis. No live checking, cross-run history, forecast or notification. Scores may be fuzzed.

## `skipNSFW` (type: `boolean`):

Default true excludes explicit NSFW at any supplied time and unknown flags, unless a comment inherits a safe root. Root flags protect dependent comments. False permits unknown/flagged exports; it is not authorization to submit sexually explicit or otherwise prohibited content.

## `removedRecordKeys` (type: `array`):

Import only: up to 1,000 t3_postId / t1_commentId keys. Suppresses that ID and its supplied history; removed roots suppress comments in their supplied thread. Does not discover new Reddit deletions or erase previous datasets, INPUT, reports or downloads. Purge affected historical copies separately. Leave empty in demo.

## Actor input object example

```json
{
  "mode": "demo",
  "demoPreset": "us-saas-research",
  "authorizedData": false,
  "maxRecords": 100,
  "watchTerms": [],
  "comparisonWindowDays": 30,
  "skipNSFW": true,
  "removedRecordKeys": []
}
```

# Actor output Schema

## `status` (type: `string`):

No description

## `report` (type: `string`):

No description

## `insightsCsv` (type: `string`):

No description

## `threadsCsv` (type: `string`):

No description

## `mentionsCsv` (type: `string`):

No description

## `changesCsv` (type: `string`):

No description

## `qualityCsv` (type: `string`):

No description

## `markdown` (type: `string`):

No description

## `savedRecords` (type: `string`):

No description

# API

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

## JavaScript example

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

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

// Prepare Actor input
const input = {
    "mode": "demo"
};

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

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

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

```

## Python example

```python
from apify_client import ApifyClient

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

# Prepare the Actor input
run_input = { "mode": "demo" }

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

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

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

```

## CLI example

```bash
echo '{
  "mode": "demo"
}' |
apify call fascinating_lentil/reddit-posts-comments-scraper --silent --output-dataset

```

## MCP server setup

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

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

## OpenAPI specification

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