# Reddit Scraper: Posts, Comments & Search (`kumo-data/reddit-scraper`) Actor

Scrape public Reddit posts, comments and search results with no login or API key. Paste subreddits, URLs, post IDs or old RSS links. Pay only for what is saved: $1.50 per 1,000 posts, $0.50 per 1,000 comments, no start fee. Exact limits. Schedules return only new posts.

- **URL**: https://apify.com/kumo-data/reddit-scraper.md
- **Developed by:** [Kumo](https://apify.com/kumo-data) (community)
- **Categories:** Social media, Automation, Lead generation
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.20 / 1,000 post saveds

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?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

Reddit Scraper collects public Reddit data: subreddit feeds, Reddit search (site-wide or inside subreddits), single posts and their comment threads, with no login and no API key. Paste any Reddit link, a subreddit name, a post ID or even your old RSS feed URL. You pay only for posts and comments saved to your dataset. Runs that collect nothing cost nothing.

### Why this scraper

- **One input for everything.** `python`, `r/python`, `https://www.reddit.com/r/python/top/?t=week`, a post URL, `https://redd.it/1xm7oif`, `1xm7oif`, a search URL, `https://www.reddit.com/r/python/new/.rss`: all go into the same **Targets** list.
- **Pay per result, nothing else.** No start fee, no charge for duplicates, filtered posts, deleted comments, problem reports, or empty and blocked runs.
- **Honest status.** Every problem target (private, missing, blocked, feed ended early) is a row in the dataset with a reason and a hint. The run's status line says what was collected and what was not. A run that Reddit blocked completely ends as FAILED and costs $0.
- **Exact limits.** `maxTotalPosts` and the run's charge limit are never exceeded, not even by one item.
- **Schedules without duplicates.** `newSinceLastRun` remembers what each target already showed, so an hourly schedule delivers only new posts: no overlap, no gaps, no date math.
- **Replacement for Reddit RSS** (Reddit turns RSS off on 2026-11-13) and for other Reddit scrapers: paste their input as is; `outputFormat: rss` gives the items n8n's RSS Read node made of Reddit feeds, `outputFormat: trudax` the rows of trudax/reddit-scraper-lite.

### What people use it for

- **Brand and product monitoring.** Search for your product name and get new mentions on a schedule.
- **Market and audience research.** Pull what a community says about a topic, with scores and comments.
- **Keeping automations alive after Reddit RSS ends.** Feed new posts to n8n and other tools.

### Quick start

Latest 100 posts of a subreddit:

```json
{ "targets": ["r/python"] }
```

A week's top posts of two subreddits, with the 20 best comments each, top-level only (the cheapest comment mode):

```json
{
  "targets": ["https://www.reddit.com/r/python/top/?t=week", "r/learnpython/top"],
  "timeWindow": "week",
  "maxPostsPerTarget": 25,
  "includeComments": true,
  "maxCommentsPerPost": 20,
  "maxCommentDepth": 0
}
```

Search for a product across Reddit, last month, only posts that really mention it:

```json
{
  "searchQueries": ["\"standing desk\""],
  "sort": "new",
  "timeWindow": "month",
  "filterKeywords": ["standing desk"],
  "fullText": true
}
```

Every hour, only what is new (schedule this):

```json
{
  "targets": ["r/SaaS", "r/startups"],
  "newSinceLastRun": true,
  "maxPostsPerTarget": 0,
  "postedAfter": "2 hours"
}
```

### Input

| Field | What it does |
|---|---|
| `targets` | Subreddits, posts, search pages, RSS feed URLs. Sort and time window inside a URL win over the fields below. |
| `searchQueries` | Reddit search queries. Reddit operators work: `"exact phrase"`, `OR`, `NOT`, `title:`, `author:`, `flair:`, `self:yes`. |
| `searchIn` | Run every query inside these subreddits instead of all of Reddit. |
| `sort`, `timeWindow` | `new`, `hot`, `top`, `rising`, `controversial` for feeds; `new`, `hot`, `top`, `relevance`, `comments` for search. A sort that does not fit a target falls back to `new`, with a warning. |
| `postedAfter`, `postedBefore` | Date filters for any sort: `2026-10-01`, `2026-10-01T12:00:00Z`, or relative `30 minutes`, `6h`, `2 weeks`. A `new` feed stops reading once it is past `postedAfter`. |
| `newSinceLastRun` | Only posts the target has not shown in earlier runs. |
| `maxPostsPerTarget` | Default 100. `0` means as many as Reddit shows. |
| `maxTotalPosts` | A hard cap for the whole run. `0` means no cap. |
| `includeComments`, `maxCommentsPerPost`, `commentSort`, `maxCommentDepth` | Comments in reading order. `maxCommentDepth: 0` gives top-level comments only and is the cheapest mode. |
| `includeStickied` | Pinned moderator posts are dropped by default. |
| `filterKeywords`, `filterKeywordMode` | Keep only posts whose title, text or flair contain the words (whole words, any or all). |
| `fullText` | Open each post's page for the full text and fields the feed card lacks. On automatically with comments. |
| `outputFormat` | `default`, `trudax` or `rss`. |

`0` always means "no limit". Fields from other Reddit scrapers (`startUrls`, `searches`, `maxItems`, `maxPostCount`, `skipComments`, `time`, ...) are accepted and listed in the run summary under `inputWarnings`.

### Output

One dataset; the `type` field tells rows apart (`post`, `comment`, `target`). The console views **Posts**, **Comments** and **Problems** pick columns for each kind but show every row, so filter by `type` in your sheet, Zapier step or code. With `outputFormat` `trudax` or `rss` rows use those field names and the views stay mostly empty; use the JSON or CSV export.

A post:

```json
{
  "type": "post",
  "id": "1xm7oif",
  "url": "https://www.reddit.com/r/Python/comments/1xm7oif/fastapi_cloud_is_now_generally_available/",
  "title": "FastAPI Cloud is now generally available",
  "body": "I'm excited to share with you all that FastAPI Cloud is now generally available: ...",
  "author": "tiangolo",
  "subreddit": "Python",
  "score": 70,
  "upvoteRatio": 0.9,
  "commentCount": 12,
  "createdAt": "2026-11-02T19:57:30Z",
  "postType": "text",
  "flair": "News",
  "mediaUrls": [],
  "isNsfw": false,
  "target": "r/python",
  "listing": "new",
  "rank": 2,
  "scrapedAt": "2026-11-04T09:00:07Z"
}
```

A comment carries `postId`, `postTitle` and `postUrl`, so every row reads on its own in a sheet or a Slack message. `parentCommentId` is `null` for top-level comments, `depth` starts at 0.

A problem row (only when something went wrong):

```json
{
  "type": "target",
  "target": "r/python",
  "status": "partial",
  "reason": "feed_end_before_window",
  "message": "Reddit's 'new' feed ends after ~1000 posts. Reached 2026-02-26T07:14:09Z; postedAfter is 2025-01-01T00:00:00Z. Delivered 954 of up to 5000 posts.",
  "hint": "Older posts are reachable only as a sample: add searchQueries with searchIn: [\"r/python\"]."
}
```

Every row of one type has the same keys; unknown values are `null`, lists are `[]`. Times are UTC ISO 8601 with `Z`. IDs have no `t3_`/`t1_` prefix.

The run summary (key-value store record `SUMMARY`) lists every target with its status, counts, the oldest and newest post reached, requests made, and what was charged.

### Pricing

| Event | Free plan | Paid plans |
|---|---|---|
| Post saved to the dataset | $1.50 per 1,000 | $1.20 per 1,000 |
| Comment saved (not deleted) | $0.50 per 1,000 | $0.40 per 1,000 |

Examples on the Free plan:

| Job | Cost |
|---|---|
| 100 latest posts | $0.15 |
| 1,000 posts with 20 comments each | $11.50 |
| Hourly schedule for a month, 3,000 new posts in total | $4.50 |

Nothing else is charged. The status line shows the most a run can cost before it starts. Set a maximum cost per run in the run options and the scraper stops exactly there.

### Limits of public Reddit pages

- **A feed shows about 1,000 posts** (`top` and `controversial`: about 500 per time window). Older posts of a busy subreddit are not in any feed; the problem row says how far back the feed went. Reddit search reaches further: combine `searchQueries` with `searchIn`.
- **A search returns about 250 results** per query and sort. Split a broad query into narrower ones.
- **Posts marked 18+ are never collected**: they are dropped and not charged, and an 18+ community stops after two pages.
- **Private communities** are not readable without login. Reddit shows logged-out visitors the same page for a private and a missing community, so both are reported as `not_found_or_private`.
- **Search results show fewer fields** than feed cards: post type, flair and text come with `fullText: true`.
- **User profiles and community info** are not scraped yet.

### Moving from Reddit RSS or another scraper

- Put your RSS URL (`https://www.reddit.com/r/<sub>/new/.rss`) into `targets`, set `outputFormat: "rss"` and `newSinceLastRun: true`: each run returns only new posts as items with the keys and date format of n8n's RSS Read node on a Reddit feed (`title`, `link`, `pubDate`, `author`, `content`, `contentSnippet`, `id`, `isoDate`). Checked against real Reddit feeds: titles, links, authors, ids and dates match exactly; `contentSnippet` matches up to line breaks and spaces; `content` is equivalent HTML, not byte-identical. Make and Zapier RSS modules and Reddit's comment feeds have not been compared yet.
- Paste a trudax/reddit-scraper-lite input as is and set `outputFormat: "trudax"`: rows come with trudax field names (`dataType`, `username`, `communityName`, `upVotes`, `numberOfComments`, ...). Search queries also run when start URLs are present.

### FAQ

#### Why did my run return fewer posts than I asked for?

Reddit shows about 1,000 posts per feed and about 250 per search. The Problems view says where a target stopped and what to try next.

#### Why did my run fail?

If Reddit blocks every request, the run ends as FAILED and costs $0. Try again later. If it keeps happening, open an issue with the run ID.

#### Can I get old posts?

Only as far as Reddit shows them. Combine `searchQueries` with `searchIn` to reach further back in a subreddit.

#### Can I scrape private communities or user profiles?

No. The Actor reads only what a logged-out visitor sees. It does not use accounts.

### Responsible use

You are responsible for how you use the data and for following the laws and terms that apply to you, including data protection rules such as GDPR. Reddit data includes usernames. Collect only what you need and do not use it to identify or profile people. This is not legal advice.

The Actor keeps no data outside your Apify account. With `newSinceLastRun` it stores, per target, the time of the last run and the IDs of posts already returned in the key-value store `reddit-scraper-state` in your account.

This Actor is an independent tool. It is not affiliated with, endorsed by, or sponsored by Reddit, Inc.

### Support

Open an issue with the run ID. The `SUMMARY` record and the problem rows usually tell what happened; we read every issue.

# Actor input Schema

## `targets` (type: `array`):

Subreddits, posts or search pages, one per line. Any form works: `python`, `r/python`, `https://www.reddit.com/r/python/top/?t=week`, an RSS URL `https://www.reddit.com/r/python/new/.rss`, a post URL, `https://redd.it/1xm7oif`, a bare post ID `1xm7oif`, a search URL. Sort and time window inside a URL win over the fields below for that target. Private and quarantined communities and user profiles are not available without login; such targets are reported in the Problems view, never charged. Posts marked 18+ are never collected.

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

Each query is a separate search. Reddit search operators pass through: `"exact phrase"`, `OR`, `NOT`, `title:`, `selftext:`, `author:`, `flair:`, `self:yes`, `subreddit:`.

## `searchIn` (type: `array`):

Restrict every search query to these communities (`python`, `r/python` or a URL). Each query runs in each community. Empty: search all of Reddit.

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

Reading order for feeds and searches. A sort that does not apply to a target falls back to `new`; the fallback is reported in `inputWarnings`.

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

Reddit's own time filter. Applies to `top`, `controversial` and searches only. For an exact range use the date fields below.

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

Keep posts created at or after this moment. A date `2026-10-01`, a date-time `2026-10-01T12:00:00Z`, or a relative value counted from the run start: `30 minutes`, `6 hours`, `1 day`, `2 weeks`, `3 months` (short forms `30m`, `6h`, `1d`, `2w`). With sort `new` the Actor stops reading at the first older post, so you pay only for posts in the window.

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

Keep posts created before this moment (exclusive). Same formats as Posted after.

## `newSinceLastRun` (type: `boolean`):

Return only posts that earlier runs with the same targets have not returned. Made for schedules and 'new item' triggers: no duplicates, no gaps, no date math. State is kept in the key-value store `reddit-scraper-state` in your account. The first run behaves normally.

## `maxPostsPerTarget` (type: `integer`):

Posts to take from each subreddit feed or search. `0` reads to the end of what Reddit serves. Not used for post URLs.

## `maxTotalPosts` (type: `integer`):

Hard cap on posts across all targets. Exact: the Actor never saves or charges a post beyond it. `0` means no cap. The platform's 'Maximum charge per run' also stops the run cleanly.

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

Collect comments for every post. Opens each post page, so the post text becomes complete too.

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

Comments to take from each post, in the order a reader sees them with the chosen sort. `0` means all comments.

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

Reddit's comment ordering. It decides which comments come first when a limit cuts the thread.

## `maxCommentDepth` (type: `integer`):

`0` keeps only top-level comments: the fastest and cheapest mode. `1` adds direct replies, and so on. Empty: no limit. Matches the `depth` field of comment items.

## `includeStickied` (type: `boolean`):

Keep moderator-pinned posts in feeds. Off by default: pinned announcements are often years old and pollute 'newest posts'. Dropped posts are not charged.

## `filterKeywords` (type: `array`):

Words or phrases that must appear in the title, text or flair (case-insensitive). Use it when Reddit search returns loosely related posts. Dropped posts are not charged.

## `filterKeywordMode` (type: `string`):

`any`: one keyword is enough. `all`: every keyword must appear.

## `fullText` (type: `boolean`):

Open every post page to get the complete text and page-only fields (edit time, gallery media). Slower. Implied by Include comments. Off: text comes from the feed and `bodyIsPreview` is true.

## `outputFormat` (type: `string`):

Shape of post and comment items. `trudax`: drop-in for workflows built on trudax/reddit-scraper-lite. `rss`: the items n8n's RSS Read node made of Reddit RSS feeds, which Reddit shuts down on 2026-11-13 (same keys and dates; text equivalent). Target items and the SUMMARY record always use the default shape.

## Actor input object example

```json
{
  "targets": [
    "r/python"
  ],
  "sort": "new",
  "timeWindow": "all",
  "newSinceLastRun": false,
  "maxPostsPerTarget": 100,
  "maxTotalPosts": 0,
  "includeComments": false,
  "maxCommentsPerPost": 50,
  "commentSort": "best",
  "includeStickied": false,
  "filterKeywordMode": "any",
  "fullText": false,
  "outputFormat": "default"
}
```

# Actor output Schema

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

No description

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

No description

## `problems` (type: `string`):

No description

## `summary` (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 = {
    "targets": [
        "r/python"
    ]
};

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

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

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,kumo-data/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/3XlDJAizfo2DEQdmF/builds/8rDy6hfhsuSa0QHoC/openapi.json
