# RedditAPIs [Health: Active]

**Category:** 🌐 Social Media  
**Repository:** https://github.com/redditapis/redditapis-mcp  
**GitHub Stars:** 2  
**npm Downloads (last month):** 2292  
**Views:** 0  
**Installs:** 0  
**Upvotes:** 0  
**Directory Page:** https://allmcps.com/mcp/redditapis

## Description
Reddit API as MCP tools: search, subreddits, posts, comments, users, monitors, and webhooks.

## Tools
Capabilities this server exposes over MCP:

- **reddit_subreddit_posts** — Lists posts from a subreddit by sort order: newest, hot, top of a time window, rising, controversial or best. Returns post title, author, score, comment count, and permalink, plus an `after` cursor for paging. When `after` comes back null the response carries `listing_status`: `complete` means no older posts and is only claimed when the whole run came back in under one page, `truncated` means Reddit's cap cut the listing off, and `unknown` means completeness could not be established. A busy feed that Reddit stops serving reports `unknown`, not `complete`. Example: subreddit='programming' sort='top' t='week'.
- **reddit_verify_comments** — Checks whether specific Reddit comments still EXIST and are publicly visible, in one batch of up to 100 ids. A READ despite being a POST (ids travel in the body because a hundred of them exceed a URL's length); it costs the same as any other read and changes nothing on Reddit. It distinguishes 'deleted by the author', 'removed by a moderator' and 'still there', which a normal comment fetch cannot. Accepts bare ids and t1_-prefixed fullnames interchangeably. Returns one row per id, in request order, each with a status. Example: ids=['n5abcde','t1_n5fghij'].
- **reddit_home_feed** — Reads the caller's own Reddit home feed, the front page their subscriptions produce. The other read tools are served from a shared pool of accounts and so cannot return a personal feed; this one sends the caller's session. Requires the caller's Reddit session cookies (session and loid) from POST /api/reddit/login on the REST API, which is not an MCP tool. Without them it returns 400, because Reddit's logged-out front page is a different feed belonging to nobody rather than a thinner version of the caller's. Same post shape and `after` cursor as the subreddit listing. Example: sort='best' limit=25.
- **reddit_search** — Searches Reddit posts across all of Reddit or within one subreddit. Returns matching posts with author, score, comments, permalink, and an `after` cursor. `subreddit` scopes the search to one community. Optional advanced filters narrow the results by minimum/maximum score, comment count, media type, and post flags, with an optional re-sort of the page. Filters apply to the returned page, so a filtered response carries a `meta` object with page-completeness counts, and the next page comes from `after`. SORT BEHAVIOUR, measured 2026-09-11: `top`, `new` and `comments` order the MATCHING set by score, date or comment count alone, and Reddit matches loosely (image OCR, comments, and the word 'reddit' is in almost every big post), so a generic multi-word query with sort='top' and no subreddit returns the site-wide viral listing, not the topic. Even a distinctive term is ranked by score alone (q='pgvector' sort='top': 1 of 5 results was about pgvector, the rest were large posts that mention it once). Best match then highest score comes from sort='relevance' with a large `limit`, where sort_type='score' re-orders that returned page (it does not reach past the page); a quoted phrase (q='"rust vs go"') or a `subreddit` scope also narrows the matching set. Example: q='rust vs go' sort='relevance' t='year' limit=100 sort_type='score'.
- **reddit_post_visibility** — Is a post still publicly visible, or did it quietly stop being so? A removed Reddit post still returns when fetched by id, so the post alone does not answer this. This fetches the post and then one page of its author's submitted listing and compares them. Returns a verdict of live, not_visible or undecidable, a plain-language reason, and a confident flag. By design it does not say WHY a post is not visible: a moderator removal, an admin removal, a spam filter and an author who has hidden their history are indistinguishable from outside. undecidable is a real answer, not a failure. When the post resolves but the author listing read fails, the answer is still returned as undecidable with checked.author_listing false and checked.listing_error naming why (upstream_unavailable or upstream_timeout: our read was refused, not billed; listing_unavailable: Reddit answered the listing with 403 or 404, billed). Two upstream calls, billed as one $0.004 dual read.
- **reddit_post_comments** — Fetches a single post and its comment tree by permalink. Returns the post plus threaded comments (author, body, score, replies) and an `after` cursor. The `permalink` is the one carried by any post result. Size a big thread with the three knobs: `limit` caps top-level comments (1-500, default 100), `depth` caps reply depth (1-10; `depth=1` is top-level only; absent is the full tree) and `sort` orders them (confidence = Reddit's best, top, new, controversial, old, qa). `limit=20, depth=1, sort='top'` reads the twenty best top-level comments and nothing below them, which keeps a 2,700-comment thread readable. `meta` echoes limit_used, depth_used and sort_used.
- **reddit_post_duplicates** — Every other Reddit post that shares this post's link: the list Reddit shows on a post's "other discussions" tab, across all subreddits, so every conversation about one article, video or product page can be read, not only the first thread found. Returns the original `post`, `duplicates` (up to 100 in one read, not paged, `after` is null) and `available` (how many Reddit returned). A made-up or removed post id is a 404 NOT_FOUND. A text post usually has none.
- **reddit_search_communities** — Searches for subreddits (communities) by name or topic. Returns matching subreddits with title, subscriber count, description, and NSFW flag. Example: q='machine learning'.
- **reddit_search_comments** — Searches Reddit by COMMENT text. Reddit's comment search matches the keyword against comment bodies but returns the PARENT POSTS, not the individual comments, so each result is a post whose discussion mentions the query, carrying that post's title, selftext, score, and comment count. It surfaces threads where a topic comes up in the replies, which a post-title search misses. Reddit does not expose which specific comment matched or its text, so the results are posts, not comment bodies. Example: q='best mechanical keyboard' sort='relevance' t='year'.
- **reddit_deep_comment_search** — Comment search that returns the ACTUAL comments whose body matches the keyword, sorted by score (highest first), with body, score, author, a comment-deep permalink, and the parent post. It fetches each matching post's comment tree and filters the comment bodies, so the results are first-hand opinions and answers rather than parent posts. Premium call (it fans out into several reads): `limit` sets how many parent POSTS to expand, 1-25 (default 5), not how many comments come back. The response's `after` cursor expands the NEXT batch of parent posts. `max_comments` optionally caps how many comments come back (the top-scored are kept). Matching is on the visible comment text at word boundaries (link URLs are ignored), so every result mentions the query where a reader can see it. Best-effort: a deleted or deeply-nested comment may be missed (meta.truncated flags when a tree was too deep). Each parent post is its own comment-tree read and one can fail while the others succeed: meta.partial is true with meta.trees_failed and meta.trees_fetched when that happened, so the result covers fewer parent posts than it scanned; the call fails only when every tree failed and at least one was refused upstream. group_by='author' switches to a research mode that returns WHO is talking about the query (distinct people ranked by matching-comment count) in place of a flat comment list, capped by max_authors. Example: q='best mechanical keyboard' sort='relevance' t='year'.
- **reddit_search_media** — Searches Reddit posts filtered to media (images, video, gifs). Returns media posts with the media URL/type, author, score, and the post url. `kind` narrows the results to one media type. Example: q='aurora borealis' kind='image'.
- **reddit_search_users** — Searches for Reddit users (redditors) by name or keyword. Returns matching accounts with username, karma, and account age. Example: q='spez'.
- **reddit_subreddit_top** — Gets the TOP posts of a subreddit for a time window, the highest-scoring posts of a community. Returns posts with score, author, comments, and permalink plus an `after` cursor. Example: name='science' t='month'.
- **reddit_post** — Fetches a single Reddit post by its id. Returns the full post object (title, author, score, text, permalink, subreddit, url). Example: id='abc123' (the base-36 id, no t3_ prefix).
- **reddit_user_profile** — Fetches a Reddit user's public profile by username. Returns account info: username, id, karma (post + comment), account age, verified/employee flags, and avatar. Example: name='spez'.
- **reddit_user_achievements** — Lists a Reddit user's public achievements, the trophies shown on their reddit.com/user/<name>/achievements page. Returns each achievement's name, description, granted timestamp and icons, plus a count. An account with none returns an empty list rather than an error, so a zero count is a real answer. Achievements such as One-Year Club and Verified Email reflect account age and standing. Example: name='spez'.
- **reddit_user_comments** — Lists a Reddit user's recent comments. Returns comments with body, score, subreddit, parent link, and timestamp plus an `after` cursor. Example: name='spez' sort='top'.
- **reddit_user_submitted** — Lists a Reddit user's submitted POSTS (their post history, as distinct from their comments). Returns posts with title, author, score, comment count, and permalink, plus an `after` cursor. Example: name='spez' sort='top'.
- **reddit_user_upvoted** — Lists the posts and comments a Reddit account has UPVOTED. PRIVATE data: Reddit serves it only to the account that owns it, so the request carries that account's Reddit session cookies (the session and loid arguments, from POST /api/reddit/login on the REST API, which is not an MCP tool) and returns 403 when `name` is a different account. Mixed listing: each item is a post or a comment, tagged `kind`. Example: name='spez' (the logged-in account).
- **reddit_user_saved** — Lists the posts and comments a Reddit account has SAVED. PRIVATE data: Reddit serves it only to the account that owns it, so the request carries that account's Reddit session cookies (the session and loid arguments, from POST /api/reddit/login on the REST API, which is not an MCP tool) and returns 403 when `name` is a different account. Mixed listing: each item is a post or a comment, tagged `kind`. Example: name='spez' (the logged-in account).
- **reddit_user_hidden** — Lists the posts and comments a Reddit account has HIDDEN. PRIVATE data: Reddit serves it only to the account that owns it, so the request carries that account's Reddit session cookies (the session and loid arguments, from POST /api/reddit/login on the REST API, which is not an MCP tool) and returns 403 when `name` is a different account. Mixed listing: each item is a post or a comment, tagged `kind`. Example: name='spez' (the logged-in account).
- **reddit_user_gilded** — Lists the posts and comments a Reddit account has received an award (gold) on. PRIVATE data: Reddit serves it only to the account that owns it, so the request carries that account's Reddit session cookies (the session and loid arguments, from POST /api/reddit/login on the REST API, which is not an MCP tool) and returns 403 when `name` is a different account. Mixed listing: each item is a post or a comment, tagged `kind`. Example: name='spez' (the logged-in account).
- **reddit_subreddit_comments** — Streams the NEWEST comments across an entire subreddit (Reddit's /r/<name>/comments feed), not one post's thread. Returns comments with body, author, score, subreddit, the parent post link, and timestamp, plus an `after` cursor. Repeated calls surface new comments in a community as they are posted. Example: name='python'.
- **reddit_subreddit_about** — Fetches a subreddit's public metadata by name (Reddit's /r/<name>/about data). Returns the subreddit's title, public description, subscriber count, active-user count, creation timestamp, type, and NSFW flag. Example: name='python'.
- **reddit_subreddit_rules** — Fetches a subreddit's posting rules by name (Reddit's /r/<name>/about/rules data). Returns a `rules` list, each with name, description, what it applies to (posts, comments, or all), violation reason, priority, and creation date, plus a `site_rules` list of Reddit's site-wide rules. Example: name='python'.
- **reddit_subreddit_moderators** — Fetches a subreddit's moderator team by name (Reddit's /r/<name>/about/moderators data). Returns a `moderators` list, each with `name`, `id`, `mod_permissions`, `flair_text`, and `added` (when they joined the mod team). Example: name='python'.
- **reddit_subreddit_flairs** — Lists a subreddit's post (link) flair templates by name: the flairs a new post in that community can carry. Returns `subreddit`, `count` and `flairs`, each with `id` (the value a post submission takes as flair_id), `text`, `editable` (true when a custom flair_text is accepted with that id), `type` (text or richtext), `background_color`, `text_color`, `css_class`, `mod_only`, `max_emojis`, `allowable_content` and `richtext`. Many subreddits remove a post that has no flair. Reddit lists flairs only to signed-in accounts, so the read runs as the Reddit session connected for this API key; with none connected it answers 503 SESSION_REQUIRED before Reddit is asked, and that answer is not charged. A moderator's session sees every template, mod-only ones included; any other account sees the templates it can post with, listed with mod_only false. A subreddit with no post flairs returns an empty list. Billed at the read rate, including a 403 or 404 from Reddit. Example: name='SaaS'.
- **reddit_subreddit_wiki** — Fetches a subreddit's wiki page by name and page (Reddit's /r/<name>/wiki/<page> data). Returns a single object with `content_md` and `content_html`, a `may_revise` flag, and the last revision (`revision_id`, `revision_date`, `revised_by`, `reason`). A wiki commonly holds a community's rules or FAQ. The page may be multi-segment, for example index, rules, or config/sidebar. Example: name='python', page='index'.
- **reddit_by_id** — Bulk-fetches posts by their t3_ fullnames in ONE call (up to 100), in place of a request per post. Takes a comma-separated list of fullnames from a search or listing. Returns posts with title, author, score, comment count, and permalink, the same post shape as the listing endpoints. The result is not necessarily one-to-one with the request, and `meta` reports which: `listing_status` is `complete` only when every fullname came back, `truncated` is a boolean, and `missing_fullnames` names exactly which ids did not come back. Example: fullnames='t3_abc123,t3_def456'.
- **reddit_subreddits_popular** — Browses the most-subscribed, trending subreddits right now, with no keyword. Returns a `subreddits` list (each with name, title, subscriber count, description, type, and NSFW flag) plus an `after` cursor for paging.
- **reddit_subreddits_new** — Browses the newest subreddits, the communities most recently created, with no keyword. Returns a `subreddits` list (each with name, title, subscriber count, description, type, and NSFW flag) plus an `after` cursor for paging.
- **reddit_subreddits_default** — Browses Reddit's default front-page set of subreddits, with no keyword. Returns a `subreddits` list (each with name, title, subscriber count, description, type, and NSFW flag) plus an `after` cursor for paging.
- **reddit_monitor_add** — Creates a Reddit monitor: watches one or more subreddits, or ALL of Reddit, for new posts (or comments, via `kind`) matching a filter, and delivers every match to the account's registered webhooks. With `subreddit` absent and `q` set, it is a sitewide keyword monitor covering every subreddit at once (posts only). By default matches go to EVERY active webhook on the account; `webhook_ids` routes this monitor's matches to specific webhooks. Every redditapis.com account holds a free entitlement of ONE all-of-Reddit post watch at a 60s cadence (up to 10,000 deliveries a day), so that monitor needs no subscription. Naming a `subreddit`, matching comments, a faster cadence and any additional watch require a paid plan. Needs at least one free monitor slot (the `slots` object in the monitor list). Forward-looking only, from the moment of creation or from `baseline_item_id` if given; posts that already existed are not backfilled. Returns the created monitor (with its `id`) on success, or `subscription_required` (402) if the account holds no recognised entitlement at all, `subreddit_scope_requires_paid_plan` (402) if a free account named a `subreddit`, `monitor_slots_exhausted` (402) if the plan's slot limit is reached, `sitewide_slots_exhausted` (402) if the plan's separate sitewide cap is reached, `distinct_subreddit_limit_reached` (402) if the account already watches as many DIFFERENT subreddits as the plan covers (the limit counts distinct subreddits across all the account's monitors, not monitors, and the same subreddit in two monitors counts once; `slots.distinct_subreddits_total` in the monitor list), `sitewide_comment_monitoring_not_available` (501) if a sitewide monitor asks for comments, `subreddit_reserved` (400) if `subreddit` names 'all', `subreddit_not_found` (400) if a named subreddit does not exist, or `webhook_not_found` (400) if a `webhook_ids` entry is not on this account.
- **reddit_monitor_list** — Lists every monitor on the caller's account, with each one's filter, active state, and cadence, plus a `slots` object ({used, total, tier}) showing how many monitor slots are purchased vs in use. Reading the list requires no active plan (a lapsed subscription shows an empty or paused list, not an error). A null `webhook_ids` is the default and means matches go to EVERY active webhook on the account, the normal healthy state, not a monitor without a destination; a non-empty array narrows delivery to those webhook ids. Where a monitor's matches actually went is recorded per delivery, as the resolved `webhook_id` on each delivery row.
- **reddit_monitor_update** — Updates an existing monitor: pauses or resumes it (`active`), changes its poll interval (`cadence_s`), switches between posts and comments (`kind`), replaces its filter entirely, or re-targets which webhooks it delivers to (`webhook_ids`). Any filter field (subreddit, q, kind, domain, etc.) REPLACES the whole filter: it does not merge with the existing one, so a field absent from the request is cleared, and a request without `kind` reverts the monitor to posts-only. `webhook_ids` likewise REPLACES the monitor's targeting outright (an empty array clears back to every active webhook); a request without it leaves the existing targeting untouched. A request with only `active` and/or `cadence_s` changes only those. Returns 404 `monitor_not_found` if the id does not exist or is not on this account, or 400 `webhook_not_found` if a `webhook_ids` entry is not on this account.
- **reddit_monitor_remove** — Deletes a monitor. There is no undo endpoint: it stops matching immediately, its slot is freed for a new monitor, and it disappears from the monitor list. Its past deliveries are NOT erased; they remain queryable in the delivery history (both by monitor id and in the aggregate, no-id view) indefinitely. Returns 404 `monitor_not_found` if the id does not exist or is not on this account.
- **reddit_monitor_health** — Per-monitor health: active state, poll cadence, when it last matched something, and delivery counts for the last 24h (`delivered_24h`, `failed_24h`, `suppressed_24h`), plus whether its delivery ceiling has been hit (`ceiling_reached`) and what that ceiling is (`daily_delivery_ceiling`). `suppressed_24h` COUNTS TWO DIFFERENT THINGS AND `suppressed_breakdown` SPLITS THEM: `ceiling` (the monitor's daily cap was reached) and `stale` (the item was already older than the freshness window when first seen, so it was withheld rather than delivered as if it were new). A stale withhold is the usual reason a monitor's delivered count sits far below its matched count with no error anywhere; it is the freshness gate working as intended, not a failure or a plan limit, and retrying cannot recover the items. `ceiling_reached` is the field that indicates the daily cap was hit, and a stale withhold does not set it. When `suppressed_breakdown.resolved` is false the split does not add up to the total and `unresolved_reason` says why; `ceiling_reached` stays conservative in that case rather than being cleared. THOSE COUNTS COVER ONLY POSTS THAT WERE FETCHED. A post that was not fetched was not matched and leaves no row behind, so it is absent from all three counters; zeros there mean no matches were recorded, not that nothing was missed. `coverage_24h` is the separate field that speaks to fetching. Its `status` is `complete` (every poll reached the point where the previous poll finished), `degraded` (at least one poll was truncated and posts were lost unrecoverably; detail in `gaps[]`, `posts_in_window_at_least` and the estimated `posts_missed_estimate`), `partial` (nothing found, but not every kind of loss was checked; detail in `unobserved_events`), or `unknown` (the check could not be run at all; detail in `reason`). `unknown`, `partial` and a null count each mean the question went unanswered. `coverage_24h` is fed by the poll log and covers BOTH ways a poll loses posts unrecoverably (`listing_exhausted`, where Reddit stopped serving older posts, and `poll_overflow`, where the feed outran one poll's page budget); `observed_events` names what was actually checked, and a status of `complete` is awarded only when both were. WHAT `complete` ESTABLISHES: every poll that RAN got back to where the previous poll finished, AND every feed was actually polled. The second half comes from `stream_liveness`. A feed that is not polled writes nothing to the poll log, so on the poll log alone it looks identical to a healthy monitor; `stream_liveness` reads a different source (the polling registry plus the per-feed last-successful-poll stamp), neither of which a poll that did not run can produce. Its `status` is `live` (every feed checked within three times its own interval), `degraded` (at least one is not: `streams[]` marks each feed `never_polled`, `stalled` or `unregistered`), or `unknown` (could not be established, including `streams_awaiting_first_poll` on a monitor created moments ago, which clears itself). `coverage_24h` reads `complete` only while `stream_liveness` is `live`. Per feed in `streams[]`, `consecutive_errors` counts failed checks in a row. While a feed's checks are failing, `last_poll_error.detail` says why as a code such as `HTTP_403` (often a private or quarantined subreddit), `HTTP_429` (Reddit rate-limited the check) or `network_error` (`reason` is `fetch_failed`, or `poll_failed` with a null detail for an internal fault), and `retry_within_s` is the longest the feed waits for its next check while the poller backs off. `last_poll_error` is null while checks succeed, and can also be null during a failure when no stored failure for that feed could be read; a null there is unanswered, not healthy. A zero delivery count on a `never_polled` feed means nothing was checked, not a quiet subreddit. `gaps[]` IS A SAMPLE, NOT THE WHOLE LIST: it carries the most recent gaps only, `gaps_returned` says how many are in it, `gaps_truncated` says whether more exist, and `gap_events_24h` is the true total. `cadence` reports whether the monitor is served the interval its plan sells, which none of the fields above can. `promised_cadence_s` is the floor the account's CURRENT plan includes and `requested_cadence_s` is what this monitor is actually set to; `meets_entitlement` false means the monitor is set slower than the plan allows, which happens because a plan upgrade does not re-cadence monitors that already exist (setting `cadence_s` on the monitor changes it, and a deliberately slower cadence is also a legitimate choice). `last_checked_s_ago`, `freshness_ratio` and `within_margin` describe the slowest feed feeding this monitor, named in `slowest_stream`; `freshness_reading` marks them as ONE INSTANTANEOUS SAMPLE, not an average and not a sustained verdict, so a single reading past the margin is not by itself proof of under-service. These fields are TRI-STATE: a null `within_margin` or `meets_entitlement` means the question could not be answered, and `unknown_reason` says why.
- **reddit_monitor_deliveries** — Delivery history: the actual Reddit posts a monitor's webhook has received (or attempted), newest first, including the real post content (title, subreddit, permalink, author). It records what was sent, not only how many (the counts are in monitor health). Every delivered item also carries `payload.items[].enrichment`: a `relevance.score` (0-1, how much of THIS monitor's own keyword criteria the item matched; not a model's confidence), a `sentiment` (`polarity` -1 to 1 plus a positive/negative/mixed/neutral `label`), and an `intent.tag` (question, recommendation_request, complaint, promotion, praise, or discussion). All three are deterministic keyword, lexicon and rule heuristics computed at no extra cost; each carries its own `method` field and none of them is a machine-learning or LLM call. Without `id`, the history aggregates across every monitor on the account.
- **reddit_monitor_webhook_create** — Registers a delivery target for monitors to send matches to. Requires an active monitoring plan (a webhook with no plan could not receive anything). Returns the webhook with its signing `secret` SHOWN ONCE: the webhook list does not return it again. HTTPS only; the URL is re-validated (including a fresh DNS check) at every delivery, not just at creation.
- **reddit_monitor_webhook_list** — Lists every webhook registered on the caller's account. The signing secret is not included; it is shown once, at creation.
- **reddit_monitor_webhook_test** — Sends a one-off test delivery to a registered webhook (rate-limited to 10/min), confirming it is wired up correctly before a real match arrives. The test payload is formatted by the webhook's `kind`, the same way a real delivery would be. On failure the response carries `reason` and `status`, plus `hint`, a sentence naming the fix (most often that the target's `kind` does not match its host, which no test can succeed through), and `detail`, a bounded, sanitised copy of what the destination itself replied. `hint` carries the remedy; a bare `reason` such as `http_error` with a 400 names no field, no value and no remedy. Returns 404 `webhook_not_found` if the id does not exist or is not on this account, or `webhook_url_rejected` if the URL fails re-validation (e.g. now resolves to a private address).
- **reddit_monitor_webhook_delete** — Permanently deletes a webhook. A monitor still pointing at it fails to deliver until it is repointed at a different webhook: deletion does NOT cascade-delete or pause the monitors using it. Cannot be undone. Returns 404 `webhook_not_found` if the id does not exist or is not on this account.
- **reddit_feedback_send** — Reports a product problem or gap in redditapis.com to its team, through a local draft queue. action "draft" (the default) writes a report to a queue on this machine and sends nothing; it makes no network call. action "list" returns the pending drafts with their ids. action "send" posts the named draft ids to POST /feedback (free, not metered) and returns a server id per report. action "discard" drops the named drafts. The queue holds at most 10 drafts; a draft past that is refused. A draft takes `type`, a one-line `title` (at most 120 characters), `details` (at most 8000 characters) and optional `area` and `evidence`; evidence keys left out (tool, endpoint, HTTP status, request id) are filled from the last failing call in this session, and the MCP version and client name are attached to every report.
- **reddit_feedback_list** — Lists the feedback reports this account has sent, newest first, with each one's current status. The server id is returned only once, when a report is sent, so this listing is how a report whose id was not kept is found again; it also shows whether a report landed and whether the team has acted on it. Optional filters by status or type; paging uses the cursor from a previous response. Free per call, not metered. Returns {feedback: [...], count, limit, next_cursor}; pages continue while next_cursor is non-null, and an account that has filed nothing gets an empty list and a 200, not an error. It lists SENT reports on the server, which differ from the unsent local drafts on this machine.
- **reddit_account_me** — Returns the remaining credit balance and usage totals of the account behind this API key. Free: not metered, and costs no credit. A 402 from a metered call means the balance is exhausted; that response carries a top-up URL.
- **reddit_account_payments** — Returns the payment history of the account behind this API key: each top-up's amount, the credits it added, its status and when it happened, newest first. Free: not metered, and costs no credit.
- **reddit_connect** — Stores the customer's own Reddit session once, so the comment, submit, vote and DM tools in this server act as that account without cookies in later calls. The session is kept server-side, encrypted (AES-256-GCM, key held only by the API), against this API key's account, after a check with Reddit: a logged-out session is refused with SESSION_LOGGED_OUT. Takes Reddit's session and loid cookies (from the REST login endpoint or the customer's own browser), plus the CSRF cookie for voting and messaging. Connecting again replaces the stored session. Free.
- **reddit_session_status** — Whether a Reddit session is connected for this account: `connected`, the Reddit username, `status` (active, or dead after Reddit rejected it), and when it was first stored, last updated and last used. The cookies are not returned. Free.
- **reddit_disconnect** — Deletes the stored Reddit session for this account. After this, the write tools answer with a request for cookies until a session is connected again. Idempotent: `deleted` is false when nothing was stored. Free.
- **reddit_comment** — Posts a comment on a Reddit post as the connected account. A public action on Reddit under the customer's name, marked as a write so a client can ask before it runs. The text is posted as Reddit markdown. Billed at the write rate. With no connected session it answers 400 asking for cookies.
- **reddit_submit** — Submits a new post to a subreddit as the connected account: a text post (kind self) or a link post (kind link). A public action on Reddit under the customer's name, marked as a write so a client can ask before it runs; a subreddit's rules decide whether the post stays up. Billed at the write rate.
- **reddit_vote** — Upvotes, downvotes or clears the vote on a post (t3_) or comment (t1_) as the connected account. Voting needs the CSRF cookie in the stored session. Billed at the vote rate.
- **reddit_dm** — Sends a private Reddit chat message as the connected account. A direct message to a real person under the customer's name, marked as a write so a client can ask before it runs. Messaging needs the CSRF cookie in the stored session. Billed at the DM rate.
- **reddit_scheduled_create** — Schedules a Reddit post or comment to be made later as the connected account, at `run_at` (60 seconds to 30 days ahead). At that time the stored request runs through the same path a live post submission or comment takes, as the Reddit session connected when it runs, so a session logged out in between makes the run fail without a charge. A public action on Reddit under the customer's name, marked as a write so a client can ask before it runs; a subreddit's rules decide whether a post stays up. Due actions are picked up every 30 seconds and run one at a time, normally within about a minute of `run_at`. A failed run is not retried: it is recorded as failed with Reddit's reason. Up to 100 actions can wait per account (409 SCHEDULE_LIMIT_REACHED past that). Scheduling is free; a run that succeeds is billed at the write rate to this API key, and a failed or cancelled one is not charged. With no connected session it answers 400 SESSION_NOT_CONNECTED. Returns 201 with `action`: `id`, `type`, `status` (scheduled), `run_at`, the stored `request`, `created_at`, `finished_at`, `result`, `error` and `billed`.
- **reddit_scheduled_list** — Lists this account's scheduled posts and comments, newest first. Returns `actions` and `count`. Each action carries `id`, `type`, `status` (scheduled, running, succeeded, failed or cancelled), `run_at`, the stored `request`, `created_at`, `finished_at`, `result` (what the post or comment returned, when the run succeeded), `error` (`code`, `message` and `http_status`, when the run failed) and `billed`: true for a run that succeeded, false for one that failed with an answer or was cancelled, null when the outcome was not confirmed (error EXECUTION_UNCONFIRMED or EXECUTION_INTERRUPTED, where the post or comment may still have been made and charged). Free.
- **reddit_scheduled_cancel** — Cancels a scheduled post or comment by its id, while it has not run yet. Returns `cancelled`, the `action` and a `message`. An action that was still scheduled comes back with cancelled true: it will not run and is not charged. An action that already ran, or is running, comes back with cancelled false and its current status, and nothing changes. 404 NOT_FOUND when the id is not a scheduled action on this account. Free.
- **reddit_set_watch** — Creates a Reddit watch from a plain-words description in one call: the description is compiled into a monitor filter, the account's own plan capabilities are read, the monitor is created, and an HTTPS delivery target given in `deliver_to` is registered, pointed at this monitor and test-fired. The compiler reads subreddit names written as r/Name, quoted phrases as keyword terms (two or more become an any-of set), 'comments' or 'posts and comments' as the match kind, terms after 'except' or 'ignoring' as exclusions, and 'at least N upvotes' as a score floor; everything it derived comes back in `understood` and `compiled_filter`. A description naming neither a subreddit nor a keyword is refused before any request is sent, because a monitor is anchored by one or the other. On an account whose plan carries no subreddit-scoped watches, a description that names subreddits and also carries a keyword is built over every subreddit instead, and the swap is reported in `notes`. Monitor and webhook calls are not metered. The result carries the created monitor, the delivery target and its test outcome, and the account's current slot and capability figures.
- **reddit_explain** — Returns the provenance of the rows this server has already fetched in this session: per completed call, the tool that made it, the endpoint it reached, the timestamp the response arrived, how many seconds old that copy now is, the upstream request id and the response size. Provenance is recorded per call, so every row in one response shares it. The redditapis.com read path holds no response cache for customer reads, so each call is served live at the moment it runs and the age reported here is the age of this session's copy of the rows rather than the age of a stored row; `response_cache` says so in the result. The ledger holds the 20 most recent completed calls, lives in memory, starts empty, is scoped to this server and carries no other caller's calls. How long each category of data is kept is published at https://www.redditapis.com/privacy-and-data-handling.
- **reddit_feedback_get** — Returns the status of a feedback report this account sent earlier, by its server id: status new, triaged, shipped or declined, the team's response text if any, and updated_at, which moves only when the team acts on it. Free per call. 404 if the id is not on this account.

## Claude Desktop Quick Installation
Install path detected from listing signals. Uses `npx` (confidence: high):

```json
"mcpServers": {
  "redditapis": {
    "command": "npx",
    "args": ["-y","redditapis-mcp@latest"]
  }
}
```

## Documentation & README

# redditapis-mcp

[![npm version](https://img.shields.io/npm/v/redditapis-mcp)](https://www.npmjs.com/package/redditapis-mcp)
[![npm downloads](https://img.shields.io/npm/dm/redditapis-mcp)](https://www.npmjs.com/package/redditapis-mcp)
[![license](https://img.shields.io/npm/l/redditapis-mcp)](./LICENSE)

redditapis-mcp is the official MCP server for RedditAPIs, built for developers and AI agents that need live Reddit data in Claude, Cursor, Windsurf, VS Code or any MCP client, with no Reddit developer app. Your agent can search posts and comments, read subreddit feeds and rules, expand comment trees, look up users and communities, and set up monitors that send new matching posts to your webhook. Reads cost $0.0015 per call (or a monthly read plan from $19 for 30,000 reads), every account gets 1,000 free reads a month, and new accounts get $0.50 of free credit with no card. Connect your Reddit account once to comment, post, vote and DM as you. Every tool maps to one endpoint at api.redditapis.com.

Install: `npx -y redditapis-mcp@latest` with `REDDITAPIS_KEY` from https://www.redditapis.com

Hosted, sign in instead of pasting a key (Claude Code): `claude mcp add --transport http reddit https://api.redditapis.com/mcp` (carries every tool except the 5 that read your own account)

Website: https://www.redditapis.com/mcp

## Quick start

No install needed. Run with `npx`. You need one thing: an API key from **[redditapis.com](https://www.redditapis.com)**. Reads work with just that key, so there is no account login or session step to set up. Monitor/webhook management additionally requires an active monitoring plan (monitoring has no free tier).

## Setup

### Claude Desktop

Edit `claude_desktop_config.json` (Settings > Developer > Edit Config):

```json
{
  "mcpServers": {
    "reddit": {
      "command": "npx",
      "args": ["-y", "redditapis-mcp@latest"],
      "env": { "REDDITAPIS_KEY": "YOUR_API_KEY" }
    }
  }
}
```

Restart Claude Desktop. The `reddit_*` tools appear in the tool picker.

### Claude Code

```bash
claude mcp add reddit --env REDDITAPIS_KEY=YOUR_API_KEY -- npx -y redditapis-mcp@latest
```

### Cursor

`~/.cursor/mcp.json` (or Settings > MCP > Add New Server):

```json
{
  "mcpServers": {
    "reddit": {
      "command": "npx",
      "args": ["-y", "redditapis-mcp@latest"],
      "env": { "REDDITAPIS_KEY": "YOUR_API_KEY" }
    }
  }
}
```

### Windsurf

`~/.codeium/windsurf/mcp_config.json`:

```json
{
  "mcpServers": {
    "reddit": {
      "command": "npx",
      "args": ["-y", "redditapis-mcp@latest"],
      "env": { "REDDITAPIS_KEY": "YOUR_API_KEY" }
    }
  }
}
```

### VS Code (Copilot / agent mode)

`.vscode/mcp.json` in your workspace, or the user-level MCP settings:

```json
{
  "servers": {
    "reddit": {
      "type": "stdio",
      "command": "npx",
      "args": ["-y", "redditapis-mcp@latest"],
      "env": { "REDDITAPIS_KEY": "YOUR_API_KEY" }
    }
  }
}
```

## Configuration

| Env var | Required | Default | Purpose |
|---|---|---|---|
| `REDDITAPIS_KEY` | Yes | (none) | API key from [redditapis.com](https://www.redditapis.com). `REDDIT_APIS_KEY` is accepted as an alias. |
| `REDDITAPIS_BASE_URL` | No | `https://api.redditapis.com` | Override the API host. |
| `REDDITAPIS_TIMEOUT_MS` | No | `30000` | Per-request timeout in milliseconds. |
| `REDDITAPIS_FEEDBACK_DIR` | No | `~/.redditapis` | Where `reddit_feedback_send` keeps its local draft queue (`feedback-queue.json`). |

Authentication is a Bearer token: the server sends `Authorization: Bearer <REDDITAPIS_KEY>` on every request.

## Tools

59 tools and 3 playbook resources: 32 Reddit reads, 10 Reddit account tools (connect, status, disconnect, comment, submit, vote, DM, and schedule, list or cancel a post or comment for later), 10 monitor/webhook management tools, `reddit_set_watch` (one call that stands up a whole watch), `reddit_explain` (where the rows you are holding came from), 3 feedback tools, and 2 account tools (`reddit_account_me`, which is also where the credit balance lives, and `reddit_account_payments`, the top-up history). Reddit writes (commenting, posting, voting, DMs) act as YOUR Reddit account and only after you connect it with `reddit_connect`; each is marked as a write so your client asks before it runs. Monitor/webhook tools configure your OWN redditapis.com account (an alerting subscription), never Reddit itself, and the feedback tools send a report to the redditapis.com team, never to Reddit. Every read works with just your API key; the 6 monitor/webhook writes additionally need an active monitoring plan (see Monitoring below). The feedback tools are free and need only your key.

A few conventions across the catalog:

- Subreddit names go in **without** the `r/` prefix, and usernames **without** the `u/` prefix. `reddit_subreddit_posts` takes its community as `subreddit`; the `/sub/{name}/...` tools take it as `name`.
- Listing and search tools return an `after` cursor. Pass it back as `after` to fetch the next page, exactly as it was returned. `limit` accepts 1 to 100 (the API clamps out-of-range values).
- When `after` comes back `null` there is no next page to ask for. That does not always mean you have every item: Reddit often stops serving a busy listing long before it runs out. The final response also carries `listing_status`, which reads `complete`, `truncated` or `unknown`. Only `complete` means nothing is missing. Treat the other two as a partial answer and widen across sorts, timeframes or search terms rather than paging deeper.
- `t` (`hour`, `day`, `week`, `month`, `year`, `all`) sets the time window; on subreddit listings it applies to the `top` and `controversial` sorts, and on search it bounds the whole result set.

### Search and discovery

| Tool | Endpoint | What it does |
|---|---|---|
| `reddit_search` | `GET /api/reddit/search` | Search posts across all of Reddit or within one subreddit (`subreddit`). |
| `reddit_search_comments` | `GET /api/reddit/search/comments` | Search by comment text; returns the parent posts, since Reddit's comment search does not hand back the matching comment itself. |
| `reddit_deep_comment_search` | `GET /api/reddit/search/comments/deep` | Genuine comment search: returns the actual matching comment bodies (score, author, comment-deep permalink, parent post). `limit` sets how many parent posts to expand; `group_by="author"` switches to research mode (the distinct people talking about your query). Premium call. |
| `reddit_search_media` | `GET /api/reddit/search/media` | Search posts filtered to media, narrowed by `kind` (`image`, `video`, `gif`, `all`). |
| `reddit_search_communities` | `GET /api/reddit/search/communities` | Find subreddits by name or topic (title, subscribers, description, NSFW flag). |
| `reddit_search_users` | `GET /api/reddit/search/users` | Find redditors by name or keyword (username, karma, account age). |

### Subreddits

| Tool | Endpoint | What it does |
|---|---|---|
| `reddit_subreddit_posts` | `GET /api/reddit/posts` | List a subreddit's posts by `sort` (`new`, `hot`, `top`, `rising`, `controversial`, `best`). |
| `reddit_feedback_list` | `GET /feedback` | List the reports this account has sent, newest first, with their status. The way back to a report whose id was not kept. Free. |
| `reddit_verify_comments` | `POST /api/reddit/comments/verify` | Check whether up to 100 specific comments still exist and are publicly visible. A read despite the POST; tells 'deleted by author' from 'removed by a mod' from 'still there'. |
| `reddit_home_feed` | `GET /api/reddit/feed` | Read YOUR OWN Reddit home feed. Needs your `reddit_session` and `loid` from `POST /api/reddit/login` (a REST call, not an MCP tool); they travel as headers, never in the URL. |
| `reddit_subreddit_top` | `GET /api/reddit/sub/{name}/top` | Top posts of a subreddit for a time window (`t`). |
| `reddit_subreddit_comments` | `GET /api/reddit/sub/{name}/comments` | Stream the newest comments across an entire subreddit (not one post's thread). |
| `reddit_subreddit_about` | `GET /api/reddit/sub/{name}/about` | A subreddit's public metadata: title, description, subscriber and active-user counts, type, NSFW flag. |
| `reddit_subreddit_rules` | `GET /api/reddit/sub/{name}/rules` | A subreddit's posting rules plus Reddit's site-wide rules. |
| `reddit_subreddit_moderators` | `GET /api/reddit/sub/{name}/moderators` | A subreddit's moderator team, each with permissions, flair, and when they joined. |
| `reddit_subreddit_flairs` | `GET /api/reddit/sub/{name}/flairs` | A subreddit's post flair templates (`id`, `text`, `editable`, colours, `mod_only`), so a post can carry the `flair_id` the community requires. Reddit lists flairs only to signed-in accounts, so it reads as your connected Reddit session; with none connected it answers 503 `SESSION_REQUIRED` and is not charged. |
| `reddit_subreddit_wiki` | `GET /api/reddit/sub/{name}/wiki/{page}` | A subreddit's wiki page by name and `page` (markdown + HTML, revision metadata). |

### Posts and comments

| Tool | Endpoint | What it does |
|---|---|---|
| `reddit_post_visibility` | `GET /api/reddit/post/{id}/visibility` | Is a post still publicly visible, or did it quietly stop being so? Fetches the post and one page of its author's listing and compares. Returns `live`, `not_visible` or `undecidable` with a reason, and never claims to know WHY. Two upstream calls, $0.004. |
| `reddit_post` | `GET /api/reddit/post/{id}` | A single post by its base-36 `id` (no `t3_` prefix): title, author, score, text, permalink, subreddit, url. |
| `reddit_post_comments` | `GET /api/reddit/comments` | A post plus its full threaded comment tree, fetched by `permalink`. |
| `reddit_post_duplicates` | `GET /api/reddit/post/{id}/duplicates` | Every other post that shares this post's link, across subreddits (Reddit's "other discussions"). |
| `reddit_by_id` | `GET /api/reddit/by_id/{fullnames}` | Bulk-hydrate up to 100 posts in one call from a comma-separated list of `t3_` fullnames. |

### Users

| Tool | Endpoint | What it does |
|---|---|---|
| `reddit_user_profile` | `GET /api/reddit/user/{name}` | A user's public profile: karma, account age, verified/employee flags, avatar. |
| `reddit_user_comments` | `GET /api/reddit/user/{name}/comments` | A user's recent comments (body, score, subreddit, parent link, timestamp). |
| `reddit_user_submitted` | `GET /api/reddit/user/{name}/submitted` | A user's submitted posts (the sibling of `reddit_user_comments`). |
| `reddit_user_achievements` | `GET /api/reddit/user/{name}/achievements` | A user's public achievements (the trophies on their profile): name, description, granted timestamp, icons. An account with none returns an empty list. |

### Community browse (no keyword)

| Tool | Endpoint | What it does |
|---|---|---|
| `reddit_subreddits_popular` | `GET /api/reddit/subreddits/popular` | Browse the most-subscribed, trending subreddits right now. |
| `reddit_subreddits_new` | `GET /api/reddit/subreddits/new` | Browse the newest subreddits, most recently created first. |
| `reddit_subreddits_default` | `GET /api/reddit/subreddits/default` | Browse Reddit's default front-page set of subreddits. |

### Monitoring: manage your own monitors and webhooks

Monitors watch **named subreddits or all of Reddit**, for **posts or comments** (`kind`). Every account holds a free entitlement of one all-of-Reddit post watch at a 60s cadence; naming a subreddit, matching comments, a faster cadence and any further watch need a paid plan. Registering a webhook does **not** need a paid plan, because the free watch delivers to one exactly like a paid watch does. Reading your own list, health and deliveries never needs a plan.

`reddit_monitor_list` returns a `slots` object that states this account's capabilities outright: `used`, `total`, `tier`, `is_free`, `cadence_s` (the tier's cadence floor), `sitewide_slots`, `distinct_subreddits_used`, `distinct_subreddits_total`, `comments_allowed`, `scoped_allowed` and `sitewide_allowed`. `reddit_set_watch` reads those flags before it builds anything.

| Tool | Endpoint | What it does |
|---|---|---|
| `reddit_set_watch` | `POST /api/reddit/monitor/add` (+ webhook create/update/test) | One call instead of three: compiles a plain-words description into a monitor filter, reads this account's plan capabilities, creates the monitor, then registers, points and test-fires the `deliver_to` webhook. Quoted phrases are taken out of the sentence first, then it is cut into clauses, so each part reads only its own words: subreddits as `r/Name` **or a pasted `reddit.com/r/Name` link**, quoted phrases as keyword terms, `except`/`without`/`ignoring` as exclusions, `at least N upvotes` as a score floor, `comments` / `posts and comments` as the match kind. **It never returns a filter it cannot fully account for** (see below). Returns `understood` and `compiled_filter` so you can check it, and `notes` names anything it dropped, rewrote or read as a whole phrase. |
| `reddit_monitor_add` | `POST /api/reddit/monitor/add` | Create a monitor: subreddits to watch plus an optional filter (keyword, author, domain, include/exclude terms, min score, NSFW). Forward-looking only from creation (or from `baseline_item_id`). |
| `reddit_monitor_list` | `GET /api/reddit/monitor/list` | List every monitor on your account, plus `slots` ({used, total, tier}). |
| `reddit_monitor_update` | `POST /api/reddit/monitor/update` | Pause/resume (`active`), re-cadence, or replace a monitor's filter. Passing any filter field REPLACES the whole filter -- resupply everything you want kept. |
| `reddit_monitor_remove` | `POST /api/reddit/monitor/remove` | Permanently delete a monitor. Cannot be undone. |
| `reddit_monitor_health` | `GET /api/reddit/monitor/health` | Per-monitor delivered/failed/suppressed counts (last 24h), `suppressed_breakdown` splitting those suppressions into `ceiling` and `stale` reasons, and whether the delivery ceiling specifically has been hit. |
| `reddit_monitor_deliveries` | `GET /api/reddit/monitor/deliveries` | The actual posts delivered (or attempted), newest first, with real content -- not just counts. Omit `id` to aggregate across every monitor you own. |
| `reddit_monitor_webhook_create` | `POST /api/reddit/monitor/webhook/create` | Register a delivery target (`webhook`/`slack`/`discord`). Returns a signing secret shown ONCE. |
| `reddit_monitor_webhook_list` | `GET /api/reddit/monitor/webhook/list` | List your webhooks. Never returns the secret. |
| `reddit_monitor_webhook_test` | `POST /api/reddit/monitor/webhook/test` | Send a one-off test delivery to confirm a webhook is wired up correctly. |
| `reddit_monitor_webhook_delete` | `POST /api/reddit/monitor/webhook/delete` | Permanently delete a webhook. Does not cascade-pause monitors still pointing at it. |

#### `reddit_set_watch` refuses rather than guesses

Every character of your description has to be claimed by exactly one thing: a subreddit, a quoted phrase, a keyword, an exclusion, a score clause, a delivery clause, or a word that carries no filter content. Anything left over means the compiler did not understand the whole sentence, and it returns **no filter at all** rather than a monitor built from the half it did understand.

| outcome | when | what you get |
|---|---|---|
| a filter | every word is accounted for | the monitor, plus `understood`, `compiled_filter` and `notes` |
| `partial_understanding` | some words could not be placed, or two parts of the filter counted the same words twice | the exact unplaced words, what *was* understood, and nothing created |
| `contradictory_filter` | the same phrase is both watched for and excluded, so the monitor could never deliver | both halves named, and nothing created |
| `no_anchor` | neither a subreddit nor a keyword | a refusal, before any request is sent |

This matters because a wrong watch is **invisible**: you get a monitor id, a green test delivery, and then either silence forever or a firehose. A refusal that names the words it could not place is recoverable in seconds; a monitor whose keyword is also its exclusion is not noticed for a week. If a description is refused, quote the exact phrase to watch for, write subreddits as `r/Name`, or build the filter directly with `reddit_monitor_add`.

### Playbooks: three recipes, served as MCP resources

Three reusable recipes ship as MCP **resources** rather than tools, because the protocol makes resources application-driven: a host shows them in a picker or a search box and reads one only when it is asked for by URI. Nothing is prefetched and nothing is injected.

| URI | What it covers |
|---|---|
| `playbook://competitor-mention-watch` | Standing up continuous coverage of competitor names and routing every match to a channel, including how to cut the false positives a brand name that is also an ordinary word produces, and how to tell a quiet monitor from a broken one. |
| `playbook://subreddit-audit` | Profiling one community before posting in it: size against real comment rate, the rules as written and as enforced, the moderator team, what endures versus what is working this month, and whether posts survive. |
| `playbook://pain-point-mining` | Finding problems in the words people actually use, from comment bodies rather than post titles, ranking them by distinct authors rather than hits, and turning a one-off sweep into a standing feed. |

Each is a short markdown document built only from calls this package already exposes, so a recipe cannot point at something unbuilt. A test checks every tool each playbook names against the live catalog.

The bodies are documentation and name the calls they are recipes for, which is the point of them. That is a deliberate difference from tool **descriptions**, which a client fetches at connect time and nobody chose: those state product facts only and a gate enforces it. A resource is read only when something asks for its URI, so it is a document you opened, not an injection. The bodies are still checked for hidden or encoded text, for any link off our own hosts, and for adversarial steering (overriding your instructions, concealment, exfiltration, or spending your credits in a loop), each with a planted-defect control.

```jsonc
// resources/list, then
{ "method": "resources/read", "params": { "uri": "playbook://subreddit-audit" } }
```

### Your Reddit account: connect once, then comment, post, vote, DM and schedule

`reddit_connect` stores your Reddit session on our side, encrypted (AES-256-GCM, the key held only by the API), against this API key's account, after checking it with Reddit. Every write after that sends no cookies, so they never sit in your agent's tool calls or transcript. Each write acts publicly as you, so it is marked as a write and your client asks before it runs. Connecting, status and disconnecting are free.

| Tool | Endpoint | What it does |
|------|----------|--------------|
| `reddit_connect` | `POST /api/reddit/session` | Store your Reddit session once (reddit_session and loid cookies; add csrf_token for votes and DMs). A logged-out session is refused. Free. |
| `reddit_session_status` | `GET /api/reddit/session` | Whether a session is connected, its username, and when it was last used. Never returns the cookies. Free. |
| `reddit_disconnect` | `POST /api/reddit/session/delete` | Delete the stored session. Free. |
| `reddit_comment` | `POST /api/reddit/comment` | Comment on a post as you. |
| `reddit_submit` | `POST /api/reddit/submit` | Submit a text or link post to a subreddit as you. |
| `reddit_vote` | `POST /api/reddit/vote` | Upvote, downvote or clear a vote on a post or comment as you. |
| `reddit_dm` | `POST /api/reddit/dm` | Send a private chat message as you. |
| `reddit_scheduled_create` | `POST /api/reddit/schedule` | Schedule a post or a comment for later (`run_at` 60 seconds to 30 days ahead). It runs as your connected session at that time. Free to schedule; billed at the write rate only when the run succeeds. |
| `reddit_scheduled_list` | `GET /api/reddit/schedule` | Your scheduled posts and comments, newest first, with each one's `status`, `result` or `error`, and whether it was `billed`. Free. |
| `reddit_scheduled_cancel` | `POST /api/reddit/schedule/cancel` | Cancel a scheduled post or comment that has not run yet. A cancelled action never runs and is not charged. Free. |

### Feedback: tell the team what broke, after you review the draft

Modelled on Claude Code's own feedback tool. When a call fails in a way that is not your key, credits or a rate limit, when you ask for something no tool covers, or when a result is plainly wrong, the model can **draft** a report into a local queue (`~/.redditapis/feedback-queue.json`, at most 10 drafts, override the directory with `REDDITAPIS_FEEDBACK_DIR`). Nothing is sent until you ask to review the queue and name the drafts to send. Each report carries the last failing call's endpoint, status and request id, your client name and this package's version, so the team can act on it without a follow-up. Use `reddit_feedback_get` with the returned server id to see whether it was triaged, shipped or declined. Both tools are free.

| Tool | Endpoint | What it does |
|------|----------|--------------|
| `reddit_feedback_send` | `POST /feedback` | `action: "draft"` (default) queues a report locally and sends nothing; `"list"` shows the queue; `"send"` posts only the drafts you name; `"discard"` drops them. |
| `reddit_explain` | none (in-process) | Provenance for the rows already fetched in this session: per completed call, the tool, the endpoint, when the response arrived, how old that copy now is, the upstream request id and the response size. The read path holds no response cache, so the age is the age of your copy, not of a stored row. |
| `reddit_account_me` | `GET /account/me` | How much credit this key has left, before spending any. Free, never metered. |
| `reddit_account_payments` | `GET /account/payments` | Every top-up on this key: amount, credits added, status and date, newest first. Free, never metered. |
| `reddit_feedback_get` | `GET /feedback/{id}` | Read a sent report's status (`new`, `triaged`, `shipped`, `declined`) and the team's response. |

## Usage examples

### Research a topic across Reddit

> "What are people saying about the Rust borrow checker this month?"

The agent calls `reddit_search` with:

```
q: "borrow checker"
sort: "relevance"
t: "month"
sort_type: "score"
```

### Read a community's top posts of the week

> "Show me the top posts in r/programming this week."

The agent calls `reddit_subreddit_top` with:

```
name: "programming"
t: "week"
```

To page further, pass the `after` cursor from the response back on the next call: `{ name: "programming", t: "week", after: "<after from response>" }`.

### Find who is talking about a product

> "Which redditors are recommending mechanical keyboards, and what do they say?"

The agent calls `reddit_deep_comment_search` with:

```
q: "mechanical keyboard"
group_by: "author"
sort: "relevance"
```

Research mode returns the distinct people who mentioned the query, ranked by how many of their comments matched, each with their top comment and the subreddits they matched in.

### Vet a subreddit, then read a thread

> "Find a discussion about API rate limiting in r/webdev, check the sub's rules, and read the full thread."

The agent calls `reddit_search` scoped to the community:

```
q: "rate limiting"
subreddit: "webdev"
sort: "relevance"
t: "year"
```

Then `reddit_subreddit_rules` with `{ name: "webdev" }`, and finally `reddit_post_comments` with the `permalink` from a search result, for example `{ permalink: "/r/webdev/comments/abc123/some_title/" }`, to pull the post and its comment tree.

### Set up brand monitoring and check what came in

> "Watch r/SaaS and r/startups for mentions of my product, send matches to my Slack, and show me what's come in so far."

The agent calls `reddit_monitor_webhook_create` with:

```
url: "https://hooks.slack.com/services/..."
kind: "slack"
```

Then `reddit_monitor_add` with:

```
subreddit: ["SaaS", "startups"]
q: "my product name"
```

Later, `reddit_monitor_deliveries` with `{ id: "<monitor id from the add response>" }` returns the actual matching posts sent so far, or `reddit_monitor_health` for just the counts.

## No session needed for reads; monitor/webhook writes need an active plan

Reads need nothing but your API key -- no account linking, login, or cookie step. Reddit write actions (commenting, posting, voting, sending DMs) need one extra step: `reddit_connect` stores your Reddit session on our side, encrypted, so later calls carry no cookies. Until you connect, no tool can post, vote or DM as you, and `reddit_disconnect` deletes the stored session at any time. Monitor/webhook management tools are a different kind of write: they configure your OWN redditapis.com account (an alerting subscription) and require an active monitoring plan for anything that creates or changes state (`reddit_monitor_add`/`update`/`remove`, `reddit_monitor_webhook_create`/`test`/`delete`); reading your own list, health, or delivery history never does.

## Troubleshooting

**`HTTP 401 (invalid or missing API key)`** Check that `REDDITAPIS_KEY` is set correctly in your MCP client config and matches the key from [redditapis.com](https://www.redditapis.com).

**`HTTP 402 (insufficient credits)`** Top up your account at [redditapis.com](https://www.redditapis.com). For a monitor/webhook write specifically, a 402 body of `subscription_required` means there is no active monitoring plan (monitoring has no free tier); `monitor_slots_exhausted` means the plan's monitor slot limit is already in use -- `reddit_monitor_list`'s `slots` field shows used vs total.

**`HTTP 403 (access forbidden)`** The subreddit or user may be private, banned, or quarantined, or your plan may not include this endpoint.

**`HTTP 404 (not found)`** The subreddit, post id, user, or permalink may be wrong or the content may have been deleted or removed.

**`HTTP 429 (rate limited)`** Wait a few seconds and retry, or reduce request frequency. For bulk work, space out calls and raise `REDDITAPIS_TIMEOUT_MS`.

**`HTTP 408` or `HTTP 503` with `upstream_timeout`** The read did not finish in time and was not billed (a 503 means the API's own read budget ran out). The server already retried once after `retry_after` (or the `Retry-After` header), when that retry can still finish inside `REDDITAPIS_TIMEOUT_MS`; call the tool again shortly. A `503 upstream_unavailable` on a read gets the same one retry.

**`Request failed: timed out after 30000ms`** The default timeout is 30 seconds. For large `reddit_deep_comment_search` or paginated fetches, set `REDDITAPIS_TIMEOUT_MS` higher (for example `60000`).

**Tools do not appear in Claude / Cursor** Ensure `npx` is on your PATH and Node.js 18+ is installed (`node --version`). Check your MCP client logs for startup errors.

## Development

```bash
npm install
npm run check   # syntax-check both source files
npm test        # unit-test the tool catalog + query/path builders (no network)
npm start       # run the stdio server (needs REDDITAPIS_KEY)
```

## Links

- Site and API keys: [redditapis.com](https://www.redditapis.com)
- REST API base URL (call it directly, without MCP): `https://api.redditapis.com`

## FAQ

**Do I need a Reddit developer app?** No. One RedditAPIs key covers every read, with no Reddit developer app to register.

**What does it cost?** $0.0015 per read pay as you go, or a monthly read plan from $19 for 30,000 reads; every account gets 1,000 free reads a month, plus $0.50 of free credit at signup and no card. Monitors need a monitoring plan, which has no free tier.

**Can it post, comment or vote?** Yes, after you connect your Reddit account once with `reddit_connect` (your session is stored encrypted on our side and never travels in later calls). `reddit_comment`, `reddit_submit`, `reddit_vote` and `reddit_dm` then act as that account, and each is marked as a write so your client asks before it runs. `reddit_disconnect` deletes the stored session. Of the other tools, the 10 monitor/webhook tools manage your OWN redditapis.com monitors and webhooks (a write, but never a write to Reddit itself), `reddit_set_watch` sets up a whole watch in one call, `reddit_explain` says where the rows you hold came from, the 3 feedback tools send or read a report to the redditapis.com team, and `reddit_account_me` and `reddit_account_payments` read your own balance, plan and top-up history.

**Which clients are supported?** Claude Desktop, Claude Code, Cursor, Windsurf, and VS Code (Copilot agent mode), or any Model Context Protocol client.

**Does it store my key or data?** No. The server holds no state and forwards your API key on each call.

## License

MIT

