Reddit Scraper | Posts, Comments and Search | No API Key
Pricing
from $1.00 / 1,000 posts
Reddit Scraper | Posts, Comments and Search | No API Key
Scrape Reddit posts, comments, subreddits, search & user history. No login, pay per result, full nested comment trees, monitor mode for new posts only. Use in Claude, ChatGPT & any MCP agent for market research, sentiment & AI training data.
Pricing
from $1.00 / 1,000 posts
Rating
5.0
(1)
Developer
The Mine Works
Maintained by CommunityActor stats
0
Bookmarked
152
Total users
49
Monthly active users
0.37 hours
Issues response
a day ago
Last modified
Categories
Features
Share
From The Mine Works, makers of Threads Scraper and B2B Leads Finder, with over 290,000 runs across our actors.
Updated 13 Nov 2026: on 12 and 13 Nov Reddit stopped serving its API and .json pages to automated readers. The actor now reads Reddit's normal public pages first and uses a public Reddit archive as backup. Every row says which in data_source. Modes, input fields and the price per post are unchanged. clientId is no longer needed.
Bookmark this Actor on its Store page so it is one click away next time.
More Reddit tools from The Mine Works: Reddit Comments Scraper for every comment and reply as a row, Reddit Subreddit Scraper for posts from any subreddit with a cap per subreddit, and Reddit User Scraper for profiles, karma and post history.
Why choose this actor?
- No Reddit account, no API key. The actor reads the public Reddit pages anyone can open without logging in. You do not need a Reddit login, a password, cookies or an API key of your own.
- Whole comment threads, nested the way Reddit shows them. Turn on comments and each post carries its comment tree with replies nested inside. Comments ride inside the post row; from 27 Nov 2026 each comment is charged (see Pricing).
- You pay only for posts that land in your dataset. $1.00 to $2.00 per 1,000 posts depending on your Apify plan. Summary rows, error rows, empty searches and posts that fail to save are never charged, and monitor mode charges only for posts you have not received before.
Part of The Mine Works Social media and video family: Threads Scraper, Threads Search Scraper, Instagram Profile Scraper, Instagram Followers & Following, Reddit Search Scraper, Twitter / X Scraper.
Try it in one minute
Paste this into the input's JSON tab and start the run. It returns the 10 top posts of the week from r/Python, each with up to 5 comments and two levels of replies.
{"mode": "subreddit","subreddits": ["Python"],"sortBy": "top","timeframe": "week","maxPosts": 10,"includeComments": true,"maxCommentsPerPost": 5,"maxDepth": 2}
There are four ways to say what you want, one per mode: subreddit names without the r/ ("Python", "MachineLearning"), a search query in Reddit's own search syntax ("lab grown diamonds" subreddit:EngagementRings), a username without the u/ ("spez"), or full post URLs that contain /r/<subreddit>/comments/<id>/ from www.reddit.com or old.reddit.com. Share links of the /s/ kind and redd.it short links are not read, so open them in a browser first and copy the full address.
Apify's free plan includes $5 of credit every month, which covers about 2,400 posts at this actor's price (the Free plan rate of $0.002 per post, plus the $0.02 start fee on each of 10 runs at the default 4,096 MB of memory).
Copy to your AI assistant
themineworks/reddit-scraper on Apify. Scrapes public Reddit posts, with optional nested comment trees, from subreddits, a search query, a user's post history or specific post URLs, with no Reddit login. Call ApifyClient("TOKEN").actor("themineworks/reddit-scraper").call(run_input={...}), then client.dataset(run["defaultDatasetId"]).list_items().items. Required: mode ("subreddit", "search", "user" or "post") plus the matching subreddits, searchQuery, username or postUrls. Optional: sortBy (default "hot"), timeframe (default "all", used with sortBy "top"), maxPosts (default 25, up to 1000 per run), includeComments (default false), maxCommentsPerPost (default 100), maxDepth (default 3), after (cursor from a previous run), skipPinnedPosts (default false), monitorMode (default false). clientId is no longer used and is ignored. Post rows have no _type field and carry data_source ("web" for live Reddit pages, "archive" or "google+archive" for the backup archive); archive rows under about 36 hours old also carry score_note, as their score and comment count may still be low. Every run also writes one row with _type "summary", a run that delivered posts cleanly adds one row with _type "info", and each failed target gets a row with _type "error"; none of these extra rows is charged. Full spec: GET https://api.apify.com/v2/acts/themineworks~reddit-scraper/builds/default (Bearer TOKEN), which returns inputSchema and readme. Token: https://console.apify.com/account/integrations?fpr=ymnoit&utm_source=apify-readme&utm_medium=referral
Key features
- Four modes in one actor.
subredditreads a community's hot, new, top or rising feed,searchruns a Reddit search across the whole site,userreads a person's submitted posts, andpostreads the exact threads you list. - Up to 23 fields per post. ID, subreddit, title, author, score, upvote ratio, link, body text, flair, comment count, creation time, pinned and locked flags, thumbnail, data source and more, all as flat top-level fields that drop straight into a spreadsheet.
- Nested comment trees. Turn on
includeCommentsand each post carries acommentsarray with replies nested insidereplies.maxCommentsPerPostgoes up to 500 andmaxDepthup to 10. The actor expands collapsed threads itself, up to those limits. - Up to 1,000 posts per run, with a cursor.
maxPostscaps the whole run. The summary row carrieslast_after_cursor, which you paste intoafterto continue a feed where the last run stopped. - Monitor mode for schedules. With
monitorModeon, the actor remembers up to 50,000 post IDs it has already delivered to you and skips them on later runs, so a daily run delivers and charges only new posts. - Live Reddit pages first, a public archive as backup. The actor reads Reddit's normal public pages in a real browser, through a proxy it brings itself. It only waits for Reddit's own check page to pass; it never clicks or solves anything. For any target the live pages cannot finish, it reads a public Reddit archive instead. Every row says which in
data_source.
How to use it
Basic: one subreddit, top posts of the month
{"mode": "subreddit","subreddits": ["smallbusiness"],"sortBy": "top","timeframe": "month","maxPosts": 100}
timeframe only matters with sortBy: "top". It takes hour, day, week, month, year or all.
Several subreddits in one run
{"mode": "subreddit","subreddits": ["personalfinance", "financialplanning", "fatFIRE"],"sortBy": "new","maxPosts": 300,"skipPinnedPosts": true}
Subreddits are read in the order you list them, and maxPosts is shared across all of them: the run stops once 300 posts are delivered in total. To get a fixed number from each one, run them separately or put each in its own saved task. skipPinnedPosts drops the moderator posts that sit at the top of most feeds.
Pain point research: what people complain about
{"mode": "search","searchQuery": "(subreddit:Landlord OR subreddit:PropertyManagement) (\"nightmare\" OR \"so frustrating\" OR \"is there a tool\")","sortBy": "top","timeframe": "year","maxPosts": 200}
Reddit search accepts subreddit:, author:, title:, quoted phrases, OR and NOT. This is the kind of query we run ourselves for product research: one run per phrase family, 200 posts each, then read the titles and bodies for repeated problems.
Brand mention alerts, delivered daily
{"mode": "search","searchQuery": "\"your brand name\"","sortBy": "new","maxPosts": 100,"monitorMode": true}
Save this as a task and put it on a daily schedule. The first run sets the baseline and delivers everything it finds. Every later run delivers and charges only posts that were not in an earlier run, and the summary row shows new_this_run and skipped_duplicates.
Read specific threads with every comment
{"mode": "post","postUrls": ["https://www.reddit.com/r/Python/comments/1woh8y4/whats_new_in_python_315/"],"maxCommentsPerPost": 500,"maxDepth": 10}
In post mode the comment tree is always fetched, whatever includeComments says. maxPosts still caps how many URLs are read.
A user's post history
{"mode": "user","username": "spez","maxPosts": 50}
User mode always returns the newest submissions first; sortBy and timeframe do not apply to it. It returns posts only, not the comments a user wrote on other threads. Reddit's live profile pages leave out posts in some communities (for example r/announcements), so a history that comes up short of maxPosts is checked against the archive, which adds only the posts that were missing.
Input parameters
| Parameter | Type | Default | What it does |
|---|---|---|---|
mode | string | subreddit | Required. subreddit, search, user or post. Picks which of the next four fields is read. |
subreddits | array | [] | Subreddit names without r/, for subreddit mode. Read in order; maxPosts is shared across them. |
searchQuery | string | none | Reddit search query, for search mode. Supports Reddit's search syntax. |
username | string | none | Reddit username without u/, for user mode. |
postUrls | array | [] | Full Reddit post URLs, for post mode. Comments are always fetched in this mode. |
sortBy | string | hot | hot, new, top or rising. Used by subreddit and search modes. In search, rising sorts by relevance. |
timeframe | string | all | hour, day, week, month, year or all. Only used with sortBy: "top". |
maxPosts | integer | 25 | Most posts delivered in the whole run, 1 to 1000. This is also your cost cap. |
includeComments | boolean | false | Adds each post's comment tree as a comments array. The actor reads each post's thread page, so runs take longer. |
maxCommentsPerPost | integer | 100 | Most comments kept for each post, 1 to 500. Used when comments are fetched. |
maxDepth | integer | 3 | How many levels of replies to follow, 1 to 10. 1 returns top level comments plus their direct replies; each step adds one level. |
after | string | none | A cursor such as t3_1va1tm3 from an earlier run's summary row. The run starts from that point in the feed. |
skipPinnedPosts | boolean | false | Skips moderator pinned posts. Skipped posts do not count toward maxPosts. |
clientId | string | none | No longer needed and ignored. Kept so old inputs that still carry it keep working. |
monitorMode | boolean | false | Delivers and charges only posts not delivered to you in an earlier run. |
What data do you get?
One row per post, plus one summary row per run. Post rows have no _type field, so filtering on _type separates them from the rest.
The post: id (such as 1woh8y4), name (the full Reddit ID, t3_1woh8y4), title, selftext (body text, empty for link posts), is_self, url (the linked page, or the thread itself for text posts), permalink, domain, flair, thumbnail (when the post has one).
Where and who: subreddit, subreddit_id, author, author_fullname.
Engagement and status: score, upvote_ratio, num_comments, is_pinned, is_locked, created_utc (Unix seconds), scraped_at (ISO time the row was written). is_pinned and is_locked are sent on rows from the live site only, because the archive does not keep them.
Source: data_source (web, archive or google+archive, see below) and score_note (only on archive rows under about 36 hours old).
Search rows from the live site carry title, author, score, num_comments, created_utc, subreddit and permalink, but not selftext, url, is_self, domain, flair or upvote_ratio, because Reddit's search page does not show them. Turn on includeComments to read each thread page, which fills them in.
Comments (when fetched): each comment has id, name, author, body, score, created_utc, is_submitter (true when the post's author wrote it), depth (0 for top level) and replies, a list of comments in the same shape. The actor expands collapsed branches itself, up to maxCommentsPerPost and maxDepth. Deleted and removed comments are left out; a reply to a removed comment is kept at the top level with its real depth.
No longer sent: awards_count, and the collapsed more stubs that used to appear in comment trees.
Summary row (_type: "summary", never charged): posts_requested, posts_scraped, posts_failed, subreddits_processed, charged_for, last_after_cursor (when the feed has more), ended_on_deadline (true when the run stopped early for its time limit), monitor, plus previously_seen, new_this_run and skipped_duplicates in monitor mode, and shortfall_reason when nothing was found or the run stopped for time. It also counts where the posts came from: posts_from_web, posts_from_archive, posts_from_google_archive, posts_with_score_note and comments_delivered, and targets lists each target with its status and the routes used.
Info row (_type: "info", never charged): written on runs that delivered posts with no archive rows and no failed targets, with the count and a note on scheduling.
Error rows (_type: "error", never charged): one per target that failed, with error_type (not_found, fetch_error, invalid_url or post_processing_error), identifier, reason and http_status.
How the data is collected
Every row says which route it came from, in data_source:
web: Reddit's normal public pages, read live through a real browser. This is the first route for every target. Scores, comment counts, flair and pinned state are what Reddit shows at that moment.archive: a public Reddit archive, used for a target only when the live site could not be read for it. It keeps posts and comments as they are posted, so new posts are usually there within minutes.google+archive: for a search across all of Reddit when the live site could not be read. The actor finds matching threads with a Google search limited to reddit.com, then reads those posts from the archive. Google's index lags, so this route finds older discussion, not posts from today.
When a subreddit feed, search or user history ends short of the number asked because Reddit's live page stopped early, the actor tops it up from the archive with new posts only: no repeats, and maxPosts still holds. In post mode, a thread whose live page showed far fewer comments than its count is topped up the same way.
Comments. In subreddit and search mode with comments on, the thread of a post younger than 36 hours is read live from Reddit. The thread of a post 36 hours or older is read from the public Reddit archive, where its comment scores have mostly settled; this keeps runs fast and cheap. Comments with score_note are under about 36 hours old. In post and user mode every thread is read live first.
Scores on archive rows. The archive updates a post's score and comment count about 36 hours after it was posted. Until then they are often 1 and 0. Any archive row (post or comment) under about 36 hours old carries score_note saying so. Rows from the live site never carry it.
The run log has one line per target saying which route served it and why, and the summary row's targets list says the same. A run ends FAILED only when no route could read any target.
Stable fields for automations
These field names will not change, so a Google Sheet, Zapier zap or n8n flow can map them once. Most post rows carry all of them; the exceptions are listed under the table.
| Field | What it is |
|---|---|
id | Reddit post ID, the key to store and join on |
name | Full ID with the t3_ prefix |
subreddit | Community name, without r/ |
title | Post title |
author | Author's username, or [deleted] |
score | Upvotes minus downvotes at the time of the run |
upvote_ratio | Share of votes that are upvotes, 0 to 1 |
num_comments | Comment count Reddit shows on the post |
url | Linked page, or the thread for text posts |
permalink | Full link to the Reddit thread |
selftext | Body text, empty string for link posts |
created_utc | Post time in Unix seconds, UTC |
is_pinned | True for moderator pinned posts |
scraped_at | When this row was written, ISO 8601 |
flair and author_fullname are left out of a row when Reddit has no value, so treat them as optional. Search rows from the live site have no upvote_ratio, url or selftext unless includeComments is on. Archive rows have no is_pinned, subreddit_id or author_fullname. The comments key appears only when comments were fetched in that run.
Output examples
A subreddit post with its comment tree (test run, r/Python top of the week, maxDepth: 2; body and replies trimmed):
{"id": "1wqy9yg","name": "t3_1wqy9yg","subreddit": "Python","subreddit_id": "t5_2qh0y","title": "The Python documentation is now available in Persian","author": "AlSweigart","author_fullname": "t2_369qx","score": 145,"upvote_ratio": 0.91,"url": "https://www.reddit.com/r/Python/comments/1wqy9yg/the_python_documentation_is_now_available_in/","permalink": "https://www.reddit.com/r/Python/comments/1wqy9yg/the_python_documentation_is_now_available_in/","selftext": "https://blog.python.org/2026/09/the-python-documentation-is-now-available-in-persian/ ...","is_self": true,"domain": "self.Python","flair": "News","num_comments": 7,"created_utc": 1790448473,"is_pinned": false,"is_locked": false,"scraped_at": "2026-09-29T22:44:52.760Z","data_source": "web","comments": [{"id": "pc7zmbx","name": "t1_pc7zmbx","author": "AlSweigart","body": "I think it's great that the Python devs aren't just throwing ChatGPT at translation projects. ...","score": 41,"created_utc": 1790448522,"is_submitter": true,"depth": 0,"replies": [{"id": "pc8o80p","name": "t1_pc8o80p","author": "shinitakunai","body": "A big burden to localize all texts if you are alone. ...","score": 10,"created_utc": 1790455249,"is_submitter": false,"depth": 1,"replies": []}]}]}
A search result (recorded run, a search across four expat and visa subreddits, top of the year, shown in the shape the live search page gives now):
{"id": "1qlqn4l","name": "t3_1qlqn4l","subreddit": "expats","subreddit_id": "t5_2rhwp","title": "Malta looks great on holiday. Here’s what daily life is actually like","author": "True-Ingenuity-8974","author_fullname": "t2_e48mtn3e","score": 303,"permalink": "https://www.reddit.com/r/expats/comments/1qlqn4l/malta_looks_great_on_holiday_heres_what_daily/","num_comments": 130,"created_utc": 1769270342,"scraped_at": "2026-09-24T19:22:45.888Z","data_source": "web"}
This row has no body, link or flair because Reddit's search page does not show them. With includeComments on, the actor reads the thread page and fills them in.
The summary row (the recorded r/CFP run):
{"_type": "summary","posts_requested": 300,"posts_scraped": 300,"posts_failed": 0,"subreddits_processed": 1,"charged_for": 300,"last_after_cursor": "t3_1va1tm3","monitor": false,"scraped_at": "2026-09-24T19:20:29.995Z"}
An error row and the summary of a run that found nothing (test run on a subreddit that does not exist; nothing was charged):
[{"_type": "error","error_type": "not_found","identifier": "r/thissubdoesnotexist98765","reason": "Resource not found (404)","http_status": 404,"scraped_at": "2026-09-29T22:47:53.487Z"},{"_type": "summary","posts_requested": 5,"posts_scraped": 0,"posts_failed": 0,"subreddits_processed": 1,"charged_for": 0,"monitor": false,"scraped_at": "2026-09-29T22:47:53.489Z","shortfall_reason": "No posts found matching the input criteria."}]
Pricing
Pay per event. You are charged for each post delivered to your dataset, plus a small start fee per run. The rate falls as your Apify plan rises.
| Event | Free | Bronze | Silver | Gold and above |
|---|---|---|---|---|
Post delivered (post-scraped), per post | $0.002 | $0.0015 | $0.00125 | $0.001 |
| Per 1,000 posts | $2.00 | $1.50 | $1.25 | $1.00 |
Comment delivered (comment-scraped), per comment, from 27 Nov 2026 | $0.002 | $0.0015 | $0.00125 | $0.001 |
Run start (apify-actor-start) | $0.005 per GB of run memory, minimum one event | same | same | same |
The start fee, exactly. Apify's apify-actor-start event is charged once when a run starts, one event per GB of memory with a minimum of one. At the default 4,096 MB (4 GB) that is four events, $0.02 per run. The live route runs a browser, which needs that memory; a run started with less than 2,048 MB reads from the archive only.
Comments, from 27 Nov 2026. Until 26 Nov a post's comment tree rides inside the post row at no extra charge. From 27 Nov 2026 each comment delivered is charged once as comment-scraped (every nested reply counts as one comment): $0.002 on the Free plan, $0.0015 on Bronze, $0.00125 on Silver, $0.001 on Gold. Runs without comments cost the same as before. Your maximum cost per run covers posts and comments together: a post is delivered only with all its comments, and only when both fit.
Never charged: the summary row, the info row, error rows, posts that fail to save, posts skipped by skipPinnedPosts, and posts skipped by monitor mode because you already have them. A run that finds nothing pays only the start fee.
What real jobs cost on Gold: 300 posts cost $0.30 plus $0.02. A daily brand search in monitor mode that finds 20 new posts costs about $0.04 a day.
The only price change scheduled is the comment charge from 27 Nov 2026 above. The Pricing tab on this page always shows the rate for your own plan.
FAQ
What is Reddit, and what does this actor read from it? Reddit is a network of topic communities (subreddits) where people post links and text and discuss them in threaded comments. This actor reads public posts and their comments: a subreddit's feeds, Reddit's site search, a user's submitted posts, or the threads you name.
How many posts can I get?
Up to 1,000 per run. Reddit itself stops serving a single feed after roughly a thousand posts: a test run asking for 1,000 of r/Python's newest posts got 948 before Reddit's listing ended. To go further back, combine sorts and time windows (top with year, then all, then new), split a search into narrower queries, or pass the after cursor from an earlier run's summary row.
How fresh is the data?
Rows with data_source web are live: read from Reddit at the moment the run reaches them, stamped in scraped_at. Rows from the archive are as fresh as the archive, usually minutes, but their scores and comment counts can be up to about 36 hours behind (see score_note).
Do I need a Reddit account, API key or cookies?
No. The actor reads Reddit's public pages and needs no login. The clientId field is no longer used; old inputs that still have it keep working.
Do I need a proxy? No. The actor brings its own. You do not set anything.
What memory should I give the run? 4,096 MB, the default. The live route runs a browser. A run started with less than 2,048 MB (for example an old saved task with 512 MB) reads from the archive only and says so in its log.
How do I get the data out?
From the run's Storage tab as JSON, CSV, Excel, XML or HTML, or through the Apify API. JSON keeps the nested comments tree intact. CSV and Excel flatten nested fields, so for comment work use JSON.
What happens with deleted, removed or private content?
Deleted and removed posts and comments are left out. A subreddit or user that does not exist, is private or is banned produces an error row (not_found or fetch_error) and is not charged. Quarantined communities are not shown to readers who are not logged in, so expect an error row there.
How do I run it on a schedule or get only new posts?
Save your input as a task, then add a schedule in Apify Console (Schedules, then Create new, for example daily at 06:00). Turn on monitorMode so each run delivers and charges only posts you have not received before. Monitor mode keeps up to 50,000 post IDs in a storage named monitor-reddit-scraper in your own Apify account, shared by all your runs of this actor. If that storage cannot be opened, the run says so in its log, delivers everything, and marks monitor_degraded: true in the summary row.
Why did a run return fewer posts than maxPosts?
The feed or search ran out, pinned posts were skipped, or monitor mode skipped posts you already had. The summary row says which: posts_scraped, skipped_duplicates and shortfall_reason. When Reddit's live page stops early, before the feed really ends, the actor already tops the target up from the archive with posts it did not have yet. If you set a maximum cost per run in Apify and it is reached, the run stops delivering posts at that point.
What happens when a run reaches its time limit?
The default time limit is 600 seconds. The actor stops starting new work about 25 seconds before it, keeps every post already delivered and ends as Succeeded with ended_on_deadline: true. last_after_cursor names the last post handled; pass it in after to continue. On new feeds and user history the run then picks up the posts older than that one. On hot, top and search it picks up after that post if it is still in the list. Comments mean one thread page per post, so includeComments with a large maxPosts needs more time.
A run ended FAILED. Why? Only when no route could read any target. Nothing is charged per post in that case, and the error row and status message say what happened.
Can I get the comments of a post without scraping the whole subreddit?
Yes. Use post mode with the thread URLs. Comments are always fetched in that mode, up to maxCommentsPerPost and maxDepth.
Can I use it from Claude, ChatGPT or another AI assistant?
- Connector URL:
https://mcp.apify.com/?tools=themineworks/reddit-scraper. - Claude: Settings > Connectors > Add custom connector, paste the URL, sign in with Apify.
- ChatGPT: developer mode, add an MCP connector with the URL, sign in with Apify.
- Cursor or VS Code: add it as an HTTP MCP server with that URL.
- Claude Code:
claude mcp add -t http reddit-scraper "https://mcp.apify.com/?tools=themineworks/reddit-scraper".
It is also one of the tools inside our Social Research MCP server, which bundles 8 social research tools.
Is it legal to scrape Reddit? The actor reads only public posts and comments, the content anyone can see without logging in, and nothing behind a login. Public posts can still contain personal data such as usernames. You are responsible for how you use the data, including Reddit's terms and data protection law such as GDPR and CCPA. This is general information, not legal advice.
Integrations
Results land in a standard Apify dataset, so they connect without extra code:
- Google Sheets: send each run's posts to a sheet with Apify's Google Sheets integration.
- Make, Zapier and n8n: start a run and read its dataset with the official Apify apps and nodes.
- Webhooks: get a call to your own URL when a run succeeds, then fetch the dataset.
- API and SDKs: start runs and read results with the Apify API, or the Python and JavaScript clients.
- MCP clients: Claude, ChatGPT, Cursor and other MCP clients can call the actor through
https://mcp.apify.com/?tools=themineworks/reddit-scraper.
More from The Mine Works
Social media and video
- Threads Scraper
- Threads Search Scraper
- Instagram Profile Scraper
- Instagram Followers & Following
- Reddit Search Scraper
- Twitter / X Scraper
- Xiaohongshu (RED) Scraper
- YouTube Transcript
- Telegram Channel Scraper
- Telegram Channel Finder
- Pinterest Profile Scraper
- TikTok Scraper
Leads and business directories
Marketing, SEO and reviews
Real estate
Science, health and government data
Jobs and hiring
E-commerce and marketplaces
Company and business data
Food and local services
Developer and AI tools
More tools
- Tennis Match & Player Data Scraper
- Google Hotels Prices Scraper
- LandWatch Scraper
- Taobao Products Scraper 淘宝 天猫
Support
Found a bug or need a field? Open an issue on the Issues tab of this actor. To ask for a new source, email dmineworks@gmail.com.
Reddit Scraper pulls public Reddit posts and nested comment threads from subreddits, search, users or post links, with no login, from $1 per 1,000 posts.

