reddit-posts-scraper-by-keyword-score-filter avatar

reddit-posts-scraper-by-keyword-score-filter

Pricing

$15.99/month + usage

Go to Apify Store
reddit-posts-scraper-by-keyword-score-filter

reddit-posts-scraper-by-keyword-score-filter

Scrape Reddit posts from any subreddit with ease. This actor collects titles, upvotes, timestamps, authors, comment counts, and post URLs. Perfect for research, trend tracking, content analysis, and automations that rely on fresh Reddit data.

Pricing

$15.99/month + usage

Rating

0.0

(0)

Developer

Scraper Engine

Scraper Engine

Maintained by Community

Actor stats

0

Bookmarked

6

Total users

2

Monthly active users

2 days ago

Last modified

Share

Reddit Posts Scraper โ€” Keyword Filter, Scores, Flair and Comments

Reddit Posts Scraper pulls public Reddit posts from subreddits, Reddit URLs and search terms, then keeps only the ones matching your keyword watchlist and clearing a minimum score. Every row carries title, body, author, score, num_comments, linkFlairText and a nested comments tree, plus the filter provenance that produced it. Structured JSON, no HTML parsing, no Reddit account or API key. Paste a subreddit, set a keyword and a score floor, press Start.

startUrls decides what each line means by looking at it, and the rule is mechanical: a line starting with http is a URL, a line starting with r/ is a subreddit, a line containing no space and no + is a subreddit, and everything else runs as a Reddit search. So artificial intelligence searches Reddit, but layoffs fetches r/layoffs โ€” a real subreddit that is not what you meant. To search on a single word, add a +: layoffs+2026, or use two words.

Two related consequences worth knowing before your first run:

  • A Reddit search-results URL pasted into startUrls is classified as a URL, not a search, and is then read with the listing parser โ€” which looks for post containers that a search page does not have. It returns zero posts. Enter the search phrase itself instead.
  • Only URLs of the exact form /r/<name> get your Sort order and Time range appended. A deeper URL is fetched exactly as you typed it, so sortOrder and timeFilter are silently ignored for it.

What is Reddit Posts Scraper?

Reddit Posts Scraper is an Apify Actor that reads public Reddit listing, search and comment pages from old.reddit.com as HTML and flattens each post into one typed dataset row, with its top-level comments and their replies nested inside it. On top of the raw post it applies a social-listening layer: a case-insensitive keyword watchlist with four match modes, a minimum-score floor and an NSFW exclusion.

No Reddit account, login, OAuth app or API key is required. The Actor never authenticates. The only cookie it sends is over18=1, which is the age confirmation any logged-out visitor clicks through โ€” it is not a session.

It is built for social-listening and brand-monitoring teams, market researchers tracking how communities discuss a topic, and developers piping Reddit discussion text into dashboards or AI pipelines.

What Reddit post data is publicly available to scrape?

A logged-out Reddit listing page carries the post title, the author's username and stable account id, the score, the comment count, the timestamp, the flair, the link domain and outbound URL, the crosspost count, and the full body text of self posts. Comment threads on a public post are equally open. Everything tied to an account โ€” votes, saves, messages, mod queues โ€” is not.

Data CategoryPublicly AvailableGated behind a Reddit account or not exposed in HTML
Post title, author, score, comment count, timestamp, subredditโœ… Publicโ€”
Self-post body text, flair, link domain, outbound URL, crosspost countโœ… Publicโ€”
Top-level comments and their nested replies, with author and scoreโœ… Publicโ€”
NSFW posts and NSFW subredditsโœ… Public behind an age interstitialThe Actor sends the over18=1 confirmation cookie โ€” no account involved
Upvote ratioโŒReddit publishes it in the .json API response, not in the old.reddit.com HTML this Actor reads โ€” not returned
Awards and gallery image setsโŒNot parsed and not returned
Private, quarantined or banned subredditsโŒNot accessible without an account, or not at all
Your votes, saved posts, messages, moderator queueโŒBehind a Reddit login โ€” never returned
An author's real name, email or any contact detailโŒReddit does not publish it

Reddit Posts Scraper only returns publicly visible data โ€” what any visitor sees. Nothing behind a login wall.

What data can I extract with Reddit Posts Scraper?

Every charged row describes one Reddit post and carries up to 30 keys: post identity and text, engagement metrics, safety and link metadata, and a record of exactly which filters let the row through. Comments arrive as a nested array of objects with 5 further keys each, replies included.

Field NameDescription
post_idReddit's base-36 post id, taken from the t3_ fullname. Always present on charged rows
titlePost title. "No Title" when the title link cannot be read
authorAuthor's username. "Unknown" when Reddit omits the author attribute
authorFullnameReddit's stable t2_ account id for the author. Omitted when not exposed
subredditSubreddit name without the r/ prefix
subredditTypeSubreddit visibility as Reddit labels it, defaulting to "public"
bodyFull self-post text, flattened to plain text. "" for link posts and for every search-sourced row
scoreNet upvote score as Reddit displayed it at fetch time
num_commentsComment count Reddit displayed on the listing
numCrosspostsNumber of crossposts. 0 on search-sourced rows
created_utcPost creation time as a Unix epoch in seconds. Omitted when unreadable
publishedAtThe same moment as ISO-8601 UTC, YYYY-MM-DDTHH:MM:SSZ. Omitted when created_utc is
scrapedAtISO-8601 UTC stamp of when the Actor wrote the row โ€” the Actor's clock, not Reddit's
permalinkReddit-relative path to the post, e.g. /r/technology/comments/โ€ฆ/
postUrlThe same post as an absolute https://www.reddit.com link
outboundUrlThe URL the post links out to. Omitted when empty
domainLink domain Reddit shows under the title, e.g. reuters.com or self.technology. Omitted on search-sourced rows
image_urlImage or media URL for link posts. "" for self posts and for every search-sourced row
thumbnail_urlThumbnail image URL, or one of Reddit's placeholder words self, default, nsfw, or ""
linkFlairTextPost flair text. Omitted when the post has no flair
over18true when Reddit flags the post NSFW
spoilertrue when the post is marked a spoiler. Always false on search-sourced rows
commentsArray of comment objects, nested. [] when maxComments is 0 or the thread could not be read
matchedKeywordsArray of the watchlist terms that matched this post, lowercased. [] when keyword filtering is off
matchedKeywordThe first entry of matchedKeywords, for a flat CSV column. Omitted when there was no match
keywordModeThe filterKeywordMode in force for this run. Omitted entirely when filterKeywords was empty
passedFiltersAlways true on a charged row โ€” posts that fail the filters are never written. An audit echo
minScoreAppliedThe minScore value in force for this run, echoed onto every row
successtrue on charged post rows, false on uncharged error rows
error_messageOnly present on uncharged error rows. Omitted from every successful row

Post identity, text and link fields

title and body are the two text fields worth indexing. body is Reddit's complete self-post text flattened to plain text โ€” never truncated, but empty for link posts and for every row that came from a search-term source, because Reddit's search results page does not include post bodies. permalink is the relative path, postUrl the absolute link, and outboundUrl the external destination for link posts. domain reads self.<subreddit> for text posts, which is also how the Actor decides whether to populate image_url.

author and authorFullname are two views of the same person: the display username, which can change, and Reddit's stable t2_ account id, which does not.

Engagement, safety and filter-provenance fields

score and num_comments are Reddit's own counters read straight off the page. Reddit fuzzes vote counts and they move constantly, so score is a point-in-time snapshot taken at fetch โ€” a post that read 480 when it was collected may read differently on Reddit a minute later. Treat a minScore result as "cleared the floor when we looked", not as a fixed property of the post. numCrossposts, over18 and spoiler come from the same listing attributes; on search-sourced rows numCrossposts is 0 and spoiler is false, because Reddit's search results markup does not carry them.

matchedKeywords, matchedKeyword, keywordMode and minScoreApplied exist so a row explains itself months later: you can see which watchlist term caught the post and what thresholds were in force, without keeping the run input alongside the data.

Each object inside comments has exactly five keys โ€” author (falling back to [deleted] when Reddit renders no author link), body, score, created_utc and replies, which is another array of the same shape. Replies nest up to 9 levels; comments with no body text are skipped rather than written as blanks.

๐Ÿค– Add-on: Need additional Reddit data?

Reddit Trends Scraper By Sort, Time & Score Filter covers the same platform from the trend side, for when you want what is rising right now rather than what mentions your keyword. Reddit User Profile Posts And Comments Scraper With Account Age goes the other way โ€” take an author from these rows and pull that account's full posting history with karma and account age. For the same discussion research on another platform, Quora Search Scraper is the closest equivalent.

How does Reddit Posts Scraper differ from the official Reddit API?

Reddit does publish a Data API, but it is a credentialed surface: you register an application, authenticate with OAuth, and your usage falls under Reddit's developer and commercial terms. This Actor reads the same public pages a logged-out visitor sees, with no registration step, and adds filtering that the API leaves to you.

FeatureReddit Data APIReddit Posts Scraper
CredentialsRegistered app plus OAuth credentials; commercial use is governed by Reddit's developer terms โ€” verify against Reddit's current published API termsโœ… None. Reads old.reddit.com HTML anonymously
Rate limitingEnforced per credential, under terms Reddit revises โ€” verify against Reddit's current published API termsNot credential-based. The Actor paces itself between pages and escalates through proxies when blocked
Keyword and score filteringNot provided as this Actor defines it โ€” you fetch a listing and filter it yourself, or learn Reddit's own search syntaxโœ… Built in: filterKeywords with four match modes, minScore, excludeNsfw
CommentsA separate call per post, returned as its own tree to stitch togetherโœ… Fetched automatically for each kept post and nested under comments
Upvote ratioPresent in the JSON payloadโŒ Not returned โ€” the Actor's own source notes record it as JSON-only and therefore dropped
Output shapeReddit's raw Listing envelope, to be unwrappedโœ… One flat typed row per post, same keys every run
Where it runsYour infrastructure, your IP addresses, your retry logicโœ… Apify, with the proxy ladder and retries handled

Use the official API when you need write access, authenticated endpoints, upvote_ratio, or a commercial arrangement Reddit has approved. Use this Actor when you want filtered, ready-to-analyse public post data without registering an application.

How to use Reddit Posts Scraper

Reddit Posts Scraper runs on Apify. Start it from the Apify Console or call it through the Apify API. No Reddit credentials are involved at any point.

  1. Open Reddit Posts Scraper on Apify and click Try for free
  2. Fill Subreddits, URLs or search terms (startUrls) โ€” this is the one required input. One entry per line; mix subreddit names, r/ names, full Reddit URLs and multi-word search phrases freely
  3. Optionally set Watchlist keywords (filterKeywords) and a Keyword match mode (filterKeywordMode) to keep only posts that mention your terms
  4. Optionally set a Minimum score (minScore) and switch on Exclude NSFW posts (excludeNsfw) to drop low-engagement or NSFW posts before they are written
  5. Set Max matching posts per source (maxPosts) and Max comments per post (maxComments) โ€” set maxComments to 0 to skip comment fetching entirely and run faster
  6. Click Start, then export as JSON, CSV or Excel, or read the dataset through the Apify API

A run submitted with an empty or missing startUrls does not fall back to a default subreddit. Input validation raises startUrls is required and cannot be empty, and the run fails immediately with zero rows written and nothing charged.

How to scale to bulk Reddit post extraction

startUrls is a list, so bulk is the normal mode โ€” put fifty subreddits and ten search phrases in one run. Exact duplicate lines are merged before the run starts. Beyond the list, the field is forgiving about shape: a newline-separated string works, and { "url": "..." } objects are accepted alongside plain strings, so output from another tool usually drops in unchanged.

maxPosts is per source, not per run. Ten sources with maxPosts: 25 is a run that can write up to 250 rows. Sources are processed one after another, and within a source the kept posts have their comments fetched five at a time.

What can you do with Reddit post data?

  • ๐Ÿ”Ž A brand monitoring lead watching for product complaints runs r/technology and r/gadgets with filterKeywords set to their product names and minScore: 25, so only complaints the community actually upvoted reach the dashboard.
  • ๐Ÿ“ˆ A market researcher sizing demand for a category collects title, body and score across a month of sortOrder: "top" and ranks themes by score rather than by raw post volume.
  • ๐Ÿ’ผ A recruiter sourcing from niche communities filters the dataset on linkFlairText and reads author plus permalink to reach candidates in the thread where they posted.
  • ๐Ÿงญ A community manager tracking sentiment shifts pulls the nested comments tree, where each object's score shows which replies the room agreed with, not just which post started the conversation.
  • ๐Ÿค– An AI engineer building a discussion-aware assistant indexes title and body into a vector store with subreddit, score, publishedAt and matchedKeywords as metadata filters, so the agent can answer "what are people saying about this right now" from live threads instead of a stale export.

Every one of these is callable from an agent framework over the Apify API, since the Actor is a standard HTTP-triggered run.

How does Reddit Posts Scraper handle rate limits and blocking?

Egress runs on a proxy escalation ladder that starts at the cheapest rung. With the default proxy setting the run begins on a direct connection with no proxy at all. Only when Reddit pushes back does the Actor climb: direct โ†’ Apify datacenter โ†’ Apify residential, logging each switch and staying on the higher rung once it has escalated. On the residential rung it rotates to a fresh IP up to three more times before giving up on a target.

Requests are made with curl_cffi impersonating Chrome's TLS fingerprint and carrying a full desktop browser header set โ€” no headless browser is launched, which is why comment-heavy runs stay light. A response counts as blocked when it returns 403 or 429, or when it returns 200 but contains no post or comment containers, which is what Reddit serves for a private, quarantined or banned subreddit. Each page gets up to four attempts, with a wait that grows by one second per attempt and a proxy escalation between them.

There is no CAPTCHA solving in this Actor, and none is claimed. If a page is still blocked after every attempt the Actor logs a warning, writes nothing for that source and moves to the next one. A comment fetch that fails is caught separately: the post row is still written, with comments set to [].

โฌ‡๏ธ Input

Twelve parameters. Only startUrls is required โ€” everything else has a working default, so the minimum viable input is a single line of text.

ParameterRequiredTypeDescriptionExample Value
startUrlsYesarraySubreddits, Reddit URLs or search terms, one per line. Mix freely: news or r/technology (subreddit), https://www.reddit.com/r/news/ (URL), artificial intelligence (search). Duplicate entries are merged.["r/technology", "https://www.reddit.com/r/news/", "artificial intelligence"]
filterKeywordsNoarrayWords or phrases to match against each post. Case-insensitive. Leave empty to disable keyword filtering and keep every post the source returns. Default [].["layoffs", "acquisition"]
filterKeywordModeNostringHow the watchlist must match. One of any, all, titleOnly, exactPhrase. Default any.all
minScoreNointegerKeep only posts whose score is at least this value. Minimum 0. Default 0, which keeps every score.500
excludeNsfwNobooleanDrop posts Reddit flags NSFW before they are written. Default false.true
restrictToSubredditNobooleanRun search-term sources as an in-subreddit search (restrict_sr=1) instead of a global Reddit search. Needs searchSubreddit set. Default false.true
searchSubredditNostringSubreddit name used when restrictToSubreddit is on. Ignored when that toggle is off. Default "".technology
sortOrderNostringReddit's feed sort. One of hot, new, top, rising. Default top.new
timeFilterNostringTime window. One of hour, day, week, month, year, all. Default week.day
maxPostsNointegerMaximum posts to keep per source, after filtering. Minimum 1, maximum 1000. Default 10.50
maxCommentsNointegerTop-level comments to fetch for each kept post. Minimum 0, maximum 1000. Set 0 to skip comments. Default 5.20
proxyConfigurationNoobjectApify Proxy configuration. Prefilled {"useApifyProxy": false}, which starts the run on a direct connection; the Actor escalates to datacenter and then residential on its own when blocked.{"useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"]}

Seven honest notes on how these actually behave:

  • filterKeywordMode in exact terms. Matching is plain case-insensitive substring matching, not whole-word matching โ€” ai matches said. any keeps a post when at least one term appears in the title or the body. all requires every term to appear somewhere in title or body. titleOnly searches the title alone and keeps the post when any term matches โ€” there is no all-terms-in-title mode. exactPhrase is the surprising one: it joins your entire keyword list into a single space-separated string and looks for that one string in title and body. ["machine", "learning"] in exactPhrase searches for machine learning, and matchedKeywords returns that joined phrase rather than the individual terms, so list order matters. An empty filterKeywords list disables the filter entirely โ€” every post passes, matchedKeywords is [], and keywordMode is left off the row.
  • The filters run in this Actor, not on Reddit. The Actor fetches a listing page, parses it, then applies the keyword, score and NSFW tests locally to each post. Reddit never sees your watchlist. The upside is that discarded posts are never written and never charged โ€” the score floor and the keyword filter cost you nothing in row events. What they do cost is run time, because the Actor keeps paginating until it has maxPosts matches, so a very selective filter means many pages fetched for few rows.
  • timeFilter reaches Reddit differently by source type. For subreddit and /r/<name> URL sources it is sent only when sortOrder is top or rising, exactly as the field description says. For search-term sources it is always sent to Reddit's search, whatever the sort.
  • โš ๏ธ searchSubreddit mangles subreddit names beginning with r. The code strips leading r and / characters so it can tolerate an r/ prefix, which correctly turns r/technology into technology โ€” but also turns rust into ust and relationships into elationships. If your target subreddit starts with r, do not use restrictToSubreddit; put the subreddit itself in startUrls as its own source instead, where the name is parsed correctly.
  • requestDelay is accepted but is not in the schema. The code reads a requestDelay value from the input and uses it as the pause in seconds between listing pages and between sources, defaulting to 1 plus up to half a second of random jitter. The Console will not show the field because it is not in the input schema; an API caller can send it. Negative values are clamped to 0.
  • keywords is an undocumented alias for filterKeywords. When filterKeywords is absent the code falls back to a keywords field. Either one may also be sent as a plain string, in which case it is split on newlines and commas.
  • maxPosts and maxComments are clamped by this Actor, not by Reddit. Values are forced into 1โ€“1000 and 0โ€“1000 respectively. maxComments caps top-level comments only โ€” the replies nested under those comments are returned in full, so a run with maxComments: 5 on a busy thread can still return dozens of comment objects.

Example input

{
"startUrls": [
"r/technology",
"https://www.reddit.com/r/news/",
"artificial intelligence"
],
"filterKeywords": ["layoffs", "acquisition", "restructuring"],
"filterKeywordMode": "any",
"minScore": 250,
"excludeNsfw": true,
"restrictToSubreddit": false,
"searchSubreddit": "",
"sortOrder": "top",
"timeFilter": "week",
"maxPosts": 50,
"maxComments": 10,
"proxyConfiguration": {
"useApifyProxy": true,
"apifyProxyGroups": ["RESIDENTIAL"]
}
}

โฌ†๏ธ Output

Typed, normalized JSON with the same key names on every run. Rows stream into the dataset as each post is processed, so the table fills while the run is still going. Export as JSON, CSV or Excel, or read the dataset through the Apify API.

Keys whose value would be null are omitted from the row rather than written as null, so use a presence check (row.get("linkFlairText")) instead of assuming a key exists. Empty strings, false and 0 survive and are always written. The keys most often absent are linkFlairText, domain, outboundUrl, authorFullname, matchedKeyword, keywordMode and error_message.

Error rows are pushed uncharged. When a post fails while its comments are being fetched or its row is being built, the Actor writes a short accounting row with no charged event attached โ€” it costs nothing. The verified marker is the success field, backed by error_message: charged post rows always carry "success": true and have error_message stripped out entirely, while uncharged error rows carry "success": false and an error_message string. There is no errorReason, status, isDiagnostic, rowType or charged field anywhere in this Actor โ€” success is the one to filter on:

posts = [r for r in items if r.get("success") is True]
errors = [r for r in items if r.get("success") is False]

An error row carries only post_id, title, author, subreddit, success and error_message. Note that it is written only when an already-collected post fails downstream; a source that could not be fetched at all, or that returned nothing passing the filters, writes no row of any kind and shows up only in the run log.

Example output

A full post row, from a subreddit source with keyword filtering on:

{
"post_id": "1n4qk2x",
"title": "Chipmaker announces 4,000 layoffs as it restructures its foundry division",
"author": "quietanalyst",
"created_utc": 1753372841.0,
"num_comments": 1842,
"score": 24713,
"permalink": "/r/technology/comments/1n4qk2x/chipmaker_announces_4000_layoffs_as_it/",
"image_url": "https://www.reuters.com/technology/chipmaker-foundry-restructuring-2026-07-24/",
"thumbnail_url": "https://b.thumbs.redditmedia.com/8Kx2fQ0pVn3yTgHc6mLwZrJd1sAoEeYb.jpg",
"body": "",
"comments": [
{
"author": "semi_engineer_",
"body": "The foundry side has been burning cash for six quarters. This was priced in the moment they split the reporting segments.",
"score": 3841,
"created_utc": 1753375102.0,
"replies": [
{
"author": "marketwatcher88",
"body": "Priced in for investors, not for the 4,000 people who found out from a press release.",
"score": 2204,
"created_utc": 1753376890.0,
"replies": []
}
]
},
{
"author": "[deleted]",
"body": "[removed]",
"score": 412,
"created_utc": 1753377455.0,
"replies": []
}
],
"subreddit": "technology",
"success": true,
"matchedKeywords": ["layoffs", "restructuring"],
"matchedKeyword": "layoffs",
"keywordMode": "any",
"passedFilters": true,
"minScoreApplied": 250,
"authorFullname": "t2_9k4mzq1x",
"subredditType": "public",
"over18": false,
"spoiler": false,
"domain": "reuters.com",
"outboundUrl": "https://www.reuters.com/technology/chipmaker-foundry-restructuring-2026-07-24/",
"numCrossposts": 37,
"linkFlairText": "Business",
"publishedAt": "2026-07-24T15:20:41Z",
"postUrl": "https://www.reddit.com/r/technology/comments/1n4qk2x/chipmaker_announces_4000_layoffs_as_it/",
"scrapedAt": "2026-07-25T09:14:02Z"
}

A self post from the same run, showing body populated and domain reading self.<subreddit>:

{
"post_id": "1n4t8vd",
"title": "My team was part of yesterday's layoffs โ€” here is what the severance package actually looked like",
"author": "throwaway_fab_2026",
"created_utc": 1753401277.0,
"num_comments": 596,
"score": 8104,
"permalink": "/r/technology/comments/1n4t8vd/my_team_was_part_of_yesterdays_layoffs_here_is/",
"image_url": "",
"thumbnail_url": "self",
"body": "Twelve weeks base, prorated bonus, health cover to the end of the quarter, and equity vesting stops on the termination date rather than the notice date. That last part is the one nobody reads until it matters.",
"comments": [],
"subreddit": "technology",
"success": true,
"matchedKeywords": ["layoffs"],
"matchedKeyword": "layoffs",
"keywordMode": "any",
"passedFilters": true,
"minScoreApplied": 250,
"authorFullname": "t2_1fq7ce3n",
"subredditType": "public",
"over18": false,
"spoiler": false,
"domain": "self.technology",
"outboundUrl": "https://www.reddit.com/r/technology/comments/1n4t8vd/my_team_was_part_of_yesterdays_layoffs_here_is/",
"numCrossposts": 2,
"publishedAt": "2026-07-24T23:14:37Z",
"postUrl": "https://www.reddit.com/r/technology/comments/1n4t8vd/my_team_was_part_of_yesterdays_layoffs_here_is/",
"scrapedAt": "2026-07-25T09:14:19Z"
}

Note what is missing from that second row: no linkFlairText, because the post has no flair. It is absent, not null.

How does it work?

Each line of startUrls is classified as a URL, a subreddit or a search phrase, then turned into an old.reddit.com address with your sort and time range attached. Requests go out over curl_cffi with Chrome's TLS fingerprint and a desktop header set, carrying an over18=1 age-confirmation cookie โ€” no browser is launched and no account is used. The returned HTML is parsed with lxml: subreddit listings are read from Reddit's own data-* post attributes, search pages from their result blocks.

Each parsed post is then tested locally against your keyword watchlist, score floor and NSFW switch, and only survivors go on to have their comment page fetched and their row written. The Actor follows Reddit's after= cursor page by page until it has enough matches, stopping after 40 listing pages per source โ€” an Actor-imposed safety cap โ€” or when Reddit stops offering a next page. Because fields are read from Reddit's structured post attributes rather than from styling classes, a Reddit front-end restyle does not change your key names.

Integrations

Reddit Posts Scraper is an Apify Actor, so it works with anything that can call the Apify API or consume a dataset โ€” your own code, scheduled runs, webhooks, or a no-code automation platform.

Calling Reddit Posts Scraper from Python

from apify_client import ApifyClient
client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("<YOUR_USERNAME>/reddit-posts-scraper-by-keyword-score-filter").call(run_input={
"startUrls": ["r/technology", "artificial intelligence"],
"filterKeywords": ["layoffs", "acquisition"],
"filterKeywordMode": "any",
"minScore": 250,
"maxPosts": 50,
"maxComments": 10,
"proxyConfiguration": {"useApifyProxy": True},
})
for row in client.dataset(run["defaultDatasetId"]).iterate_items():
if row.get("success"):
print(row["score"], row["subreddit"], row["matchedKeywords"], row["postUrl"])

Works in Go, Ruby, Node.js, cURL โ€” any language that can make an HTTP request. Filter on success to separate charged post rows from uncharged error rows.

No-code tools (n8n, Make, LangChain)

In n8n, use the Apify node โ€” or an HTTP Request node pointed at the Apify run endpoint with your token โ€” and pass the same JSON input shown above; an IF node on score or a Filter node on matchedKeywords routes hits to Slack or a database. In Make, the Apify module supports run-and-wait, so a daily keyword sweep can feed a Google Sheets or Airtable step directly. In LangChain, wrap the run endpoint as a tool and hand title plus body straight to the model, since both arrive as flat plain text. Apify schedules and webhooks handle recurrence and completion triggers, so a standing brand monitor needs no code of your own.

Scraping publicly accessible pages is broadly treated as permissible where no authentication is bypassed, and Reddit Posts Scraper reads only what a logged-out visitor sees โ€” no account, no login, no session. Reddit's terms of service still govern automated access to and reuse of its site, and the post and comment text you collect is user-generated writing its authors own.

This Actor returns personal data, and the pseudonymity does not exempt it. Reddit usernames are handles rather than real names, but under GDPR and UK GDPR a pseudonymous identifier that consistently picks out the same individual across records is still personal data โ€” and Reddit's identifiers are unusually stable. The fields concerned are author and authorFullname on every post row, and author inside every object in the comments tree, including nested replies. Post and comment text also routinely reveals political and religious beliefs, health conditions and sexuality, which are special categories under GDPR Article 9.

One factual point about scale: assembling many posts by the same author produces a behavioural profile โ€” where someone posts, when, and about what โ€” that is substantially more identifying than any single post read on its own.

Practical steps that follow from this:

  • Lawful basis. Decide yours before you store rows. Legitimate interest is the usual route for research and monitoring, and it needs a documented balancing assessment.
  • Minimisation. If you only need the text and the engagement, drop author, authorFullname and the author key inside comments at ingestion. Everything else โ€” title, body, score, subreddit, linkFlairText โ€” analyses fine without them.
  • Retention. Set a deletion date. Reddit users delete posts and accounts, and a dataset that outlives its source drifts out of accuracy as well as out of compliance.
  • Subject requests. Keep post_id and permalink so you can locate and delete a specific person's records when asked. Under CCPA the same rows are personal information about California residents.

Consult legal counsel if your use case involves bulk storage of personal data, or republication of collected post and comment text.

โ“ Frequently asked questions

What Reddit post fields does Reddit Posts Scraper return?

The five most used are title, body, score, author and comments. Every charged row also carries subreddit, permalink, postUrl, publishedAt, num_comments, linkFlairText, over18 and the filter-provenance fields matchedKeywords, keywordMode and minScoreApplied โ€” 30 keys in total, plus 5 more inside each nested comment object. See the data fields table above for the full list.

Does Reddit Posts Scraper require a Reddit account, login or API key?

No. There is no OAuth flow, no registered app, no client id and no password anywhere in the Actor. It reads old.reddit.com pages anonymously. The one cookie it sends is over18=1, the age confirmation any visitor clicks past to view NSFW content โ€” it carries no identity. The only credential involved is your Apify token, and only if you call the Actor through the API rather than the Console.

Are the keyword and score filters applied by Reddit, and am I charged for posts they reject?

They are applied by this Actor, after the page is fetched โ€” and no, rejected posts are never charged. The Actor requests a listing page, parses every post on it, then tests each one against filterKeywords, minScore and excludeNsfw in its own code. Posts that fail are dropped in memory: they are never written to the dataset, so no charged event fires for them. What a selective filter does cost is run time, because the Actor keeps requesting the next page until it has maxPosts matches or hits the page cap.

How many Reddit posts can I extract in one run?

maxPosts accepts 1 to 1000 and applies per source, counted after filtering โ€” twenty sources at maxPosts: 100 is a run that can write up to 2,000 rows. Those bounds are this Actor's own clamps. Whether you reach the number depends on the source: the Actor follows Reddit's after= cursor through at most 40 listing pages per source before stopping, and it also stops early when Reddit offers no next page. maxComments (0โ€“1000) is a separate per-post budget covering top-level comments only.

What happens if a subreddit returns no posts that pass the filters, or is private, quarantined or banned?

Both cases end the same way for your dataset: zero rows and zero charge for that source, with the detail in the run log rather than in the data.

If the subreddit loads but nothing clears your filters, the Actor pages through it, logs 0 posts matched filters for source: <name> and moves on. If the subreddit is private, quarantined or banned, Reddit returns a page containing no post containers โ€” the Actor treats that as a block, climbs the proxy ladder, retries up to four times, then logs No HTML returned and moves on. Neither case writes an error row, because no post was ever collected to write one about. If the whole run ends with nothing, the log says 0 posts pushed โ€” check filters, source validity, or proxy availability. In code, treat an unreachable source as an absence of rows and check the log, not the dataset.

Can I scrape multiple subreddits and search terms at once?

Yes. startUrls is a list and mixes types freely โ€” subreddit names, r/ names, full Reddit URLs and search phrases in the same run, processed one source after another. Identical lines are merged before the run starts. One caveat on duplicates: posts are deduplicated within a source, not across sources. The Actor tracks post ids per source, so if r/technology and the search phrase chip layoffs both surface the same post, it is written and charged twice. Deduplicate on post_id after the run if that matters.

Does Reddit Posts Scraper work with Claude, ChatGPT and other AI agent tools?

Yes. It is callable as a standard HTTP-triggered run through the Apify API, so LangChain, CrewAI, n8n or a hand-written tool definition can invoke it and receive typed JSON with no parsing step. Give the agent title, body and comments as the content and score, subreddit and publishedAt as the metadata it can reason over.

How does Reddit Posts Scraper compare to other Reddit post scrapers?

Checked on the Apify Store on 25 July 2026: mysteriousshadow/detailed-reddit-posts-scraper-with-flair-filtering documents flair-based filtering over a single subreddit URL with automated scrolling, and its documented example output uses hyphenated keys (number-of-upvotes, number-of-comments, post-link) with numeric values as strings; it documents no keyword filter, no score filter and no comment bodies. maged120/reddit-subreddit documents a subreddits array with sort, time and maxPosts, an output including upvoteRatio and isSelf, and states plainly that no Reddit API key is needed; it documents no keyword or score filtering and no comment text. scrapers_lat/reddit-posts-scraper documents three input modes โ€” subreddit feeds, search query and post URLs โ€” 26 fields per post including gallery images and awards, a global maxItems budget, and that a post which fails to load is written as a record with an error field.

What this Actor documents explicitly against that field: a keyword watchlist with four match modes plus a score floor and NSFW exclusion, all applied before any row is written or charged; nested comment trees with replies; per-row filter provenance (matchedKeywords, keywordMode, minScoreApplied); and uncharged error rows marked by success: false. It is equally explicit about what it does not return โ€” no upvote ratio, no awards, no gallery image arrays.

Does Reddit Posts Scraper return data in a format LLMs can use directly?

Yes. Typed, normalized JSON with consistent key names across runs, post bodies and comment bodies already flattened from Reddit's markup to plain text, and counters already coerced to integers. No HTML parsing, no selectors. Pass a row directly into an LLM, index it into a vector store, or hand it to an agent tool โ€” just remember that keys with no value are omitted rather than set to null.

What happens when Reddit changes its layout or anti-bot system?

The scraper is maintained, and the output schema is designed to survive front-end change: posts are read from Reddit's own data-* attributes on the listing markup rather than from styling classes, so a restyle degrades at most one field instead of breaking the row. Every field extractor also has a fallback โ€” a missing title becomes "No Title", a missing author becomes "Unknown", a missing flair is simply left off โ€” so a partial page still produces a usable row. Your field names and types stay the same on your end.

Can I use Reddit Posts Scraper without managing proxies or browser infrastructure?

Yes. The TLS impersonation, the retries and the proxy ladder are all handled for you โ€” direct, then Apify datacenter, then Apify residential with three extra IP rotations, sticky once escalated. You never create a proxy account, rotate an IP or run a browser. Leaving proxyConfiguration at its prefilled {"useApifyProxy": false} still gets you the full ladder; it only decides which rung the run starts on. For large runs, starting on Apify residential is the recommended setting. The Actor does not solve CAPTCHAs, and it makes no claim to.

Which Reddit fields work best for AI training data and RAG indexing?

For RAG indexing: title and body carry the most information per record and chunk cleanly, with the body text inside each comments object adding the community's response to the same topic; use subreddit, linkFlairText and matchedKeywords as metadata filters and postUrl as the citation. For training data: score, num_comments, numCrossposts, over18, spoiler and publishedAt are the most structurally consistent fields across records. All values return as typed primitives or plain arrays, so no normalization pass is needed โ€” just account for body being "" on link posts and on every search-sourced row, and for score being a fuzzed, point-in-time number rather than a fixed property of the post.

Scraper NameWhat it extracts
Reddit Trends Scraper By Sort, Time & Score FilterTrending Reddit posts by sort mode, time window and score
Reddit User Profile Posts And Comments Scraper With Account AgeA Reddit user's posts and comments with account age and karma stats
Quora Search ScraperQuora questions, answers, upvotes and author credentials
Google News Scraper By Publisher & Site FilterNews articles filtered by publisher and site
X (Twitter) Posts SearchPublic posts matching a search query
Threads User Posts Scraper By Keyword & Date FilterThreads posts for a user, filtered by keyword and date

๐Ÿ’ฌ Your feedback

Found a bug, or need a field that is on the Reddit page but not in the output? Open an issue on the Actor's Issues tab. Reports that include the exact input JSON and the subreddit or search phrase you ran are the fastest to reproduce and fix.