Reddit Scraper - Posts, Comments, Users & Keywords, No Login avatar

Reddit Scraper - Posts, Comments, Users & Keywords, No Login

Pricing

Pay per event

Go to Apify Store
Reddit Scraper - Posts, Comments, Users & Keywords, No Login

Reddit Scraper - Posts, Comments, Users & Keywords, No Login

Reddit scraper that works without login: posts, comments, full user history and keyword search from subreddit, user, search and domain feeds, plus user and subreddit profiles, extracted indicators (CVE, IP, hash, wallet, domain), related subreddits and a rate-limit report.

Pricing

Pay per event

Rating

0.0

(0)

Developer

daehwan kim

daehwan kim

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

10 days ago

Last modified

Share

Give it subreddits, usernames, keywords, domains or post links and get back the posts and comments — plus a profile of each user and subreddit, the indicators inside the text (CVE, IP, hash, wallet, domain, u/ and r/ mentions), keyword result counts, related subreddits, optional rule-based signals (intent, sentiment, your own labels) and a report of what Reddit's limits let the run read. No login and no cookies: it reads Reddit's public feeds. On paid plans you can add your own Reddit API key for scores and comment trees (optional, beta).

Free plan (preview)Paid Apify plan (Starter and up)
Sources per run (subreddits, users, keywords, domains and post URLs together)2500
Post and comment rows per sourcethe 10 newestas far as Reddit lists them (1,000 per listing, about 250 per search) — and everything after that with monitoring
Textfirst 280 characters (textTruncated: true); links from the shown text onlyfull
More items, date and start point (maxItemsPerSource above 10, since, after)lockedyes
New items only (incremental)lockedyes
Posts and comment stream of a subredditone listing per subreddit: its posts (its comments when include leaves posts out), 10 newestboth
Comments of one post (postUrls)the post and its 9 newest commentsabout 500 per request; a post that reaches that is read again under 5 more sort orders and merged (measured: 497 → 1,700 and 492 → 1,288 comments)
Posts of a user whose own address lists nothingthe fact, and how many posts Reddit search liststhose posts, through Reddit search (posts only)
Your own Reddit API key (scores, comment counts, comment tree)lockedyes (optional, beta)
User and subreddit profile rowssummary numbers; the top 3 subreddits + how many moreeverything: every subreddit with its share, active hours, top authors, domains and terms
Indicator rows1 of each kind + how many more were foundall
Related subreddit rows3 + how many more were foundall
Keywords1all
Signals (intent, sentiment, sales flags, your own labels — rule-based, no LLM)on the 10 rows shown; 1 custom labelevery row; every custom label
AI layer (a language model: analysis, your own questions, translation, summaries)not available — a free run calls no modelevery layer you switch on, up to aiMaxItems posts and comments per run
Limits report (limits and source rows)completecomplete
Price per eventlist pricecollection rows and rule-based signals: BRONZE −20%, SILVER −35%, GOLD and up −48%; AI events: the same price on every plan

How big is the difference? Measured on 2026-10-07: for r/cybersecurity a free run returns 10 posts, a paid run 593 posts for the last 30 days (59×) plus 494 comments per day. For u/kn0thing: 10 items and 3 subreddits against 940 posts, 973 comments and the 286 subreddits he is active in (191×). For the keyword data breach: 10 results against 242 results from 185 subreddits (24×).

A free run never fails because of a limit: it reads the first page of each listing (up to 100 items), shows the 10 newest, tells you in the dataset what stayed locked as counted numbers (1 of 19 shown), and ignores paid-only inputs with a notice instead of an error.

Unofficial. This Actor is not affiliated with, endorsed by, or sponsored by Reddit. It reads public feeds only, without login. You are responsible for how you use the input and the results, for complying with Reddit's terms, and with GDPR, PIPA, CCPA and other applicable laws.

Why use this Reddit scraper?

  • It keeps working without an account. Every request goes out on its own proxy IP, because Reddit allows one request per IP per minute on its public feeds. Nothing to log in, nothing to get banned. In the measurement behind this Actor (126 requests, 2026-10-07) 117 of 118 requests to open sources succeeded; the other 8 were two users whose feeds answer 403.
  • Empty is explained. Every source gets a source row: did Reddit answer, how many items were read, how far back, what stopped the run, did a listing reach Reddit's own limit. A hidden profile or a missing subreddit is an answer, not a failed run.
  • A user as one row. userProfile: posts and comments counted, first and last activity, every subreddit with its share of the activity, active hours (UTC), most linked domains, most used terms — counted from the items the run read, with the count stated.
  • A subreddit as one row. subredditProfile: posts in the last 7 and 30 days, authors, comments per day, top authors, top domains, and the ranked posts when you read a ranked listing.
  • Keyword answers, including "nothing found". One keywordSummary row per keyword and source: results, last 7 days, first and last, which subreddits, 5 sample posts. Rows carry matchedKeywords and matchedIn (title, body, url); strictMatch drops search results that only matched somewhere the feed does not show.
  • Indicators as rows, not as a blob. CVE IDs, IPv4 addresses, MD5/SHA-1/SHA-256 hashes, BTC/ETH/TRON wallet addresses, emails, phone numbers, domains, mentioned users (u/…) and mentioned subreddits (r/…), deduplicated per source with how often and where each was seen.
  • Related subreddits with the reason. Where a user is active, which subreddits the items mention, and what Reddit's subreddit search returns for the topic.
  • Signals without a model. Switch on signals and every post and comment row gets an intent (question, seeking_recommendation, complaint, praise, purchase_intent, comparison, self_promotion, discussion), a sentiment label and score, flags (wantsHelp, mentionsPrice, mentionsCompetitor, hasQuestion) and your own labels — rule-based, no LLM: word lists and patterns counted inside the Actor, with the words that fired each rule in signalEvidence.
  • AI layer, when you want one. Optional and separate: with ai set, a language model reads the posts and comments (any language) and adds an analysis, answers to your own questions, an English translation and written summaries whose every statement cites the posts it rests on. Paid plans; see "AI layer" for what is sent where.
  • Long threads, further than one request. One request returns about 500 comments of a post. A post that reaches that is read again under the other sort orders and merged; every comment is delivered and charged once.
  • Honest about the fields. The public feed has no scores, comment counts or comment depth. Those columns are null and every row lists them in fieldAvailability; with your own Reddit API key (paid plans, beta) they are filled — see "Add your own Reddit API key".

Built for OSINT and threat-intelligence work, brand and leak monitoring, and community research. Runs on the Apify platform with API access, scheduling, integrations and monitoring built in.

How to use it

  1. Fill in at least one of subreddits (netsec, r/netsec, netsec+osint), users (spez, u/spez), keywords, domains or post URLs.
  2. Optionally choose the listing (sort, time), how many items to read per listing, filter keywords and the indicator kinds you care about.
  3. Click Start and read the rows in the Output tab. Filter by recordType.
  4. For monitoring turn on New items only with a monitor key and put the Actor on a schedule.

Input

FieldTypeDescription
subredditsarrayName, r/name or link; a+b reads several as one listing
usersarrayName, u/name or profile link
keywordsarrayReddit-wide searches; operators subreddit:, title:, author:, url: work
domainsarrayPosts that link to this domain (viaDomain on the rows)
postUrlsarrayOne post and the comments its feed carries
includearrayposts, comments, userProfile, subredditProfile, keywordSummary, indicators, relatedSubreddits (all when omitted)
sort / timestringnew, hot, top, rising, controversial / hour … all
maxItemsPerSourceintegerItems to read per listing, newest first, 1–1,000. When omitted: 40 (above 10: paid plans)
maxCommentsPerPost + deepCommentsinteger + booleanPost URLs: items to read from one post, 1–3,000 (default 500 — about what one request carries; raise it to get more); above 500, a post whose first request returned 480 or more comments is read again under other sort orders unless deepComments is false. Each comment is one comment event (paid plans)
since / afterdate / stringStop at older items / start after an item id (paid plans)
filterKeywords + onlyMatchingarray + booleanMatch the items read; optionally deliver only the matching ones
strictMatchbooleanKeyword searches: deliver only results that contain every word
indicatorKindsarraycve, ipv4, hash, wallet, email, phone, domain, mentioned_user, mentioned_subreddit (all when omitted)
incremental + monitorKeyboolean + stringOnly items newer than the last run with the same key; any text works as a key and each distinct name keeps its own position (paid plans)
outputFormatstringfull, minimal or rag
signalsbooleanAdd rule-based signals to every post and comment row (see "Signals")
competitorNamesarrayNames that set flags.mentionsCompetitor when signals are on
customLabelsarrayYour own labels: [{ "label": "pricing", "anyOf": ["too expensive", "price increase*", "$#/mo"] }] — words and phrases, with * and # as wildcards; no regular expressions (free plan: the first label)
aiarrayAI layers to switch on: analysis, labels, translation, threadSummary, userBrief, subredditDigest, keywordInsight (paid plans; empty = no text is sent to any model) — see "AI layer"
aiLabelsarrayYour own questions for the labels layer: [{ "label": "churn_risk", "question": "Does the author say they will stop using a product?" }], optionally with "options": [...]
aiMaxItemsintegerThe most posts and comments one run sends to the model (1–20,000, default 2,000)
redditClientId + redditClientSecret + redditUserAgentsecret strings + stringYour own Reddit API app key and User-Agent (paid plans, optional, beta) — see "Add your own Reddit API key"
apiRequestsPerMinute / apiMaxRequestsintegerPace (1–100, default 80) and cap for requests sent with your key
proxyConfigurationobjectApify Proxy; keep it on
{
"subreddits": ["netsec"],
"users": ["spez"],
"keywords": ["CVE-2026"],
"domains": ["bleepingcomputer.com"],
"maxItemsPerSource": 150
}

maxItemsPerSource counts the items the run reads from each listing (a subreddit and a user have two: posts and comments); filterKeywords with onlyMatching, strictMatch and since then decide which of those become rows. With include set to profiles or indicators only, the items are read and counted but no post or comment row is delivered or charged.

Output

Every row has a recordType: post, comment, source, userProfile, subredditProfile, keywordSummary, indicator, relatedSubreddit or limits — and threadSummary when that AI layer is on. With signals on, post and comment rows also carry a signals object; with an AI layer on, an ai object. Rows that carry a noticeCode instead are messages, not results (free-plan-preview, free-plan-locked, charge-limit-reached, charge-unavailable, lookup-failed, upstream-blocked, run-summary, invalid-input, no-input, ledger-unreadable, ledger-unwritable, delivery-unconfirmed, restart-unconfirmed) — they are never charged; skip them when parsing. Source rows, the limits row and notices carry the responsibility line in disclaimer.

{
"recordType": "post",
"platform": "reddit",
"accessLayer": "rss",
"sourceKind": "subreddit",
"sourceName": "r/netsec",
"postId": "1wzpmfz",
"fullname": "t3_1wzpmfz",
"postTitle": "One Copilot model refused. Another leaked secrets about half the time.",
"postUrl": "https://www.reddit.com/r/netsec/comments/1wzpmfz/one_copilot_model_refused_another_leaked_secrets/",
"subreddit": "netsec",
"author": "Haunting_Ganache_850",
"postedAt": "2026-10-07T06:45:54.000Z",
"text": "This one gets stranger the more you look at it.…",
"textTruncated": false,
"externalUrl": "https://adversa.ai/blog/cryptographic-context-injection-github-copilot/",
"externalDomain": "adversa.ai",
"links": ["https://adversa.ai/blog/cryptographic-context-injection-github-copilot/"],
"matchedKeywords": [],
"matchedIn": [],
"rankInFeed": null,
"score": null,
"commentCount": null,
"fieldAvailability": {
"accessLayer": "rss",
"unavailable": ["score", "upvoteRatio", "commentCount", "flair", "isStickied", "isLocked", "over18"],
"reason": "Reddit's public feed does not carry these fields; they need Reddit API access with an API key."
}
}
{
"recordType": "userProfile",
"username": "spez",
"postCount": 23,
"commentCount": 150,
"historyComplete": false,
"firstActivityAt": "2015-07-11T17:02:59.000Z",
"lastActivityAt": "2026-08-05T18:31:50.000Z",
"subredditCount": 15,
"subreddits": [{ "subreddit": "RDDT", "posts": 0, "comments": 47, "items": 47, "share": 0.272 }],
"activeHoursUtc": [15, 3, 1, 3, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 4, 18, 11, 19, 16, 11, 22, 25, 18],
"countBasis": "counted in the 173 items this run read (23 posts, 150 comments)"
}
{
"recordType": "limits",
"layers": [
{
"accessLayer": "rss",
"requests": 12,
"http429": 0,
"retriedOnNewIp": 0,
"failedRequests": 0,
"rateLimit": { "used": 1, "remaining": 0, "resetSeconds": 36, "responsesWithHeaders": 12 },
"perIpWindow": { "requestsPerWindow": 1, "secondsUntilReset": 36 }
}
],
"caps": [],
"advice": "This run made 12 requests to Reddit's public feeds, each on its own proxy IP; 0 were answered 429 and 0 were retried on a new IP. Reddit's headers allowed 1 request(s) per IP per window (0 left, reset in 36 s), which is why no IP is used twice."
}

A run also writes RUN-SUMMARY to its key-value store: rows per kind, per-source numbers, requests and the listings that reached a limit.

You can download the dataset in various formats such as JSON, HTML, CSV, or Excel.

Data table

RowKey fields
postpostId, fullname, postTitle, postUrl, subreddit, author, postedAt, text, textTruncated, markdown, externalUrl, externalDomain, isSelfPost, thumbnailUrl, links, linkCount, mentionedUsers, mentionedSubreddits, feedSort, rankInFeed, viaDomain, matchedKeywords, matchedIn, signals (when switched on); null on the public feed and filled with your own API key: score, upvoteRatio, commentCount, flair, isStickied, isLocked, over18
commentcommentId, fullname, commentUrl, postId, postTitle, postUrl, subreddit, author, postedAt, text, textTruncated, markdown, links, linkCount, matchedKeywords, matchedIn, isSubmitter (post URL sources), signals (when switched on); null on the public feed and filled with your own API key: score, parentId, depth
sourcesourceKind, sourceName, status (ok, empty, forbidden, not_found, partial, blocked), statusNote, itemsRead, postsDelivered, commentsDelivered, duplicatesSkipped, newestItemAt, oldestReachedAt, stopReason, capReached, collectionError, feeds (per listing: address, HTTP status, requests, items, stop reason, capLimit, capReached, capAdvice, incrementalMode, incrementalNote, savedPosition, accessLayers, and for a post URL thread: comments received, sort orders used, whether one request came back full), limits; for users profileVisibility (public, hidden-or-empty, unavailable), recoveredVia, recoverablePosts; with a key apiLayer
userProfileusername, postCount, commentCount, itemsCounted, historyComplete, firstActivityAt, lastActivityAt, subredditCount, subreddits (posts, comments, share), activeHoursUtc, topDomains, topTerms, countBasis, profileVisibility, recoveredVia, recoveredNote, signalSummary (signals on); with your API key karma, postKarma, commentKarma, accountCreatedAt
subredditProfilesubreddit, postsCounted, commentsCounted, postsLast7Days, postsLast30Days, uniqueAuthors, commentsPerDay, daysCovered, topAuthors, topDomains, topTerms, rankedPosts, countBasis, signalSummary (signals on); with your API key subscriberCount, activeUserCount
keywordSummarykeyword, keywordOrigin, itemsScanned, matchCount, strictMatchCount, matchesLast7Days, firstMatchAt, lastMatchAt, subredditCount, subredditBreakdown, samplePosts (5 newest), signalSummary (signals on)
indicatorindicatorKind, value, occurrences, firstSeenAt, lastSeenAt, itemUrls (up to 5), hashAlgorithm (hashes), walletNetwork (wallets)
relatedSubredditrelatedSubreddit, relatedUrl, relationKinds (user_active, mentioned, topic_search), relationBasis, occurrences, shareOfActivity, searchRank, relatedTitle, relatedAbout, relatedCreatedAt, itemUrls
threadSummary (AI layer)postId, postUrl, postTitle, subreddit, summary, themes, issues, viewsFor, viewsAgainst, solutions, productsMentioned (each a statement { text, evidenceUrls } or a list of them), itemsRead, commentsRead, generatedBy, countBasis
ai on other rows (AI layer)post and comment rows: analyzed, language, sentiment, intent, emotion, flags, category, labels, translation, note; userProfile, subredditProfile and keywordSummary rows: summarized, summary and the lists of that summary, itemsRead, note
limitslayers (per access layer: requests, 429s, retries, Reddit's rate-limit headers; for your API key also the pace, rests and whether it stopped early), caps (listings that reached Reddit's limit, with advice), threads (per post URL: comments received, sort orders used; the post's total comment count is known only with an API key), layerSwitch, unconfirmedCharges (charge calls that failed; their rows were delivered and are not charged again), rowsDeliveryUnconfirmed (charged rows whose write failed and could not be checked), rowsUnconfirmedAfterRestart (rows a restarted run found under way — not charged or written again), ledgerUnwritable, runWideDedupe (whether an item reached through two sources was still handed over once — see "Restarts and repeat charges"), advice

All rows also carry recordType, platform, accessLayer (rss for the public feed, api for your own key), scrapedAt and sourceUrl. Every number in a profile or summary row is counted from items the run really read; nothing is estimated.

Run summary. At the end of a run the default key-value store holds a RUN-SUMMARY record (JSON, not charged): rows per kind and per source, the period covered, requests per access layer with Reddit's rate-limit values, sources that reached a limit, the signal distribution, the AI layer's counts (calls, tokens, items analysed, why it stopped) and the number of charges per event. It holds counts and dates only — no input values.

Signals (rule-based, no LLM)

With signals: true every post and comment row carries:

"signals": {
"evaluated": true,
"method": "rules",
"intent": "seeking_recommendation",
"sentimentLabel": "negative",
"sentimentScore": -0.5,
"flags": { "wantsHelp": true, "mentionsPrice": true, "mentionsCompetitor": true, "hasQuestion": true },
"competitorsMentioned": ["Splunk"],
"labels": { "pricing": true, "churn": false },
"signalEvidence": [
{ "rule": "label:pricing", "matched": "too expensive" },
{ "rule": "intent:seeking_recommendation:looking-for", "matched": "looking for an" },
{ "rule": "sentiment:negative", "matched": "expensive" }
],
"note": null
}
  • How it is computed. Word lists and patterns, counted inside the Actor. No model is called and no text leaves the run. signalEvidence names the rule and the words that fired, so you can check every verdict.
  • Intent is one of question, seeking_recommendation, complaint, praise, purchase_intent, comparison, self_promotion, discussion. When several fire, the order is self_promotion, purchase_intent, seeking_recommendation, comparison, complaint, praise, question.
  • Sentiment is positive, negative, mixed, neutral or uncertain, with a score from −1 to 1 (positive words minus negative words over both; a negation right before a word flips it).
  • Quoted and negated text. Quoted lines (>), quoted spans and links are left out of intent and sentiment; "not looking for" does not count as looking for. Your labels and competitor names are searched in the whole text — a mention is a mention.
  • English rules. The script is checked whatever the length of the text. A text that does not look English — or that has too few letters to tell (emoji, digits or punctuation only) — gets evaluated: false, intent: "discussion" and sentimentLabel: "uncertain", and no signals-evaluated charge; your labels, competitor names, prices and question marks are still checked and filled in, and note says so. The optional AI layer reads any language.
  • Not evaluated, not charged. Deleted, removed or empty text, a bot's standard message and a text in a language the rules do not cover get evaluated: false with sentimentLabel: "uncertain" and no signals-evaluated charge.
  • Your labels. Each anyOf entry is a word or a phrase, matched whole (not inside a longer word) and case-insensitively. Two wildcards: * stands for any letters or digits inside one word (vuln* finds "vulnerability", *ware finds "malware"), # for one or more digits (CVE-#-# finds "CVE-2026-12345"); a phrase such as price increase has to stand on consecutive words. Regular expressions are not accepted: an entry written as /…/ is refused and reported in an invalid-input notice row, and the label's other entries still apply. Limits: 50 labels (free plan: 1), 20 entries per label, 80 characters per entry; labels read the first 5,000 characters of a text and skip any single word longer than 80 characters (a long link, a hash). The matching work per item is bounded; a set of labels built to be slow leaves the remaining labels of that item unchecked (false), and the row's note says how many.
  • Summaries. Profile and keyword rows carry signalSummary (counts and shares per intent and sentiment, counts per flag and label) over the rows of that source that carry signals.
  • These are heuristics. They are right often enough to sort and filter thousands of rows, and wrong often enough that a single verdict should be read with its evidence.

AI layer (optional, paid plans)

AI analysis is a separate, optional layer on top of the rule-based signals. Nothing in it runs unless you put a layer name in ai. You can run the signals, the AI layer, or both: they fill different fields (signals and ai) and are charged separately.

What is sent where. When an AI layer is on, the text of the public posts and comments this run reads (title and body, cut to 2,000 characters per item) is sent to DeepSeek's API — DeepSeek is a company based in China — and the answer comes back to the run. Author names are not sent. Every request of this layer goes to that one address, https://api.deepseek.com — no input changes it. If you do not want text sent to DeepSeek, do not switch an AI layer on: leave ai empty and no text leaves the run (the rule-based signals layer sends nothing anywhere).

Layer in aiWhat you getWhere
analysissentiment (positive, negative, mixed, neutral), intent (the same eight values as the signals), emotion, flags (wantsHelp, purchaseIntent, mentionsPrice, isVendor, hasQuestion), category, languageai on post and comment rows
labelsthe answer to each of your own questions in aiLabels — true/false, or one of your options; comes on top of analysis, which it switches onai.labels.<label> on post and comment rows
translationan English translation of items that are not in English — the first 800 characters of each item's text are translatedai.translation on post and comment rows
threadSummarythe answers, experiences or themes that come up most, the points at issue and views for and against (when the post is a debate), recurring solutions, products and companies namedone threadSummary row per post URL
userBriefinterests, topics, apparent expertise, toneai on the userProfile row
subredditDigestmain themes, recurring complaints, frequent questionsai on the subredditProfile row
keywordInsightthe contexts a keyword is used inai on keywordSummary rows
  • What is rule-based and what is a model. signals, signalEvidence and signalSummary are counted by rules inside the Actor and call no model. Everything under ai, and the threadSummary row, is written by a language model.

  • Labels on top of the analysis. AI labels come on top of the item analysis: labels switches analysis on for the same posts and comments, also when analysis is not in ai, and each item is charged ai-analyzed ($0.0005) plus ai-label ($0.0001) per label — with 3 questions, $0.0008 per item. One request to the model does both; reading the item's text is what the analysis event pays for. Each part is charged only when its own part of the answer passed the checks. A run that picked labels without analysis says so in limits.ai.note. With labels picked and no usable question in aiLabels, neither runs.

  • Any language. The rules behind signals are English only; the model reads other languages too. That is one reason to switch analysis on for a non-English subreddit.

  • Charged after the answer, and only for what passed. A post or comment is charged its AI event once the model has answered and the answer passed the checks: the expected fields, values from the lists above, and the id of an item that was sent. An answer that is empty, malformed or outside the lists is dropped — nothing is shown and nothing is charged for it, and ai.note says why. Rule-based signals on the same row are not affected.

  • Summaries cite their sources. Every statement of a summary carries evidenceUrls, and only addresses of posts and comments this run sent to the model are accepted; a statement that cites anything else is dropped, and a summary left without a cited summary sentence is not delivered or charged. A summary is written from at most 100 items (1,000 characters each) and needs at least 3. It holds at most 5 statements in each list and 3 addresses per statement. One summary is at most 2 requests to the model: when the first answer is empty the same question is asked once more, and when it was cut off at the model's output limit a shorter answer is asked for. The second answer is the last — if it is empty, cut off or does not pass the checks, the summary is not delivered or charged. A failed request (an HTTP error, no answer in time) is not asked again. The 2 requests are counted for the whole run, restarts included (see "Restarts" below). The run keeps that count for 2,000 summaries; a summary past that number is not asked for, delivered or charged (summary-uncounted) — only a run with many keyword rows gets there.

  • Text is treated as data. Posts are written by strangers, and some contain instructions aimed at language models. The text is passed as data, the model is told not to follow anything inside it, and whatever comes back is read through the fixed lists above — a value outside them never reaches a row.

  • Limits. aiMaxItems (default 2,000, at most 20,000) caps the posts and comments one run sends. aiLabels takes up to 10 questions, each cut at 200 characters, with up to 8 options of 40 characters. The run also stops using the AI layer — and goes on without it — after 5 failed model calls in a row, when the model refuses the key or the balance is used up, when the run's own ceiling on model spend is reached, or when your maximum charge per run is reached. The ai entry of the limits row gives the calls, tokens, items sent and analysed, answers dropped, requests that were not made (requestsNotMadeLedgerSave, requestsNotMadeSpendHeld) and the reason it stopped. A row whose summary was not written says why at the end of its ai.note.

  • Why an answer was not used. limits.ai.discardReasons (also in RUN-SUMMARY) counts, per code, how often an answer of the model — or a part of one — was thrown away. Whole answers count per request (an answer asked a second time counts twice), summary-* per summary, item-* per post or comment. Only the code is recorded, never the answer itself. None of these is charged.

    CodeWhat happened
    no-answerno answer came back from the model (a timeout or a network error)
    http-errorthe model's API answered with an HTTP error
    empty-answerthe model's answer was empty
    cut-offthe model's answer was cut off at the output limit
    not-jsonthe model's answer was not the JSON that was asked for
    key-in-answerthe model's answer held a secret and was thrown away unread
    summary-missingthe model's answer had no summary sentence
    summary-uncitedthe model's summary sentence cited no post or comment of this run
    summary-aloneno statement besides the summary sentence cited a post or comment of this run
    summary-attempts-usedthe requests this summary may make were already used (a summary is asked at most twice in a run, restarts included)
    summary-uncountedthe run's record of summary requests was full, so the model was not asked for this summary
    item-missingthe model's answer had no entry for this item
    item-id-mismatchan entry of the model's answer had an id that was not asked, or one given twice
    item-invalid-valuesan entry of the model's answer held values outside the allowed lists
  • The ceiling on model spend. It is our cost, not yours — you pay the AI events, never the model's bill. Once the spend known to the run reaches the ceiling, no new request is made. Requests already out are not taken back, and what they really cost can be above the estimate held for them. Far from the ceiling up to 4 requests are out at once; once fewer than 4 large requests fit in what is left of it (about the last $0.08 of the $5), a request goes out only when no other is out. So the spend can end above the ceiling by what one request cost above what was left — the ceiling plus the excess of one request, not of four. Requests that were out together far from the ceiling pass it only if they cost, on average, more than is held for the largest request (about $0.019 each). The first request of a run goes out alone; when an answer costs more than was held for it (limits.ai.estimateExceeded), requests go out one at a time for the rest of the run.

  • Restarts. What the layer sent and spent is saved in the run's ledger before each request, so a run that Apify moves to another server counts on from there: aiMaxItems and the spend ceiling hold for the whole run, not per restart. The posts and comments of a request that was out at the moment the run was moved (at most 20 per request) count as sent and are not sent a second time: after the restart their rows go out with ai.analyzed: false, a note in ai.note and no AI event charged, and limits.ai.itemsNotResent counts them. Items whose answer had already come back but whose rows were not yet handed over are sent again after the restart — you are charged once per item either way. A summary's 2 requests are counted in the ledger before each one goes out, so they hold across a restart: a summary whose first request was out when the run was told to stop, or whose first answer was empty or cut off and whose second request had not gone out, is asked once more (with the shorter question), and one whose second request was already made, or whose answer had come back before the run was told to stop — a summary whose row was not yet handed over, or an answer that was thrown away — is not asked again: it is not delivered and not charged, the row it would ride on says so in ai.note, and limits.ai.discardReasons counts it as summary-attempts-used. That an answer came back is saved in the ledger before the summary's row goes out; if that one save fails the run goes on, and after a restart that summary may be asked for once more — still at most 2 requests in all. A restarted run that cannot read its ledger ends without collecting (see "Restarts and repeat charges"). A first start that cannot read it at first collects once its store answers again (a row is charged only after it is saved), but calls no model for that run (limits.ai.stopped: "ledger-unreadable"): rows go out without ai verdicts and without AI charges.

  • No second pass. A post or comment that went out without an AI verdict — a limit was reached, an answer was dropped, a restart — is not analysed in a later run: with incremental on, the next run starts after it.

  • Same price on every plan. The seven AI events have no plan tier discount: BRONZE, SILVER, GOLD and higher plans pay the list price for them. The discount applies to the collection rows and to rule-based signals.

  • Free plan. A free run calls no model: ai is ignored and a free-plan-locked row says so.

  • Not measured yet. How often the model's verdicts agree with a person's has not been measured for this Actor. Use them to sort and filter, and read the text before acting on a single one.

  • Your responsibility. You decide whether this text may be sent to a model provider and what the output is used for; check Reddit's terms on using its content with language models before you do.

Pricing

Pay per event. The compute, proxy and storage a run uses are included in the event prices; Apify platform usage is not billed on top of them.

EventCharged whenPrice (list)Paid plan tiersNot charged
apify-actor-startthe run starts — once per run (memory is limited to 1 GB)$0.003same on every plan—
source-checkedone source's lookup is completed — including the answer that a profile is hidden or a subreddit does not exist$0.003discountedour errors (timeouts, failed retries, parser errors, an answer that is not a feed) and sources cut short by them; the same source entered twice; sources Reddit refused before anything could be read
postone post row is written$0.002discountedduplicates (a post reached through two sources of one run is delivered once); notice rows
commentone comment row is written$0.002discountedduplicates; notice rows
user-profiledone user profile row is written$0.02discountedprofiles cut short by our error; hidden or missing profiles
subreddit-profiledone subreddit profile row is written$0.01discountedprofiles cut short by our error
keyword-checkedone keyword × source result row is written, including zero matches$0.004discountedsources that failed on our side
indicatorone indicator row, deduplicated per source, is written$0.002discountedduplicates; notice rows
related-subredditone related subreddit row is written$0.002discountedduplicates; notice rows
signals-evaluatedsignals is on and the signals of one post or comment row were evaluated — on top of that row's own event$0.0005discountedruns with signals off; deleted, removed or empty text; a bot's standard message; items in a language the rules do not cover (English only) or with too few letters to tell; duplicates; notice rows
ai-analyzedanalysis is on — picked, or switched on by labels — and a model's analysis of one post or comment was written to its ai field — on top of that row's own event$0.0005same on every planfree plan runs (no model is called); answers that were missing or did not pass the checks; failed model calls; English items (translation); duplicates; notice rows
ai-labellabels is on and one of your questions was answered for one post or comment — per label and item (all the labels of a row in one charge), on top of that row's own event and of its ai-analyzed$0.0001same on every planfree plan runs (no model is called); answers that were missing or did not pass the checks; failed model calls; English items (translation); duplicates; notice rows
translatedtranslation is on and the English translation of one non-English post or comment was written to its ai.translation (the first 800 characters of its text)$0.002same on every planfree plan runs (no model is called); answers that were missing or did not pass the checks; failed model calls; English items (translation); duplicates; notice rows
thread-summarizedthreadSummary is on and one threadSummary row is written$0.02same on every planfree plan runs (no model is called); answers that were missing or did not pass the checks; failed model calls; English items (translation); duplicates; notice rows
user-briefeduserBrief is on and a brief was written to a user profile row — on top of that row's own event$0.03same on every planfree plan runs (no model is called); answers that were missing or did not pass the checks; failed model calls; English items (translation); duplicates; notice rows
subreddit-digestedsubredditDigest is on and a digest was written to a subreddit profile row — on top of that row's own event$0.05same on every planfree plan runs (no model is called); answers that were missing or did not pass the checks; failed model calls; English items (translation); duplicates; notice rows
keyword-insightkeywordInsight is on and an insight was written to a keyword result row — on top of that row's own event$0.02same on every planfree plan runs (no model is called); answers that were missing or did not pass the checks; failed model calls; English items (translation); duplicates; notice rows

apify-actor-start: Charged once when a run starts. This Actor's memory is limited to 1 GB, so that is one charge per run (the platform adds one more charge for every 1 GB of memory above 1 GB). It costs $0.003. The start fee is the same on every plan tier.

Paid plan tiers pay less per event for collection rows and rule-based signals (source-checked to signals-evaluated in the table): BRONZE −20%, SILVER −35%, GOLD and up −48%. The seven AI events cost the same price on every plan, and so does the start fee: an AI event pays for a call to a language model, and that does not get cheaper with your plan.

Example: r/netsec with 40 posts + 40 comments + 48 indicators + 24 related subreddits + 1 subreddit profile + 1 source check + start (once) = $0.32 at list price; with signals on, the 80 post and comment rows add up to $0.04; with ai: ["analysis"], the same 80 rows add another $0.04 and a subredditDigest $0.05; with ai: ["labels"] and 3 questions in aiLabels, the same 80 rows add 80 × ($0.0005 + 3 × $0.0001) = $0.064 — the analysis and the three answers of each row. The AI amounts are the same on every plan; the tier discount applies to the other amounts. Rows read with your own Reddit API key cost the same as rows read from the public feed. Use include to leave out the row kinds you do not need, and set a maximum charge per run in Run options to cap spending; when the cap is reached the run stops normally and says so in a charge-limit-reached row.

Restarts and repeat charges. When Apify moves a run to another server, or you abort a run and resurrect it, the run gets a signal, saves its own ledger and continues from it: rows already delivered are neither written nor charged again, and finished sources are not read again. A run that dies without such a signal (for example killed at its timeout) and is then resurrected continues from its ledger as well: a row is charged only after the ledger holding it was saved, so no row is charged a second time. A row that was being handed over at the very moment the run died (charged or not, written or not — the run cannot tell) is neither charged nor written again; rowsUnconfirmedAfterRestart in the limits row and a restart-unconfirmed notice give the count. If the ledger cannot be saved (3 tries), the run charges no further row or AI event and says so in a ledger-unwritable notice. If a run that starts again cannot read its ledger (three reads in a row fail, or the record is broken), it cannot tell what it already delivered, so it delivers nothing more and charges no row and no AI event more: it makes no request to Reddit and calls no model, writes one ledger-unreadable notice row (not charged) that says how many rows were in the dataset, and ends as a normal run. The run's start fee ($0.003) is charged as for every run — "nothing more" is about rows and AI events, not about the run as a whole. The rows delivered before the restart stay valid. Run it again with the same input to get the rest; with incremental on, the next run continues after the last saved position, without it the next run starts from the beginning and charges what it delivers. A run is taken as started again when its dataset already holds a row and its ledger cannot be read or is not there — or when the dataset cannot be counted; a first start has an empty dataset and collects as usual (a store that never answers lets it charge nothing: see ledger-unwritable above). The ledger that could not be read is not written over — every save reads it again first, and once that works the old record and the new one are saved together, so the next restart skips the rows of both. If the dataset refuses a row that was already charged, the run reads the end of the dataset back and writes the row once more only when it is not there; if that fails too, the row is missing from the dataset, is not charged again, and the count is reported in the source row's collectionError (or in a run-summary row). When the dataset cannot be read back, the row is not written a second time (to avoid a duplicate) and a delivery-unconfirmed notice gives the count. From that row on the run charges no further row or AI event: sources it had not read yet are not requested. If the platform's charge call itself fails, that row is still delivered and the call is not made again, so a row is never charged twice because of it; the number of such calls is unconfirmedCharges in the limits row. When the call fails 3 times in a row, or 10 times in one run, the run stops charging and requesting, and a charge-unavailable notice says how many charges could not be confirmed and how many sources were not requested. In incremental runs the saved position is written one write at a time; a write that fails is repeated with the next one, on the next save signal and once more when the run ends, and only if every one of those fails does the next run return (and charge) those items again. A post or comment reached through two sources of one run is delivered and charged once. For the first 400,000 items of a run that also holds across a restart; past that number the run only remembers the items (it no longer saves them), so it still holds inside the run, but after a restart an item first delivered past that number and reached again through another source may be delivered and charged by both — the limits row says so (runWideDedupe.savedForRestart: false). Past 1,900,000 items the run cannot remember more, and such an item may be delivered once per source (runWideDedupe.active: false).

Interrupted monitoring runs. With incremental: true, items are handed over oldest first, and the run saves the last item it delivered when it gets a migration or abort signal and with every periodic state save of the platform. The next scheduled run starts after that item, so it does not charge those items again even when the interrupted run is never resurrected. Two things remain. A run that dies without a signal leaves the items it delivered after its last periodic save to be delivered and charged again by the next run. And the indicator, related-subreddit, profile and keyword rows of a source are written after its items, so for a source that was interrupted they come only if that run is resurrected; a later run does not produce them for items an earlier run delivered.

Limits Reddit sets, and how the run reports them

Measured on 2026-10-07 on Reddit's public feeds (no login). The limits row and every source row report the values of your own run.

LimitValueHow it was established
Requests per IP1 per window of about a minute (x-ratelimit-used: 1, x-ratelimit-remaining: 0.0, x-ratelimit-reset: <seconds>)read from Reddit's response headers on every answer; the same IP answered 200 again after the window
Items per listingthe newest 1,000r/cybersecurity new: 999 posts in 10 pages; r/sysadmin comments: 984, which was 13.5 hours
Results per searchabout 2505 keywords: 224 to 249 results
Comments of one post per requestabout 500 (limit=500)4 posts: 497, 492, 305 and 14 comments. Other sort orders return other comments: 6 sort orders merged gave 1,700 and 1,288 comments for the two large posts; after= does not page a post's comments (0 new)
Posts of a user whose address lists nothingthrough Reddit search (author:name), posts only5 users: one address answered 200 with nothing and search listed 17 posts; for an open profile search listed 38 posts against 23 at the address; a user whose address answers 403 had 0 in search
Fields in the feedid, title, body, author, time, subreddit, link, outside linkno score, upvote ratio, comment count, comment depth or flair

What the Actor does about them: every request uses a new proxy session, at most 4 requests run at the same time, and requests to one source are at least 1 s apart. Only 429, 5xx and dropped connections are retried (after 1, 2 and 4 s, each time on a new IP; a Retry-After that asks for longer is honoured up to 30 s). A 403 or 404 is the source's answer and is not retried. When 3 sources in a row cannot be read — or answer 403 before the run has read anything at all — the run stops asking, says so in an upstream-blocked row and ends normally; nothing is charged for what was not read. For a post URL the source row and the limits row say how many comments came, under which sort orders, and whether one request came back full; the public feed does not say how many comments the post has in total (the API key layer does). When a listing reaches the 1,000-item limit the source row sets capReached and capAdvice tells you, from the time span your run actually covered, how often to schedule a monitoring run so that nothing is missed.

Add your own Reddit API key (optional, paid plans, beta)

The public feed has no scores. If you need them, register an app at reddit.com/prefs/apps and give the run its client ID, client secret and a User-Agent. The run then reads through Reddit's API with your key and switches back to the public feed whenever the key cannot be used.

  • What you get in addition: score, upvoteRatio, commentCount, flair, isStickied, isLocked, over18 on posts; score, parentId, depth on comments (a post URL returns the comment tree of one request, flattened with depth); karma, postKarma, commentKarma, accountCreatedAt on user profiles; subscriberCount on subreddit profiles; the post's own comment count in the limits row. Rows read this way say accessLayer: "api".
  • No login as you. The Actor uses app-only access (grant_type=client_credentials). It takes no Reddit password, no refresh token and no cookie, so there is no session of yours to log out. It only reads: the single POST it sends is the token request.
  • Your User-Agent, not ours. Reddit asks API clients for <platform>:<app ID>:<version> (by /u/<your username>), for example web:com.example.myapp:v1.0 (by /u/yourname). Without it — or with one not in this form — the key layer stays off and the limits row says why. The Actor never makes one up.
  • Your key is kept out of the output. The client ID and secret are secret inputs (stored encrypted by Apify). They and the access token are not written to the log, the dataset, the run's key-value store or any error message.
  • Same price. Rows read with your key are charged with the same events at the same prices. An item is delivered once, whichever layer read it.
  • Free plan: the key inputs are ignored and a free-plan-locked notice says so; nothing is sent with your key.

How not to get your Reddit key throttled. Limits as Reddit publishes them (documents read on 2026-10-07):

LimitValueSource
Requests per key100 queries per minute per OAuth client id, averaged over a window of 10 minutesReddit Data API Wiki
What Reddit reports on every answerX-Ratelimit-Used, X-Ratelimit-Remaining, X-Ratelimit-Resetsame document
AuthenticationOAuth is required; a unique User-Agent in the form abovesame document
App-only accessclient_credentials at https://www.reddit.com/api/v1/access_token, requests to https://oauth.reddit.com, a token lasts 1 hourOAuth2 wiki, "Application Only OAuth"

What the Actor does with your key: one request at a time, at apiRequestsPerMinute (default 80, that is 80% of the limit; lower it when the key is used elsewhere too). It reads the three headers on every answer and, when less than 10% of the window is left, rests until Reddit's reset. On a 429 it waits what Retry-After asks (60 s at most) and tries once; a second 429 in a row stops the key layer for the rest of the run. On a 401 or 403 it checks once with a new token; if that is refused too, the key layer stops (this also happens when one of your sources is private — the rest of the run then reads the public feed). apiMaxRequests caps how many requests a run may send with the key, the token request included. A stopped key layer is never started again in that run: the run goes on over the public feed from the same item, and the source and limits rows say where and why it switched (apiLayer.switchedToRss, layerSwitch, stopReason). The limits row reports the requests sent with your key, Reddit's last header values, the pace, the rests and the share of the 10-minute budget used.

Beta — what is and is not verified. This layer was built from the two Reddit documents above. Its tests run against synthetic API answers (made up in the documented shape), because we have no Reddit API app of our own. A check with a live key has not been done yet (status 2026-10-07: not verified; planned before the layer leaves beta). If your key returns something the Actor does not understand, the run stops using it, continues over the public feed and says so in the limits row — please tell us in the Issues tab.

What it does not do

  • No scores, upvote ratios, comment counts or comment depth without your own API key. Reddit's public feed does not carry them. The columns are present and null, and fieldAvailability on each row says so. With your own Reddit API key (paid plans, beta) they are filled.
  • No login of any kind. It never asks for a Reddit password or session cookie, and the optional API key is app-only, so there is no account to get logged out or banned — and nothing that needs an account can be read: private subreddits, age-restricted content, the comments of a hidden profile.
  • No complete history of a busy listing in one run. Reddit lists the newest 1,000 items; use incremental on a schedule for the rest.
  • No guaranteed complete comment tree of a post. One request returns about 500 comments; reading under 6 sort orders reaches more (1,700 and 1,288 in two large posts) but not provably all, and the public feed has no reply structure. With your API key the comments of one request come with depth and parent; replies Reddit leaves out of that answer are counted, not fetched.
  • No comments of a hidden profile. When a user's own address lists nothing, Reddit search still lists their posts; it does not list their comments.
  • No user search. Reddit's public feeds do not offer one; give usernames.
  • No language model unless you switch one on. Signals are rule-based (word lists and patterns, English) and come with their evidence; they are not a model's reading of the text. The AI layer is off by default, and its accuracy has not been measured yet: treat a verdict as a model's opinion, not as a fact.
  • No media downloads. Image and video addresses are delivered as links.

Tips

  • Start with the default (maxItemsPerSource: 40) to see the shape of the data, then raise it.
  • For monitoring, schedule the Actor with incremental: true and one monitorKey per schedule. The first run takes a baseline (the newest items); every later run returns only what is newer, oldest first. When more new items are waiting than maxItemsPerSource, the oldest are delivered and the rest come with the next run — nothing is skipped. A run cut short by an error or by your charge limit saves the last item it delivered and the next run continues from there.
  • A busy subreddit's comments pass 1,000 within hours. Read the capAdvice of the first run and schedule accordingly.
  • include: ["userProfile", "relatedSubreddits"] profiles a list of usernames without paying for every post and comment row.
  • mentioned_user indicator values are plain usernames — you can pass them back into users, or to a username-lookup Actor in the Apify Store, as a next step.
  • A forbidden or not_found source is a fact about that source, not a failed run.
  • A post URL returns up to 500 items by default (one request, each comment one comment event). For a long thread set maxCommentsPerPost to 3000: only above 500 is the post read again under the other sort orders.
  • Lead and support triage: signals: true with customLabels and competitorNames, then filter the dataset on signals.intent and signals.flags.

FAQ, disclaimers, and support

Is this legal? The Actor reads feeds that Reddit serves publicly to anyone without an account. Whether a particular use of that data is lawful depends on what you collect and what you do with it. You are responsible for how you use the results. Do not use this tool for harassment, stalking, or any unlawful purpose.

Reddit's terms. Your use of the data is subject to Reddit's own rules: the User Agreement and, where you build on the data, the Developer Terms and Data API Terms. Among other things they expect you to respect deletions: when a post, a comment or an account is deleted on Reddit, delete it from your copies too. The Developer Terms say "as soon as possible"; Reddit's Data API Wiki strongly recommends routinely deleting stored user data and content within 48 hours (both read on 2026-10-07). Read the current texts — they change, and they are the authority, not this summary.

Privacy & data responsibility. You are the data controller for every lookup you run with this Actor; ntriqpro is a data processor acting solely on your instructions. You are responsible for having a lawful basis and a legitimate, purpose-limited reason for each lookup, and for complying with GDPR, PIPA, CCPA, and other applicable privacy laws. We do not store your results — output is written only to your own run's dataset. With incremental, the last item id per listing is kept in a key-value store in your own account.

Why did a source return nothing? Look at its source row: forbidden means Reddit answered 403 (for a user: the profile is hidden, suspended or does not exist — the feed does not say which); not_found means there is nothing at that address; empty means the feed is open and lists no items. For a user, profileVisibility is hidden-or-empty when the user's own addresses answer 200 with nothing — the profile is hidden or has no public activity, the feed does not say which — and unavailable when they answer 403. In the first case a paid run reads the user's posts through Reddit search (recoveredVia: "search"); a free run says how many it found (recoverablePosts).

Trademarks. Reddit is a trademark of its owner. This Actor is an independent tool and uses the name only to describe what it reads.

Found a bug or want a feature? Use the Issues tab.

Used this Actor? A sentence in the Reviews tab about what you used it for helps the next person decide.