# Reddit Scraper (`spookyweb/reddit-scraper`) Actor

🏷️ From $0.60 / 1K | Scrape Reddit posts and comments from subreddits, user histories, keyword searches for brand mentions, and permalinks you paste. Billed per unique post after dedupe, $1 per 1,000. Set postedAfter and a scheduled run skips the history it already holds. No API key, no login.

- **URL**: https://apify.com/spookyweb/reddit-scraper.md
- **Developed by:** [丂卩ㄖㄖҜㄚ](https://apify.com/spookyweb) (community)
- **Categories:** Social media, AI, Automation
- **Stats:** 3 total users, 2 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.60 / 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.
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

You are billed for unique posts delivered, not for rows stored and never for the same post id twice. Scrape a subreddit, an account's history, a keyword search across the whole of Reddit, or a permalink you paste, mixed together in one run. Set a posted window and a scheduled job collects what is new rather than buying the history again, because out of range posts are dropped after the dedupe and before a single comment is fetched. A single Reddit listing still dies at roughly a thousand posts; this walks the other routes and charges you once for what comes back. No API key, no login, no Reddit account.

### 🔍 What does Reddit Scraper do?

You tell it what to collect and how many posts you want. It gives you that many **unique posts** as rows: title, body, author, score, upvote ratio, comment count, flair, media, permalink and the exact time each one was posted. Turn on comments and each post's thread comes back nested inside its row.

There are three ways to say what to collect, and they work on their own or together in one run:

- **Subreddits.** Name them in `subreddits` and get their posts, as deep as you ask for.
- **Reddit users.** Name accounts in `redditUsers` and get each one's post history as rows, with its profile and its whole comment history alongside.
- **The whole of Reddit.** Put a word in `keywords`, turn on `searchAllReddit`, and every subreddit is searched rather than only the ones you named. This is the brand mention search.

The easiest way to try it is to leave everything alone, put one subreddit into `subreddits` and click Start. The defaults are a working job.

Three things are worth understanding before you run it, because they are the whole product.

**There is no thousand row wall.** Any single Reddit listing runs out at roughly a thousand posts and then returns nothing at all, forever, however you page it. That ceiling belongs to Reddit and every tool that reads one listing inherits it. This Actor does not read one listing. It keeps going until you have the number you asked for or the subreddit genuinely has no more to give. Measured on r/programming on 29 August 2026, asking for 3,000 posts with comments off: the `new` listing on its own ran out at 995 posts, and the run returned 2,718 unique ones reaching back to June 2006, with 728 duplicates removed before charging. Adding two keywords filled the whole 3,000 rather than running out. Every row says which listing produced it in `foundVia`, so the run can be checked rather than believed.

**One run, not many.** No splitting a job across several runs with different sort settings. No manual passes. No downloading four files and reassembling them yourself, then discovering they overlap. You ask once and you get one dataset.

**Every row is unique, and you pay once for it.** The same post shows up in several of those listings, which is exactly why the plan reaches so far. Before anything is delivered or charged, posts are deduplicated on Reddit's own post id, so the number on your bill is the number of usable rows in your dataset. Duplicates are counted and reported, and they cost you nothing.

### 🧭 How does this compare with Reddit's own API?

Reddit publishes a real [Data API](https://www.reddit.com/dev/api/), and it is properly documented. If you are building an application against Reddit, use it. It needs a Reddit account, an app registered for a client id and secret, your own paging code, and a commercial agreement if the use is commercial.

What it does not do is get you past the ceiling. A listing served by the official API stops at the same place, because the limit is a property of Reddit's listings rather than of the credential reading them. Getting well past that ceiling is a design problem either way, and it is the problem this Actor has already solved.

So: one input, one run, tabular output, a documented schema, nothing to register and no keys to manage, and it plugs into everything else on Apify. That is what you are buying.

### 📊 What data can I extract from Reddit?

One row per unique post.

| | Field | What it holds |
|---|---|---|
| 🆔 | `id` | Reddit's own post id, in the `t3_` form. Unique across the whole dataset |
| 📝 | `title` | The post title as submitted |
| 📝 | `body` | The post text for a self post, line breaks preserved. Null on a link post |
| 👤 | `author` | The username that posted it |
| 📢 | `subreddit` | The subreddit it came from |
| 🔢 | `score` | Upvotes minus downvotes when the post was read |
| 🔢 | `upvoteRatio` | Share of votes that were upvotes, 0 to 1 |
| 🔢 | `numComments` | How many comments Reddit reports on the post |
| 📅 | `createdAt` | When it was posted, ISO 8601 UTC |
| ✅ | `isNsfw` | Whether Reddit marks it over 18 |
| 🏷️ | `flair` | The subreddit's own flair on the post |
| 🖼️ | `media` | Attachment kind, url, thumbnail, and every image of a gallery |
| 🔗 | `url` | Where the post points |
| 🔗 | `permalink` | The post's own page on Reddit |
| 🧭 | `foundVia` | Which listing first produced this post |
| 💬 | `comments` | The comment thread, nested, when you asked for it |
| 🧾 | `rowType` | `post` for a post, `user` for the profile of an account you named |
| 📢 | `subredditInfo` | The subreddit's size, description, age, over 18 flag and rules |
| 👤 | `authorProfile` | The account behind the row: karma, age and status |
| 🏷️ | `authorFlair` | The flair the subreddit shows beside the author, which often marks a role or a speciality. Null where the subreddit uses none |
| ✅ | `isSelf` | Whether the post is text written on Reddit rather than a link out. A link post carries its target in `url` and has a null body |
| 🔗 | `domain` | Where a link post points, as a bare host, or `self.<subreddit>` for a text post. Group on this to see which sources a community shares |
| ✅ | `spoiler` | Whether the post is marked as a spoiler |
| 📌 | `stickied` | Whether a moderator pinned the post to the top of the subreddit. Usually rules and announcements rather than conversation |
| 🔢 | `totalAwards` | How many awards the post carries, the cheapest proxy for a post a community rated highly |
| 🔢 | `crossposts` | How many times the post has been shared into another subreddit |
| 🧾 | `removedBy` | Who removed the post, as Reddit categorises it, such as moderator or automod. Null on a post that stands |
| 📅 | `editedAt` | When the post was last edited, ISO 8601 UTC. Null on a post that has never been edited |

Each comment carries `id`, `author`, `body`, `score`, `createdAt`, `parentId`, `depth`, `permalink`, and `postId`, `postTitle` and `subreddit` saying which post it was written on.

### 💡 Why scrape Reddit in bulk?

**Training and evaluation data.** A subreddit's full history as clean rows, with scores and timestamps attached, so quality can be filtered on rather than assumed.

**Sentiment over time.** Pull years rather than weeks. A thousand posts is a few months on a busy subreddit and it cannot show you a trend.

**Product and brand mentions.** Add keywords and the run searches for them, reading each one four different ways, which reaches posts far older than any listing will show. Turn on `searchAllReddit` and the same search runs across every subreddit rather than a list you had to guess at.

**One account, end to end.** Name a user and get everything they have posted, everything they have commented, and the profile behind it, in one dataset. Moderation, research and due diligence all start there.

**Community research.** Flair, upvote ratio and comment counts together describe what a community rewards, not just what it says.

**Archiving.** Threads get deleted and subreddits get locked. A dataset with permalinks and timestamps is a record that survives that.

**Feeding an agent.** The output schema is declared, so an LLM pipeline can chain this Actor's rows without anyone writing a parser first.

### 🔎 How do I find every mention of a brand on Reddit?

Put the brand, product or person into `keywords`, turn on `searchAllReddit`, and leave `subreddits` empty. The run then searches every subreddit there is rather than a list you had to guess at in advance, which is the difference between finding the conversations you expected and finding the ones you did not.

That matters because the mentions that cost you money are rarely in the subreddit you were watching. A measured run on one product name returned 300 posts from **151 different subreddits**, none of them named in the input and most of them nowhere near an obvious place to look.

Each row is a full post: the title, the body, who wrote it, the score, the upvote ratio, the comment count and the exact timestamp. Turn on `includeComments` and the thread comes with it, so you get the replies as well as the mention. Schedule the same run daily and each dataset is that day's mentions.

You can also do both at once: name the subreddits you already watch closely and turn `searchAllReddit` on. The search runs across Reddit first, then the subreddits you named are walked, and the whole lot arrives deduplicated as one dataset.

### 📂 An example run you can check

The run behind the numbers on this page is public. No account, no token.

- [The dataset, 2,718 rows as JSON](https://api.apify.com/v2/datasets/QfiU1FH7yYtMUU5dC/items?format=json)
- [The same rows as CSV](https://api.apify.com/v2/datasets/QfiU1FH7yYtMUU5dC/items?format=csv)

What produced it, so you can repeat it exactly:

| | |
|---|---|
| Input | `{"subreddits": ["programming"], "maxPosts": 3000, "includeComments": false}` |
| Ran | 29 August 2026, 66 seconds |
| Unique posts delivered | 2,718 |
| Duplicates removed before charging | 728 |
| Oldest post reached | 29 June 2006 |
| What it cost | $2.72, being 2,718 unique posts at $0.001 on the free plan |

Group the rows by `foundVia` and the claim on this page checks out: the `new`
listing on its own produced 995 rows and then had nothing left, and the rest
came from `top:all`, `controversial:all`, `controversial:year` and `top:year`.
That is the thousand row wall, and the run walking around it.

### 👤 How do I scrape a Reddit user's history?

Put usernames into `redditUsers`, with or without the `u/` prefix. For each account you get:

- **Every post they have made**, as ordinary rows, with the same fields as any other post.
- **Their whole comment history**, up to 500 comments, each one carrying the title and id of the post it was written on so it reads on its own.
- **Their profile**: when the account was registered, its post and comment karma, whether it moderates, and whether Reddit marks it as verified.

The profile arrives as one extra row with `rowType` set to `user`, charged at the same rate as a post. You pay for the account's posts, its profile row, and any comments you asked for, at the rates below.

**A gone account is answered, not guessed at.** Suspended, deleted and never registered accounts are each reported by name in the run's status message, and the run finishes successfully with everything the other accounts returned. You are never charged for an account that is not there, and one bad username never costs you the rest of the list.

### 📢 What can I learn about the subreddits themselves?

Turn on `includeSubredditInfo` and every row picks up the subreddit's own details: the subscriber count, the public description, the date it was created, whether it is marked over 18, and the full list of rules the moderators post. The same details land in the run summary, so a run across fifty subreddits doubles as a comparison of them.

It costs nothing. Subreddit details are context on a row you were already paying for, not an extra row.

### 🚀 How do I use Reddit Scraper?

1. Click **Try for free**.
2. Put one or more subreddit names into `subreddits`. Plain names such as `programming`, one per line. An `r/` prefix or a full link works too. Or name accounts in `redditUsers`, or leave both empty and search all of Reddit instead.
3. Set `maxPosts` to the number of posts you want back in total.
4. Add two or three `keywords` if you want to reach further back than the recent history, and turn on `searchAllReddit` to run them across every subreddit rather than only the ones you named.
5. Turn on `includeComments` if you want each post's thread with it, and set `maxCommentsPerPost` to keep rows a sensible size.
6. Turn on `includeSubredditInfo` if you want each subreddit's size, description and rules attached to its rows.
7. Click **Start**, then download the results as JSON, CSV or Excel, or read them from the API.

### ⬇️ Input

```json
{
  "subreddits": ["MachineLearning", "LocalLLaMA"],
  "keywords": ["fine-tuning", "quantization", "benchmark", "inference"],
  "maxPosts": 5000,
  "includeComments": false,
  "sortNewestFirst": true
}
```

| Field | Type | Default | What it does |
|---|---|---|---|
| `subreddits` | array | `["programming"]` | Subreddit names, one per line. Up to 50 per run |
| `redditUsers` | array | empty | Usernames, one per line. Each returns its posts, profile and comment history. Up to 20 per run |
| `keywords` | array | empty | Search terms. They reach posts older than any listing shows |
| `searchAllReddit` | boolean | `false` | Run the keywords across every subreddit instead of only the named ones |
| `maxPosts` | integer | `1000` | Unique posts to return in total, up to 100000. This is what you pay for |
| `includeComments` | boolean | `false` | Bring each post's comment thread back nested in its row |
| `maxCommentsPerPost` | integer | `50` | Cap on comments per post, in Reddit's top order |
| `includeSubredditInfo` | boolean | `false` | Attach each subreddit's size, description, age and rules to its rows |
| `sortNewestFirst` | boolean | `true` | Order rows by post date, newest first |

`maxPosts` is a total across everything in the run, not a cap per subreddit or per account, so three subreddits at 9000 returns 9000 rows and no more.

That total is spread across everything you named rather than spent in order, so one busy subreddit cannot take the whole run. Your keywords are searched first, and the named subreddits are then walked a branch at a time each rather than one subreddit at a time. Measured on six subreddits with five keywords at `maxPosts` 600: five of the six returned rows, and all 600 came from the keyword searches rather than from the first subreddit's recent feed.

A brand mention search needs no subreddit at all:

```json
{
  "keywords": ["your brand name"],
  "searchAllReddit": true,
  "maxPosts": 500
}
```

And an account's history needs nothing but the name:

```json
{
  "redditUsers": ["spez"],
  "maxPosts": 1000
}
```

### ⬆️ Output

#### Table view

Results arrive as a **Posts** table you can sort and filter in the Console, with the date, subreddit, title, author, score, upvote ratio, comment count, flair, a direct link and the listing each post was found through lined up in columns.

#### JSON

A typical row:

```json
{
  "rowType": "post",
  "id": "t3_1w06vn1",
  "title": "An example post title",
  "body": null,
  "author": "example_user",
  "subreddit": "programming",
  "score": 1232,
  "upvoteRatio": 0.98,
  "numComments": 22,
  "createdAt": "2026-08-27T21:19:39.000Z",
  "isNsfw": false,
  "flair": null,
  "media": {
    "kind": "link",
    "url": "https://example.com/an-article",
    "thumbnail": null,
    "isVideo": false,
    "galleryUrls": null
  },
  "url": "https://example.com/an-article",
  "permalink": "https://www.reddit.com/r/programming/comments/1w06vn1/an_example_post_title/",
  "foundVia": "new",
  "subredditInfo": null,
  "authorProfile": null,
  "comments": null
}
```

A profile row, which arrives when you name an account and is never charged:

```json
{
  "rowType": "user",
  "id": "t2_1w72",
  "title": "u/example_user",
  "author": "example_user",
  "permalink": "https://www.reddit.com/user/example_user/",
  "createdAt": "2005-06-06T04:00:00.000Z",
  "numComments": 325,
  "foundVia": "user:example_user:profile",
  "authorProfile": {
    "name": "example_user",
    "createdAt": "2005-06-06T04:00:00.000Z",
    "linkKarma": 184454,
    "commentKarma": 756480,
    "totalKarma": 940934,
    "isMod": true,
    "isVerified": true,
    "isSuspended": false
  },
  "comments": [
    {
      "id": "t1_p1wosm9",
      "body": "An example comment.",
      "score": 9,
      "createdAt": "2026-08-04T09:11:50.000Z",
      "postId": "t3_1vgbkge",
      "postTitle": "An example post title",
      "subreddit": "programming"
    }
  ]
}
```

#### The run summary

Alongside the dataset, every run writes a `RUN_SUMMARY` record holding what it did: posts delivered, duplicates removed before charging, comments included, how long it took, and a table of every listing it walked with the unique posts each one contributed. It is the honest account of where your rows came from, and it is worth reading once to see how much of a large run comes from listings a single pass would never have touched.

### 🧱 What the limits actually are

**Run timeout.** A run stops starting new work shortly before its own time limit and finishes cleanly with whatever it has, naming what it did not reach. The Actor's default is 3600 seconds, which is enough for the largest input it accepts. If you set a shorter limit in your own run settings, integration or API call, expect fewer results and a note in the run's status message saying so. You pay per delivered result rather than per minute, so a generous timeout costs you nothing.

**A single listing stops at about a thousand posts.** That is Reddit's, it applies to everybody, and it is the reason this Actor exists. Beyond that point a listing answers successfully with nothing in it.

**Keywords reach the deep history, up to a point.** Listings describe recent activity and a search does not, so keywords go back much further. Reddit does cap an in subreddit search at roughly 250 results per query, so a keyword is worth a few hundred more posts rather than an unlimited archive. Each keyword is read four different ways to get the most out of that cap: measured on one subreddit, two keywords added 934 posts rather than the 500 a single reading would have.

**A quiet subreddit has a floor**, and so does a busy one without keywords. If a subreddit has published four hundred posts in its life, no tool can return a thousand. When a run comes back short of `maxPosts` it says so in its status message, tells you it walked every listing to the end, and suggests adding keywords. You are never charged for the difference.

**Comments are not capped by the thousand.** That ceiling applies to a listing, not to the inside of a post, so a thread is walked properly, including the replies Reddit collapses behind a more comments link.

**Private, quarantined and banned subreddits return nothing**, because they publish nothing. The run says so in its status message rather than failing.

**A suspended or deleted account returns nothing either**, for the same reason, and is named in the status message so you know which one it was. The rest of the run carries on.

**An account's comment history is capped at 500 comments**, taken newest first. That is the whole history of most accounts and a deep sample of the rest. Their posts are not capped by it: those are ordinary rows and `maxPosts` is what limits them.

### ⏱️ How long does a run take?

Measured on real runs, so you know what normal looks like and can tell it apart from a run that has stalled.

| Posts asked for | Typical run time |
|---|---|
| 50 | 3 to 5 seconds |
| 500 | 10 to 15 seconds |
| 3,000 | about 70 seconds |
| 20,000 | about 10 minutes |

Roughly thirty milliseconds a post once the container has started, and the first few seconds of any run are the container starting rather than the work.

Two things make a run take longer than the table: asking for comments, which fetches a thread per post, and asking for far more posts than a subreddit actually holds, because the run keeps trying other routes before it concludes the source is exhausted. Neither is a fault and both end the same way, with a status message saying exactly what was collected.

A run is never silently stuck. Progress is logged as it goes, and if it runs out of time it stops early, keeps everything collected so far and says how much was left.

### 💰 How much does it cost?

You pay **$0.001 per unique post delivered** and **$0.0002 per comment delivered**, plus Apify's own $0.00005 start charge on each run. A thousand posts is $1.00 and a thousand comments is $0.20 on the free plan. Every paid Apify plan pays less through the Bronze, Silver, Gold, Platinum and Diamond discount tiers, down to $0.40 per thousand posts and $0.08 per thousand comments on Diamond.

Four things about that price are worth being explicit about:

- **A duplicate costs nothing.** A post reached through four different listings is one row and one charge.
- **Comments are charged separately, at a fifth of the price of a post.** A run with `includeComments` off pays nothing for them at all, and a deep thread costs a fraction of the post that carries it.
- **Subreddit details and the author profile on a post cost nothing.** They are context on a row you are already paying for. A profile you asked for by naming an account in `redditUsers` is its own row and is charged as a post, because fetching it is a whole request where posts arrive a hundred at a time.
- **A run that delivers nothing pays the start charge and no more**, which is $0.00005. A suspended account, a deleted account and a private subreddit are delivered as a sentence rather than a bill.

`maxPosts` is a hard cap, so the most any run can cost is `maxPosts` times the rate, plus the $0.00005 start charge and whatever comments you asked for. There is no other fee.

### 🔌 Integrations

Send results straight to Google Sheets, Slack, Airtable, Zapier, Make, Amazon S3, a database or your own webhook using [Apify integrations](https://docs.apify.com/platform/integrations). Schedule a run daily and diff it against yesterday, or trigger one whenever something happens in another tool.

The Actor is also callable by AI agents through the [Apify MCP server](https://docs.apify.com/platform/integrations/mcp) as `spookyweb/ultimate-reddit-scraper`. It declares a full input schema, a dataset schema and an output schema, so an agent can discover it, call it and chain its rows without anybody writing an adapter. Running under limited permissions, it is available for agentic payments.

### 🔗 Using Reddit Scraper with the Apify API

Run it and get the rows back in one call:

```bash
curl -X POST "https://api.apify.com/v2/acts/spookyweb~ultimate-reddit-scraper/run-sync-get-dataset-items?token=YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "subreddits": ["datascience"],
    "maxPosts": 500
  }'
```

Start a long run and collect it later:

```bash
curl -X POST "https://api.apify.com/v2/acts/spookyweb~ultimate-reddit-scraper/runs?token=YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"subreddits": ["datascience"], "keywords": ["pandas", "forecasting"], "maxPosts": 10000}'
```

Then read the dataset, and the run summary, from the run's own links. The [Apify API reference](https://docs.apify.com/api/v2) covers the rest, and the Console gives you ready made snippets in Python, JavaScript and cURL.

### ❓ FAQ

#### Can it really get more than 1,000 posts from one subreddit?

Yes. That is the point of it. On r/programming on 29 August 2026, asking for 3,000: the `new` listing ran out at 995 and the run returned 2,718 unique posts. Two keywords took the same request to the full 3,000. How far any run reaches depends on how much that subreddit has published and on your keywords, and `foundVia` on every row says which listing it came from.

#### Do I need a Reddit account or an API key?

No. There is nothing to register and nothing to manage. You put in a subreddit name and you get rows.

#### Will I be charged twice for the same post?

No. Deduplication happens on Reddit's own post id, before charging and before delivery, across every listing and every subreddit in the run. The run summary reports how many duplicates it removed.

#### How far back does it go?

Past the recent window a listing is stuck in, and how far depends on the subreddit and on your keywords. On r/programming, 3,000 asked for with no keywords returned 2,718 posts reaching back to June 2006, where the `new` listing alone stopped at 995. Two keywords filled the full 3,000. Each keyword is drawn from the whole history rather than the recent part of it.

#### Can I get the comments too?

Yes. Turn on `includeComments` and each post's thread comes back nested in its row, including replies collapsed behind a more comments link. Comments cost nothing extra. Use `maxCommentsPerPost` to keep rows a workable size.

#### Can I scrape a specific Reddit user?

Yes. Put the username into `redditUsers` and you get their post history as rows, their profile, and up to 500 of their comments with the title of the post each one was written on. There is no practical limit on how many accounts you name in one run. If an account is suspended or deleted the run says so by name and charges nothing for it.

#### Can I search all of Reddit instead of naming subreddits?

Yes. Put what you are looking for into `keywords`, turn on `searchAllReddit`, and leave `subreddits` empty. Every subreddit is searched, which is how you find mentions in places you would never have thought to watch. A measured run on one product name returned 300 posts from 151 different subreddits.

#### Does a user's comment history cost extra?

Comments are charged at their own rate, which is a fifth of the price of a post. The profile row that carries an account's details is charged as a post, because fetching it is a whole request where a post arrives a hundred at a time. Turn `includeComments` off and you pay nothing for comments at all.

#### How do I monitor mentions of my brand on a schedule?

Set up a run with your brand in `keywords` and `searchAllReddit` on, then schedule it in the Console. Leave `sortNewestFirst` on and the newest mentions are at the top of each dataset. Point a webhook or a Slack integration at the run and you get told rather than having to look.

#### Can I scrape more than one subreddit at once?

Up to 50 per run. `maxPosts` is the total across all of them.

#### Why do some posts have an empty body?

A link post carries no text of its own, so `body` is null and `url` points at the linked page. A self post has the text in `body`.

#### What happens if a subreddit name is wrong?

The run finishes successfully, saves nothing, charges nothing, and the status message says which name it could not read. A typo never costs you money.

#### Can I run it on a schedule?

Yes. Schedule it in the Console, set `sortNewestFirst`, and take the top of each run's dataset as what is new.

### ⚖️ Is it legal to scrape Reddit?

Collecting publicly visible information is legal in the UK, the EU and the US, and courts on both sides of the Atlantic have said so about public web data more than once. Nothing here reaches anything a logged out visitor cannot see: no private subreddits, no direct messages, no quarantined content, no deleted posts.

Collecting one named account's public history is the same question with the stakes raised, because the result is a profile of a person rather than a sample of a community. Everything in it is published by that account and visible to anyone who visits the profile page, and none of it is private, but a compiled history is still personal data and building one is a purpose you need to be able to justify.

What that does not settle is what you then do with it. Usernames and post text are personal data under UK and EU GDPR the moment you can tie them to a person, so if you are storing or publishing this data you need your own lawful basis for it, and Reddit's own [terms of service](https://www.redditinc.com/policies/user-agreement) and [content policy](https://www.redditinc.com/policies/content-policy) govern what you may republish. Commercial redistribution of Reddit content is a question for your own legal advice, not for a README.

If you are unsure whether your use is compliant, take advice before you run a large job rather than after.

### 👍 Your feedback

Found a problem, or need a field that is not here? Open an issue on the Actor's **Issues** tab and it gets looked at. Bug reports come with a run id and get fixed.

We also build custom Actors. If you need something close to this but not quite this, say so in an issue and we will tell you honestly whether it is a small change or a different product.

### 🔎 You might also like

- [Telegram Channel Scraper](https://apify.com/spookyweb/telegram-channel-scraper) for posts, views, reactions and media out of any public Telegram channel.
- [Wayback Machine Scraper](https://apify.com/spookyweb/wayback-machine-scraper) for what a page said on a date in the past.
- [Website Change Monitor](https://apify.com/spookyweb/website-change-monitor) for watching a page and getting the old value and the new one in the same row.
- [Company Jobs Scraper](https://apify.com/spookyweb/company-jobs) for a company's own careers page as rows.

# Actor input Schema

## `urls` (type: `array`):

Paste Reddit links, one per line, and each is resolved to what it names: a subreddit link is walked as that subreddit, a post permalink returns that one thread, a /user/ link returns that account, and a search link keeps the keyword it was built from. Mix them freely with the name fields below. A link that cannot be read stops the run before anything is charged rather than being silently skipped.

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

The subreddits to collect from, one per line, written as a plain name such as programming. An r/ prefix or a full reddit.com link is accepted and cleaned up for you. Up to 50 subreddits per run. maxPosts is the total across all of them, spread across the list rather than spent on the first one.

## `redditUsers` (type: `array`):

Reddit usernames without the u/ prefix, one per line. Each account returns its post history, its comment history when comments are on, and one profile row carrying its karma, age and details. Posts and the profile row are charged at the same rate; comments are charged at a fifth of it.

## `keywords` (type: `array`):

Optional search terms, one per line, searched inside the subreddits you named. Keywords reach posts that are older than any listing will show, so add two or three when you want depth rather than only the recent history. Leave empty to walk the listings alone.

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

Only return posts made on or after this date, so a scheduled run collects what is new instead of paying for the same history every morning. A calendar date such as 2026-08-01 is read as UTC midnight, and a full ISO timestamp works too. Posts outside the window are dropped before comments are fetched and before anything is charged. Leave empty for no lower bound.

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

Only return posts made before this date, which is how you take one month at a time out of a deep history. Same date formats as Posted after. Leave empty for no upper bound.

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

Which of Reddit's listings to read. Leave on Every listing and the run walks new, top, controversial and rising together, which is how it reaches past the thousand posts a single listing stops at. Name one to narrow the run to it.

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

Narrows the sorts that take one, which is Top and Controversial, to a single window. Leave empty and every window is read from all time down to the past hour, which reaches further. This is Reddit's own window on a listing and is not a filter on the post date: use Posted after and Posted before for that.

## `minScore` (type: `integer`):

Drop posts scoring under this. A filtered post is not delivered and not charged. Leave empty to keep everything, including posts on zero or below.

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

Drop posts Reddit marks over 18, which is what a brand safe dataset needs. Off by default, so nothing is hidden unless you ask.

## `skipStickied` (type: `boolean`):

Drop the posts a moderator pinned to the top of the subreddit. They are usually rules and announcements rather than the conversation you came for.

## `searchAllReddit` (type: `boolean`):

Search the keywords across every subreddit there is instead of only inside the ones you named, which is how you find every mention of a brand, a product or a person. Needs at least one keyword. The subreddits you name are still walked afterwards.

## `maxPosts` (type: `integer`):

How many unique posts to return in total, from 1 to 100000. Duplicates are removed before this is counted and before anything is charged, so this is the number of usable rows you get and the number you pay for. The run stops as soon as it is reached.

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

Bring each post's comment thread back nested inside its row, including the replies Reddit collapses behind a more comments link. Comments are free: you pay per post whether the thread comes with it or not. It does make a run slower, so leave it off for a plain post export.

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

How many comments to take from each post, in Reddit's top order. Reddit serves them about a hundred at a time, so a deep thread costs several requests: a run pointed at a handful of posts will go after the whole cap, and a run walking a whole subreddit stops at roughly five hundred a post to keep the run inside its time. When a thread still had more to give, the run says so in its status message rather than leaving you to count.

## `commentOutput` (type: `string`):

Nested keeps the thread inside its post, which is the shape every existing dataset has. Flat writes one row per comment with parentId and postId, which is what a spreadsheet, a warehouse or an enrichment tool takes without a parser. Both writes the post with its thread and the comment rows beside it. A comment is charged once whichever you pick.

## `includeSubredditInfo` (type: `boolean`):

Attach the subreddit's own details to every row it produced: subscriber count, description, the date it was created, whether it is marked over 18, and its posted rules. It is context rather than a delivered row, so it costs nothing and appears in the run summary too.

## `sortNewestFirst` (type: `boolean`):

Order the rows by post date, newest first, before they are written. Runs above ten thousand posts are written in sorted blocks as they are collected, which is what keeps the run inside a small container. Turn this off to keep Reddit's own ordering instead.

## Actor input object example

```json
{
  "urls": [
    "https://www.reddit.com/r/webscraping",
    "https://www.reddit.com/r/datasets/comments/1abc/title/"
  ],
  "subreddits": [
    "programming"
  ],
  "redditUsers": [],
  "keywords": [],
  "postedAfter": "2026-08-01",
  "postedBefore": "2026-09-01",
  "sort": "all",
  "timeWindow": "",
  "minScore": 100,
  "excludeNsfw": false,
  "skipStickied": false,
  "searchAllReddit": false,
  "maxPosts": 500,
  "includeComments": false,
  "maxCommentsPerPost": 50,
  "commentOutput": "nested",
  "includeSubredditInfo": false,
  "sortNewestFirst": true
}
```

# Actor output Schema

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

One row per unique post: title, body, author, subreddit, score, upvote ratio, comment count, flair, media, permalink, which listing first produced it, and the comment thread when comments were asked for.

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

What the run did: posts delivered, duplicates removed before charging, comments included, requests made, and a table of every listing walked with the unique posts it contributed.

# 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 = {
    "urls": [],
    "subreddits": [
        "programming"
    ],
    "redditUsers": [],
    "keywords": [],
    "maxPosts": 500
};

// Run the Actor and wait for it to finish
const run = await client.actor("spookyweb/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 = {
    "urls": [],
    "subreddits": ["programming"],
    "redditUsers": [],
    "keywords": [],
    "maxPosts": 500,
}

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

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,spookyweb/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/1tt10HefCQFOqUTkg/builds/a4CC2MDJ8yxfO46q3/openapi.json
