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

Scrape Reddit posts from any subreddit or search query: titles, scores, comment counts, authors, flair, timestamps, outbound links and NSFW/spoiler/OC flags. No login, no browser.

- **URL**: https://apify.com/muhammad4hmed/reddit-scraper.md
- **Developed by:** [Muhammad Ahmed](https://apify.com/muhammad4hmed) (community)
- **Categories:** Social media, News
- **Stats:** 1 total users, 0 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.20 / 1,000 post scrapeds

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?

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

Scrape Reddit posts from any subreddit or search query — titles, scores, comment counts, authors,
flair, timestamps, outbound links and the NSFW / spoiler / original-content flags — as clean JSON,
CSV or Excel.

No Reddit account, no API key, no browser. The Actor reads Reddit's own server-rendered HTML over
plain HTTP, so a 1000-post crawl finishes in under a minute.

### What it does

- **Subreddit feeds** — hot, new, top, rising or controversial, with a time window for top and
  controversial. Multireddits (`technology+science+programming`) work too.
- **Reddit search** — site-wide or scoped to specific subreddits, with Reddit's own search
  operators (`flair:Discussion`, `nsfw:yes`, `author:spez`, `site:nytimes.com`).
- **Deep pagination** — up to 1000 posts per subreddit and ~250 per search, 100 posts per request,
  de-duplicated.
- **NSFW access** — opt in and the Actor sends the adult-content signal Reddit requires.
- **Optional detail pass** — fetch each post's own page to fill in the upvote ratio and the full
  body text of self posts.

### Example output

```json
{
  "postId": "t3_1vq1845",
  "url": "https://www.reddit.com/r/technology/comments/1vq1845/bernie_sanders_calls_out_sergey_brin_for_spending/",
  "title": "Bernie Sanders calls out Sergey Brin for spending over $100 million to defeat California's billionaire tax",
  "author": "marketrent",
  "authorId": "t2_fj9vsvfd",
  "subreddit": "technology",
  "score": 17528,
  "upvoteRatio": null,
  "commentCount": 1364,
  "createdAt": "2026-08-16T16:17:34.000Z",
  "domain": "yahoo.com",
  "linkUrl": "https://www.yahoo.com/news/politics/articles/bernie-sanders-calls-sergey-brin-120113695.html",
  "isSelfPost": false,
  "selfText": null,
  "thumbnail": "https://external-preview.redd.it/nsJu9QGFhJ_zinuKmfm2kyh9BU0Gy1D1JtDBihia2jE.jpeg?width=140&height=93&auto=webp&s=04feec5562b5afdbd5ae6fd1b193197f50f7bbd4",
  "flair": "Society",
  "isNsfw": false,
  "isSpoiler": false,
  "isOriginalContent": false,
  "isPromoted": false,
  "isStickied": false,
  "isGallery": false,
  "numCrossposts": 3,
  "rank": 1,
  "source": "r/technology",
  "sourceUrl": "https://old.reddit.com/r/technology/?limit=100",
  "scrapedAt": "2026-08-17T04:14:06.345Z"
}
```

### Output fields

Fill rates below are measured on a real platform run — 500 posts from r/technology, r/AskReddit,
r/pics, r/worldnews and r/movies. Where a field is not 100%, the reason is stated; nothing here is
guesswork.

| Field | Type | Fill | Notes |
|---|---|---|---|
| `postId` | string | 100% | Reddit fullname, e.g. `t3_1vq1845` |
| `url` | string | 100% | Permalink on `www.reddit.com` |
| `title` | string | 100% | Post title, HTML entities decoded |
| `author` | string | 100% | `[deleted]` when the account is gone |
| `authorId` | string | 100% | `t2_…` fullname; `null` for deleted accounts |
| `subreddit` | string | 100% | Canonical casing as Reddit spells it, no `r/` prefix |
| `score` | integer | 100% | Exact net upvotes. Not the abbreviated "17.1k" shown on the page |
| `upvoteRatio` | number | 0% / 100% | `null` unless **Fetch upvote ratio and self-post text** is on — see Limitations |
| `commentCount` | integer | 100% | Exact comment count |
| `createdAt` | string | 100% | ISO 8601 UTC |
| `domain` | string | 100% | `yahoo.com`, or `self.<subreddit>` for text posts. `null` on crossposts, where Reddit leaves it blank |
| `linkUrl` | string | 100% of link posts | The outbound URL. `null` for self posts and crossposts, which have no external link |
| `isSelfPost` | boolean | 100% | Text post rather than a link |
| `selfText` | string | 0% / self posts | `null` unless **Fetch upvote ratio and self-post text** is on — see Limitations |
| `thumbnail` | boolean | 91% of link posts | Absolute HTTPS URL. `null` when Reddit has no preview (self posts, plain link icons, NSFW placeholders) |
| `flair` | string | 50% | Post flair. Genuinely absent in subreddits that do not use flair — 100/100 in r/technology, 0/100 in r/AskReddit |
| `isNsfw` | boolean | 100% | Reddit's over-18 tag |
| `isSpoiler` | boolean | 100% | Reddit's spoiler tag |
| `isOriginalContent` | boolean | 100% | Reddit's OC tag |
| `isPromoted` | boolean | 100% | Sponsored post. Always `false` in practice — see Limitations |
| `isStickied` | boolean | 100% | Pinned to the top of the subreddit (usually a moderator announcement) |
| `isGallery` | boolean | 100% | Multi-image gallery post |
| `numCrossposts` | integer | 100% | How many times the post was crossposted; `0` is a real value |
| `rank` | integer | 100% | Position in the feed, continuous across pages (1…1000) |
| `source` | string | 100% | Which input produced the row, e.g. `r/technology` or `r/movies search:dune` |
| `sourceUrl` | string | 100% | The Reddit URL that source resolved to |
| `scrapedAt` | string | 100% | ISO 8601 UTC |

A source that returns nothing produces one row with `source`, `sourceUrl`, `error` and `scrapedAt`
instead of being silently dropped. Those rows are never charged for.

### Input

| Field | Type | Default | Description |
|---|---|---|---|
| `subreddits` | string\[] | — | `technology`, `r/technology`, a subreddit URL, or a multireddit like `technology+science` |
| `searchQueries` | string\[] | — | Reddit search queries. Optional |
| `sort` | enum | `hot` | `hot`, `new`, `top`, `rising`, `controversial` |
| `timeRange` | enum | `day` | `hour`, `day`, `week`, `month`, `year`, `all` |
| `maxPostsPerSource` | integer | `100` | 1–1000 posts per subreddit or query |
| `includeNsfw` | boolean | `false` | Ask Reddit for over-18 posts |
| `skipPromoted` | boolean | `true` | Drop sponsored posts and pinned announcements |
| `fetchPostDetails` | boolean | `false` | Also open each post's page for `upvoteRatio` and `selfText` |
| `maxItems` | integer | `0` | Hard cap on the whole run; 0 means no cap |
| `proxyConfiguration` | object | Apify residential | Residential proxy is required |

**Subreddits and queries combine.** With `subreddits: ["technology"]` and
`searchQueries: ["apple"]` the Actor searches *apple* **inside** r/technology. Queries on their own
search all of Reddit; subreddits on their own list the feed. Every combination is logged at the
start of the run with the exact URL it resolved to.

### Use cases

- **Brand and product monitoring** — search your product name across Reddit and watch score,
  comment count and subreddit to see where a thread is taking off.
- **Trend and topic research** — pull `top` with `timeRange: all` from a niche subreddit to get the
  canonical posts on a subject, or `new` to watch a topic emerge.
- **Content and SEO pipelines** — `domain` and `linkUrl` show which publishers a community actually
  links to.
- **Community analytics** — 1000 posts of `new` from a subreddit gives a clean posting-rate,
  flair-mix and engagement time series.
- **LLM / dataset building** — turn on the detail pass and every self post arrives with its full
  body text.

### Limitations — read before you buy

These are real constraints of Reddit's public HTML, not bugs. Everything below was measured, not
assumed.

- **`upvoteRatio` and `selfText` are `null` by default.** Reddit publishes neither in its listing or
  search markup — the ratio only exists on a post's own page and self-post bodies are loaded by
  JavaScript. Turn on **Fetch upvote ratio and self-post text** and both fill to 100% (verified:
  0/10 → 10/10 on a platform run), at the cost of one extra request per post, so a 100-post run goes
  from ~10 seconds to ~7 minutes.
- **`isPromoted` is always `false`.** Logged-out old Reddit no longer serves sponsored posts; across
  \~2500 rows from multiple countries and IP types not one promoted row appeared. The column is wired
  to Reddit's own attribute and stays in the output for completeness, but do not expect it to fire.
  Because of this, **Skip promoted and pinned posts** does its real work on the pinned half — on a
  platform control run it removed exactly the 7 stickied moderator announcements across four
  subreddits and nothing else.
- **Fully 18+ subreddits cannot be listed as a feed.** Reddit walls `r/<adult-sub>/` for logged-out
  clients no matter what. The Actor detects this and tells you so per subreddit instead of returning
  an empty result. The workaround that does work is search: put the subreddit in `subreddits`, add a
  `searchQueries` entry and turn on `includeNsfw`.
- **`rising` returns only ~25 posts.** Reddit publishes a single short rising page per subreddit
  with no "next" link, so a `rising` run stops at roughly 25 rows however high `maxPostsPerSource`
  is (r/all is the exception, at 100). Use `hot` or `new` for depth.
- **Depth is capped by Reddit.** A subreddit feed hard-stops at 1000 posts and a search at roughly
  250, whatever `maxPostsPerSource` says. For more history, split the crawl by `sort` and
  `timeRange`, or by narrower search queries.
- **`timeRange` only applies to `top`, `controversial` and searches.** Reddit accepts but silently
  ignores it on `hot`, `new` and `rising`, so the Actor does not send it there — a `hot` run is the
  same feed whatever time range you pick.
- **Reddit search has no `rising` or `controversial` order.** Choose either with a search query and
  the search falls back to relevance; the Actor logs a warning saying so. Subreddit feeds are
  unaffected.
- **`hot` and `rising` can return the same posts in a quiet subreddit.** On r/technology they
  overlapped 25/25 (in a different order); on r/all they shared 0/25. That is Reddit's ranking, not
  a filtering failure.
- **Residential proxy is required.** Reddit blocks datacenter addresses outright and answers roughly
  half of residential addresses with a login page instead of the post list. Reddit also returns a
  403 "whoa there, pardner!" page to individual exit IPs at random — even for huge public
  subreddits. The Actor treats both as address problems, rotates to a fresh proxy session and
  retries up to six times per page, and never charges for a blocked request. A genuinely private,
  restricted or quarantined subreddit also answers 403; that one is told apart by Reddit's own page
  title and reported as such instead of being retried. Leave the proxy setting alone.
- **Comments are not scraped.** This Actor returns posts only.

### Pricing

Pay per event:

| Event | Price |
|---|---|
| Actor start | $0.005 per run |
| Post scraped | $0.0002 per post |
| Post details fetched | $0.005 per post (only when the detail pass is on) |

1000 posts cost about $0.21. Failed, blocked and empty sources are never charged.

# Actor input Schema

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

Subreddits to scrape, as "technology", "r/technology" or a subreddit URL. Multireddits such as "technology+science" work too. If you also fill in search queries, each query is searched inside these subreddits instead of the feed being listed.

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

Optional Reddit search queries. With subreddits filled in, every query is searched inside every subreddit; on their own, queries search all of Reddit. Reddit search operators work, e.g. "nsfw:yes" or "flair:Discussion".

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

Order posts the way Reddit does. Searches have no rising or controversial order — if you pick one of those, searches fall back to relevance while subreddit listings still use it. Rising is also a short feed: Reddit publishes only about 25 rising posts per subreddit and offers no further pages, so Max posts per source cannot be reached with it.

## `timeRange` (type: `string`):

Time window for the ranking. Reddit applies this to Top and Controversial listings and to every search; it silently ignores it on Hot, New and Rising feeds.

## `maxPostsPerSource` (type: `integer`):

How many posts to collect from each subreddit or query. Reddit hard-stops a subreddit feed at 1000 posts and a search at roughly 250, so higher values simply finish early.

## `includeNsfw` (type: `boolean`):

Reddit hides over-18 posts from logged-out visitors. Turn this on to send the adult-content signal (an over18 cookie on feeds, include\_over\_18 on searches) so NSFW posts are returned. Fully 18+ subreddits still cannot be listed as a feed — search them instead.

## `skipPromoted` (type: `boolean`):

Drop sponsored posts and the moderator announcements pinned to the top of a subreddit. In practice only the pinned half fires: logged-out old.reddit no longer serves promoted links, so the isPromoted column is false on every row we have ever seen.

## `fetchPostDetails` (type: `boolean`):

Reddit does not publish the upvote ratio or the body of a text post in its listings. Turn this on to open each post's own page and fill in upvoteRatio and selfText. It costs one extra request per post, so runs take much longer and are billed per hydrated post.

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

Hard cap on total posts across all subreddits and queries. 0 means no cap.

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

Reddit blocks Apify's datacenter IPs and answers roughly half of all residential addresses with a login page, so residential proxy plus session rotation is required. Leave this as it is unless you know what you are changing.

## Actor input object example

```json
{
  "subreddits": [
    "technology"
  ],
  "sort": "hot",
  "timeRange": "day",
  "maxPostsPerSource": 100,
  "includeNsfw": false,
  "skipPromoted": true,
  "fetchPostDetails": false,
  "maxItems": 0,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# Actor output Schema

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

Every Reddit post scraped from the requested subreddits and searches, one row each.

## `csv` (type: `string`):

The same posts as a spreadsheet-ready CSV file.

# 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": [
        "technology"
    ]
};

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

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

```

## MCP server setup

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