# Reddit Scraper - Incremental Runs, Posts and Comment Trees (`hridayrungta/reddit-scraper`) Actor

INCREMENTAL runs: only posts since your last run, per subreddit or query - no duplicates, one page instead of twenty. First run seeded from 30 days, never a silent backfill. Comments flatten with depth and parent id for tree rebuild. Author usernames redacted unless opted in.

- **URL**: https://apify.com/hridayrungta/reddit-scraper.md
- **Developed by:** [Hriday Rungta](https://apify.com/hridayrungta) (community)
- **Categories:** Social media, News, Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 1 bookmarks
- **User rating**: 5.00 out of 5 stars

## Pricing

Pay per event

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 — incremental runs, posts and full comment trees

**Point it at your subreddits and search queries once, put it on a schedule, and every run hands
you only the posts published since the previous run.** No duplicates to filter out, no history to
re-download, and nothing billed twice.

Turn on **Only new posts since my last run** and the Actor keeps a high-water mark **per subreddit
and per search query** in a key-value store on your own Apify account. Track 20 sources and you get
20 independent marks. Because Reddit's `new` listing is newest-first, the Actor also **stops
reading a source the moment it reaches a post you already have** — a daily run on a busy community
reads one page instead of twenty.

**$2.50 per 1,000 rows, on every plan.** No start fee. Posts and comments are the same price.

***

### What you get

One row per post:

| | |
|---|---|
| `title`, `selftext` | the post and its body, as Markdown |
| `score`, `ups`, `upvoteRatio`, `numComments` | how it did |
| `createdUtc`, `createdAtIso` | exact publication time, as an epoch and as an ISO-8601 UTC timestamp |
| `permalink`, `url`, `domain`, `isSelf` | where it lives and what it points at |
| `subreddit`, `linkFlairText`, `over18`, `spoiler`, `locked`, `stickied` | how it sits in its community |
| `sourceType`, `sourceValue`, `sort` | which subreddit or query found it, in which order |

Switch on **Also scrape comments** and every reply becomes its own row, in the same columns, plus:

| | |
|---|---|
| `body` | the comment text. `[removed]` and `[deleted]` are kept verbatim — a removed comment is information |
| `depth` | 0 for a top-level comment, 1 for a reply to it, and so on |
| `parentId`, `parentType` | the parent's id with no prefix, and whether the parent is the post or another comment |
| `postId`, `postTitle` | the post the comment belongs to |
| `isSubmitter` | true when the post's own author wrote it |
| `retainedForTree` | true on the few rows kept only so the thread still joins up — see below |

`parentId` joins straight to `commentId`, so **the whole thread rebuilds from the flat table with
one self-join** — no JSON parsing, no nested objects, no ragged CSV columns. Posts and comments
share one column set. The contract is exact: **every `parentType: "comment"` row's `parentId`
matches a `commentId` that appears earlier in the dataset**, one depth level up.

#### `minScore` cannot break the thread, and the rows that keep it whole are free

Reddit returns downvoted comments routinely, so a score floor — including the default `minScore:
0` — will sometimes reject a comment that has a well-scored reply underneath it. Dropping it would
leave that reply pointing at a `parentId` in no row of your dataset, so **a comment that fails
`minScore` but is an ancestor of one that passes is kept, and flagged `retainedForTree: true`** —
it is in your dataset despite failing your filter, purely so the tree closes. **Those rows are
never charged**: you excluded them, so you do not pay for them, and `WHERE retainedForTree IS NOT
TRUE` gives you exactly the set your filter asked for (they are still rows, so they count towards
`maxItems`). A below-threshold comment with nothing
surviving under it is dropped as you would expect, subtree and all.

#### Comments are fetched in parallel, and the order never changes

A comment tree is one unblocking-proxy request and the unblocker answers in 40–90 seconds, so
`commentConcurrency` (default **5**) fetches several at once: **150 posts with comments takes
roughly 30–35 minutes at the default**, against over two hours one at a time — and about 15–18
minutes at `commentConcurrency: 10`. Each post's tree is buffered and written in listing order, so
the dataset is identical at any setting: parents still precede their replies, and nothing is
billed or counted twice. A tree that fails to load is reported in the log; its post row is still
saved and the run carries on.

### Author names are redacted unless you ask for them

**`Include author usernames` is OFF by default, and every row's `author`, `authorFullname` and
`authorFlairText` come back as `null` while it is off.** Everything that is not a person — the
title, the body, the score, the timestamp, the permalink, the subreddit — is kept, so the dataset
is fully useful for market research, monitoring and trend analysis without carrying anybody's
identity.

Turn it on only if you have a lawful basis for processing the identity of the people who wrote the
posts. It is a single switch, and it is your decision, recorded in your run's input.

### Incremental runs

| Input | What it does |
|---|---|
| `onlyNewSinceLastRun` | **off by default.** On: return only posts published since this source's last run, then move the mark forward. |
| `firstRunSince` | how far back a source the Actor has **never seen** reaches. Default `30 days`. Set it to `all` for a full backfill. |
| `stateStoreName` | the key-value store on **your** account that holds the marks. Default `reddit-scraper-state`, created for you. |
| `stateNamespace` | optional extra prefix, for keeping several independent tracking sets inside one store. |

#### The first run, spelled out

A source the Actor has never seen has no mark, so it has to start somewhere. **It starts from a
date, not from the beginning of the listing.** By default that date is 30 days ago, so the first
run on a new subreddit costs you roughly a month of posts — not everything the listing will give.
You decide how much history you pay for:

| `firstRunSince` | First run on a new source returns |
|---|---|
| `30 days` *(default)* | posts from the last 30 days, then sets the mark |
| `6 months`, `1 year`, `2026-01-01` | posts back to that point, then sets the mark |
| `all` | everything the listing will give, then sets the mark — an explicit, deliberate backfill |

Every later run ignores `firstRunSince` entirely and uses the mark. The run log names which of the
two happened for each source, so there is never any doubt about what you were billed for.

#### What the marks are, and where they live

One record per source, holding the timestamp and id of the newest post you have received, the
source's own identity, and a run counter. They are kept in a **named** key-value store, which
Apify retains indefinitely on every plan, so the marks survive across runs, schedules, plan
changes and Actor updates.

- **They are yours.** The store is created on the account that runs the Actor, alongside your
  datasets. You can open it, read it, edit a mark to re-pull a period, or delete it to start over.
- **Keyed per source.** A subreddit and a query are separate sources, and a query is keyed by its
  own sort and time range too — so "top of the week for X" and "new posts about X" never share a
  mark.
- **Namespaced.** Keys carry a namespace taken from your own Apify user id, and a record whose
  namespace does not match is ignored on read, so marks can never be crossed between accounts.
- **Give different tracking sets different store names** — `reddit-state-competitors` and
  `reddit-state-leads` — and the same subreddit is tracked independently in each.
- **Cheap.** One read and one write per source per run.

#### Where it saves you money

Reddit's `new` listing is newest-first, so once the Actor reaches a post older than your mark it
knows everything beyond it is already in your dataset and **stops reading that source
immediately**. Both things a run pays for fall: the rows you are billed for, and the proxy
requests behind them.

Modelled on a community gaining 50 posts a day, run daily with `maxPostsPerSource: 500`:

| Run | Billed rows | Listing requests |
|---|---|---|
| Stateless, every run | 500 | 5 |
| **Incremental, steady state** | **50** | **1** |

That is **10x fewer billed rows and 5x fewer proxy requests per run**. Across a 30-day daily
schedule, including the two seeding runs at the start, it is 2,100 rows against 15,000 — **7x
less**, or $5.25 against $37.50. `npm run measure` reproduces those numbers locally from the
Actor's own code, with no network.

The quieter the community and the more often you run, the bigger the gap: the stateless bill is
always your cap, while the incremental bill is only what was actually posted.

#### Safety rails

- **A source with nothing new returns zero rows and is billed nothing.** No empty rows, no minimum
  charge.
- **Two consecutive runs cannot double-count**, not even a post sitting exactly on the boundary
  second: the mark remembers recent post ids as well as the timestamp, and Reddit timestamps are
  whole seconds, so that collision is common rather than theoretical.
- **A run cut short still moves the mark, safely.** If `maxItems`, `maxPostsPerSource` or a charge
  limit stops a run part-way, the mark advances to the newest post that run actually billed you
  for — listings are newest-first, so that post is trustworthy even though older ones on the same
  run were not reached. Either way, **you are never billed twice**: `seenPostIds` remembers what
  was already charged independently of the mark, so a re-read of the same ground never re-bills it.
- **A run cut short DOES leave a permanent gap, and the run log and the Actor's OUTPUT say so.**
  If a source hits its cap before reaching the mark (or, on a first run, before reaching
  `firstRunSince`), the posts older than where it stopped are never returned by a later run — later
  runs only look forward from the mark, they never go back to fill in what an earlier capped run
  skipped. Each source's line in the log names it plainly: `"reached back to <date>, requested back
  to <date>; posts older than that will not be returned by later runs - raise 'maxPostsPerSource'
  ... for a complete first pull."` The same fact is on the mark record itself
  (`windowCovered: false`, `oldestReachedIso`) and in the run's OUTPUT under `coverage`, so you can
  check it without reading logs. If you want a source's full history, raise `maxPostsPerSource` (or
  `maxItems`) so the first run is never capped, or run once with `firstRunSince: "all"`.
- **A pinned post never ends a run early.** Communities pin old announcement threads to the top of
  every listing; the early stop skips them rather than concluding the source is up to date.
- Incremental mode needs newest-first order, and says so in the log if you asked for another.
- **Leave at least a minute between incremental runs on the same source.** The marks live in
  Apify's key-value store, and a run started only seconds after the previous one finished can read
  a not-yet-visible mark and reseed from scratch — the log says `"mark record found but not yet
  set"` when this happens, so it reads as a timing note rather than a mystery.

### A source that returns nothing does not pass quietly

A misspelt subreddit name and a missing proxy both look like "no results", and neither should
finish SUCCEEDED with an empty dataset. If a source returns no posts at all and nothing explains it
— no date window, no score floor, no mark, no cap — the run **fails** and names the source, with
the likely cause. Rows already saved are kept: they are real, and the failure is about the ones
that are missing. Set `failOnEmpty: false` if an empty result is acceptable to you.

### What it needs, and what it never does

**This Actor needs a plan with Unblocker proxy access.** Reddit answers a plain datacenter IP with
HTTP 403 and an HTML block page, so the proxy group is the reason the Actor returns anything at
all. **Unblocker access is included on every plan, the free one included**, and the input's proxy
field is already set to it — there is nothing for you to configure. Each successful request costs
about 10 proxy units, and one request returns up to 100 posts.

Requests carry a **90-second timeout** by default. That is deliberate and measured: the unblocking
proxy does real work per request and a shorter timeout aborts requests that were about to succeed.
Raise it with `requestTimeoutSecs` if you see timeouts; the minimum accepted is 45 seconds.

It reads **only the public JSON views of pages anybody can open in a browser** — a subreddit
listing, a search results page, and a post's comment tree. It never logs in, never sends a cookie
from a signed-in session, and uses no Reddit API key and no OAuth token. It reads no private
community, no direct message and no user inbox.

Reddit's own terms restrict automated collection of its content, and commercial use is gated on an
agreement with Reddit. Satisfying yourself that your use is permitted is your responsibility as
the person running the Actor.

### Input at a glance

| Input | Default | Notes |
|---|---|---|
| `subreddits` | `["smallbusiness"]` | any of `name`, `r/name` or a full community link |
| `searchQueries` | `[]` | Reddit search syntax, including quoted phrases |
| `searchInSubreddits` | `false` | on: every query runs inside every subreddit listed |
| `sort` | `new` | `new`, `hot`, `top`, `relevance` (search only) |
| `timeRange` | `week` | `top` listings and search only |
| `maxPostsPerSource` | `25` | one Reddit request returns up to 100 |
| `minScore` | `0` | dropped posts and comments are never billed; ancestors kept to close a thread are flagged and free |
| `includeComments` | `false` | one extra request per post; posts with no comments are skipped |
| `maxCommentsPerPost` | `50` | applied while the tree is walked, not after |
| `commentDepth` | `10` | 0 is top-level only |
| `commentConcurrency` | `5` | comment trees fetched at once; 150 posts ≈ 30–35 min. Output order is unchanged |
| `onlyNewSinceLastRun` | `false` | the incremental mode described above |
| `firstRunSince` | `30 days` | or `all` for a deliberate backfill |
| `postsNewerThan` / `postsOlderThan` | empty | inclusive start, exclusive end, so adjacent windows never overlap |
| `personalData` | `false` | off: author usernames are redacted |
| `maxItems` | `1000` | hard row cap for the whole run |
| `requestTimeoutSecs` | `90` | minimum 45; the unblocker is slow by design |
| `failOnEmpty` | `true` | an unexplained empty source fails the run |

### Billing

Pay per event, charged as each row is saved, with **no start fee** and no charge for platform
usage:

| Event | Price | When |
|---|---|---|
| `post-scraped` | $0.0025 | one post row |
| `comment-scraped` | $0.0025 | one comment row — **except** a row flagged `retainedForTree`, which is free |

Set `maxItems` as your cost cap, or a maximum total charge on the run. When a charge limit is
reached the run stops cleanly, keeps everything already saved, and holds the incremental marks
where they were so nothing is re-billed on the next run.

# Actor input Schema

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

Communities to read, one per line. Any form works: `smallbusiness`, `r/smallbusiness` or a full `https://www.reddit.com/r/smallbusiness/` link. Public communities only - a private or quarantined community cannot be read without logging in, and this Actor never logs in.

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

Phrases to search Reddit for, one per line - e.g. `crm recommendation`, `"per seat pricing"`. Reddit's own search syntax works, including quoted phrases. By default each query searches all of Reddit; switch on **Search inside the subreddits above** to narrow it.

## `searchInSubreddits` (type: `boolean`):

OFF by default, so a query searches all of Reddit. Turn it ON and every query is run inside every subreddit you listed - 2 queries x 3 subreddits is 6 separate sources, each with its own high-water mark. Needs at least one subreddit and one query to do anything.

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

The order to read a listing in.

- **New** returns the most recent posts first. It is the only order in which the Actor can stop reading early, so it is both the default and by far the cheapest choice - and it is the order incremental mode requires.
- **Hot** is Reddit's own front-page ranking.
- **Top** is the highest-scoring posts inside the **Time range** below.
- **Relevance** applies to search only; a subreddit listing has no relevance order, so it falls back to Hot.

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

Only used by **Top** listings and by search. Ignored with New and Hot, because neither of those orders takes a period.

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

How many posts to take from each subreddit and each query. Reddit serves 100 posts per request, so 25 is one request and 250 is three. This is your main cost control: you are billed once per post saved.

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

Drop posts and comments scoring below this. `0` keeps everything that is not net-negative; a negative value keeps downvoted content too. Set it to 10 or 50 on a busy subreddit to skip the noise - a dropped post is never billed.

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

OFF by default. Turn it ON and each post's comment tree is fetched as well, and every comment becomes its own row with `body`, `score`, `depth`, `parentId` and `parentType` - so the whole thread rebuilds from a flat table with one self-join.

Comments are billed separately as `comment-scraped`, and a comment tree is one extra request per post, so a 25-post run with comments is 26 requests rather than 1. A post Reddit reports as having no comments is skipped without a request.

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

Cap on comment rows per post. Only used when **Also scrape comments** is on. The cap is applied while the tree is walked, in Reddit's own order, so a post with 8,000 comments returns the first N rather than building the lot.

## `commentDepth` (type: `integer`):

How deep into a reply chain to go. 0 is top-level comments only, 10 covers almost every real thread. Replies below the cap are reported in the log as "not expanded" rather than silently dropped.

## `commentConcurrency` (type: `integer`):

How many comment trees to fetch at the same time. Only used when **Also scrape comments** is on.

One comment tree is one unblocking-proxy request and the unblocker takes **40-90 seconds** to answer, so fetching them one at a time is what makes a large comments run slow: 150 posts in sequence is over two hours and can hit your run timeout. At the default of 5 the same 150 posts take **roughly 30-35 minutes**, and at 10 roughly 15-18 minutes.

**Output order does not change.** Each post's tree is buffered and written in listing order, so a parent row always appears before its replies and the dataset rebuilds into the same tree at any setting. Charges do not change either: rows are counted and billed in one sequential pass.

Set it to 1 to fetch strictly one at a time. Raise it above 10 only if you also raise the run's memory, since each tree in flight is held in memory while it is parsed.

## `onlyNewSinceLastRun` (type: `boolean`):

OFF by default. Turn it ON and the Actor remembers, **per subreddit and per query**, the newest post it has already given you, and every later run returns only what was posted since. Point a schedule at it and you get a clean feed of new posts with no duplicates and no re-billing.

The marks are kept in a **key-value store on your own Apify account** (see *State store name* below), so they survive across runs, schedules and Actor updates, and 20 tracked sources get 20 independent marks.

It is also much the cheapest way to run this Actor. Reddit's `new` listing is newest-first, so the moment the Actor reads a post older than your mark it stops reading that source immediately instead of paging the rest. A daily run on a busy subreddit reads **one page instead of twenty**.

**First run:** a source with no mark yet is seeded from *First run reaches back* (30 days by default) - never the whole listing, so a first run cannot surprise you with a bill for a year of posts you did not ask for. Set it to `all` if you do want the backfill.

Incremental mode reads newest-first by necessity, so it overrides **Sort** for the run and says so in the log.

## `firstRunSince` (type: `string`):

Only used by **Only new posts since my last run**, and only for a source the Actor has never seen. Absolute (`2026-01-01`) or relative (`30 days`, `6 months`, `1 year`). Default `30 days`.

Set it to `all` to take everything the listing will give on the first run and set the mark from it - a deliberate backfill. Leaving it at a span is the safe choice: you decide how much history you pay for.

## `stateStoreName` (type: `string`):

Name of the key-value store on **your** Apify account that holds the high-water marks. Defaults to `reddit-scraper-state`, which is created for you on the first incremental run.

Give different tracking sets different names - e.g. `reddit-state-competitors` and `reddit-state-leads` - and each keeps its own marks, so the same subreddit can be tracked independently in two schedules. Letters, digits and dashes; anything else is folded into those.

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

Extra prefix on every state key. Leave empty and the Actor derives one from your Apify user id, which already keeps your marks separate from everybody else's. Set it only when you want several independent tracking sets inside ONE store - for example one namespace per client or per report.

## `postsNewerThan` (type: `string`):

Keep only posts published at or after this moment. Absolute (`2026-04-01`, or a full ISO timestamp) or relative (`7 days`, `6 months`). Leave empty for no lower bound. On the New sort this also stops the Actor reading past it, so a narrow window is cheap as well as small.

## `postsOlderThan` (type: `string`):

Keep only posts published before this moment. Same formats as the window start. Leave empty for no upper bound.

## `personalData` (type: `boolean`):

OFF by default, and please leave it off unless you need it. While it is off, the author's username, their Reddit account id and their flair are removed from every post row and every comment row - the fields come back as `null`. The post title, the body, the score, the timestamp, the permalink and the subreddit are all kept, so the dataset stays fully useful for research, monitoring and market analysis without carrying anybody's identity.

Turn it on only if you have a lawful basis for processing the identity of the people who wrote the posts.

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

Hard stop after this many rows across the whole run, posts and comments together. This is your overall cost cap: you are billed once per row saved.

## `requestTimeoutSecs` (type: `integer`):

How long one request may take. **The default of 90 seconds is deliberate and measured** - the unblocking proxy does real work per request and 30 seconds is not enough for it; a shorter value aborts requests that were about to succeed and looks like a network failure. The minimum accepted is 45. Raise it to 120-180 if you see timeouts on a busy day.

## `maxRetries` (type: `integer`):

Retries with exponential backoff for timeouts, 429s and 5xx responses. A 403 is never retried - it means the request never reached Reddit's content, so backing off cannot help.

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

**Required, and the group matters.** Reddit answers a plain datacenter IP with HTTP 403 and an HTML block page - measured, not assumed - so this Actor defaults to Apify Proxy with the **UNBLOCKER** group, which returns real JSON. Leave it as it is unless you know exactly why you are changing it. Unblocker access is included on every plan including the free one.

## `failOnEmpty` (type: `boolean`):

On by default. If a subreddit or query returns no posts at all and nothing explains it - no date window, no score floor, no incremental mark, no cap - the run FAILS and names the source, instead of finishing SUCCEEDED with an empty dataset. A misspelt subreddit name and a missing proxy both look like "no results", and neither should pass quietly. Rows already saved are kept either way. Turn this off only if an empty result is acceptable to you.

## Actor input object example

```json
{
  "subreddits": [
    "smallbusiness"
  ],
  "searchQueries": [],
  "searchInSubreddits": false,
  "sort": "new",
  "timeRange": "week",
  "maxPostsPerSource": 25,
  "minScore": 0,
  "includeComments": false,
  "maxCommentsPerPost": 50,
  "commentDepth": 10,
  "commentConcurrency": 5,
  "onlyNewSinceLastRun": false,
  "firstRunSince": "30 days",
  "stateStoreName": "reddit-scraper-state",
  "personalData": false,
  "maxItems": 100,
  "requestTimeoutSecs": 90,
  "maxRetries": 3,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "UNBLOCKER"
    ]
  },
  "failOnEmpty": true
}
```

# Actor output Schema

## `results` (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 = {
    "subreddits": [
        "smallbusiness"
    ],
    "searchQueries": [],
    "searchInSubreddits": false,
    "sort": "new",
    "timeRange": "week",
    "maxPostsPerSource": 25,
    "minScore": 0,
    "includeComments": false,
    "maxCommentsPerPost": 50,
    "commentConcurrency": 5,
    "onlyNewSinceLastRun": false,
    "firstRunSince": "30 days",
    "stateStoreName": "reddit-scraper-state",
    "stateNamespace": "",
    "postsNewerThan": "",
    "postsOlderThan": "",
    "personalData": false,
    "maxItems": 100,
    "requestTimeoutSecs": 90,
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "UNBLOCKER"
        ]
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("hridayrungta/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": ["smallbusiness"],
    "searchQueries": [],
    "searchInSubreddits": False,
    "sort": "new",
    "timeRange": "week",
    "maxPostsPerSource": 25,
    "minScore": 0,
    "includeComments": False,
    "maxCommentsPerPost": 50,
    "commentConcurrency": 5,
    "onlyNewSinceLastRun": False,
    "firstRunSince": "30 days",
    "stateStoreName": "reddit-scraper-state",
    "stateNamespace": "",
    "postsNewerThan": "",
    "postsOlderThan": "",
    "personalData": False,
    "maxItems": 100,
    "requestTimeoutSecs": 90,
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["UNBLOCKER"],
    },
}

# Run the Actor and wait for it to finish
run = client.actor("hridayrungta/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": [
    "smallbusiness"
  ],
  "searchQueries": [],
  "searchInSubreddits": false,
  "sort": "new",
  "timeRange": "week",
  "maxPostsPerSource": 25,
  "minScore": 0,
  "includeComments": false,
  "maxCommentsPerPost": 50,
  "commentConcurrency": 5,
  "onlyNewSinceLastRun": false,
  "firstRunSince": "30 days",
  "stateStoreName": "reddit-scraper-state",
  "stateNamespace": "",
  "postsNewerThan": "",
  "postsOlderThan": "",
  "personalData": false,
  "maxItems": 100,
  "requestTimeoutSecs": 90,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "UNBLOCKER"
    ]
  }
}' |
apify call hridayrungta/reddit-scraper --silent --output-dataset

```

## MCP server setup

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