← Back to dashboard

Reddit Integration Guide

What is Reddit?

The real model

OAuth2 REST API at `oauth.reddit.com`: listen via listings/search, engage via `/api/comment` + `/api/submit`.

Mint a ~1h token at `/api/v1/access_token` (HTTP Basic `client_id:secret`; `client_credentials` for read-only, `password` for a script-app brand account), then call `oauth.reddit.com` with a **descriptive User-Agent** (generic ones get throttled) inside a **100 QPM** per-OAuth-client budget surfaced by `X-Ratelimit-*`. Fullnames matter: `t3_`=post, `t1_`=comment. For codeAmani this is the ingestion edge of voice-of-customer research — pull US-subreddit threads, cluster them with Claude into ranked pain-points to feed the roadmap, then engage value-first. Free tier is non-commercial only (commercial/bulk use needs an approved contract). No sockpuppets or vote manipulation: it's bannable and burns the codeAmani-Labs account.

Six Reddit primitives

One OAuth token, then read to listen and write to engage — all over `oauth.reddit.com`.

Text
██████╗ ███████╗██████╗ ██████╗ ██╗████████╗
██╔══██╗██╔════╝██╔══██╗██╔══██╗██║╚══██╔══╝
██████╔╝█████╗  ██║  ██║██║  ██║██║   ██║   
██╔══██╗██╔══╝  ██║  ██║██║  ██║██║   ██║   
██║  ██║███████╗██████╔╝██████╔╝██║   ██║   
╚═╝  ╚═╝╚══════╝╚═════╝ ╚═════╝ ╚═╝   ╚═╝   

Reddit Integration Guide

Focus: Use the Reddit Data API as a market-research and community-engagement channel — listen to what users in our target subreddits actually ask for, surface threads codeAmani can genuinely answer, and grow the codeAmani-Labs account as a trusted voice (not a billboard).

Overview

Reddit is a forum-of-forums: thousands of topic communities ("subreddits") where people ask real questions and complain about real problems in their own words. That makes it one of the highest-signal voice-of-customer sources on the open web — and a place to build reputation by being helpful before being promotional.

The Reddit Data API is a JSON/REST API authenticated with OAuth2. There is no required SDK — every endpoint is reachable with fetch — but snoowrap (Node) and praw (Python) wrap auth, rate limiting, and pagination for you.

The codeAmani loop is listen → analyse → engage: pull threads, cluster them with Claude into themes/pain-points that inform the roadmap, then reply with genuine value (and only sometimes a soft, rule-compliant mention).

Official Documentation

ResourceURL
API referencehttps://www.reddit.com/dev/api
Data API Wiki (current rules, rate limits, access tiers)https://support.reddithelp.com/hc/en-us/articles/16160319875092-Reddit-Data-API-Wiki
OAuth2 flow (legacy archive wiki)https://github.com/reddit-archive/reddit/wiki/OAuth2
API access rules / User-Agent (legacy archive wiki)https://github.com/reddit-archive/reddit/wiki/API
Register an apphttps://www.reddit.com/prefs/apps
snoowrap (Node wrapper — deprecated)https://github.com/not-an-aardvark/snoowrap
PRAW (Python wrapper)https://praw.readthedocs.io/

Heads-up on the archive wiki: reddit-archive/…/wiki/API still states the old 60 req/min figure. The current, authoritative rate limit is 100 QPM per OAuth client — always cross-check the Data API Wiki above for live rules.


1. Register an app

At https://www.reddit.com/prefs/apps (logged in as codeAmani-Labs), create an app. The type decides the OAuth flow:

App typeUse it forGrant
scriptServer-side bot acting as one account (our brand account)password
web appActing as other users via OAuth consent; long-lived botsauthorization_code (+ refresh token)
installed appPublic clients (mobile/SPA), no secretinstalled_client

You receive a client ID (under the app name) and a client secret. Store both server-side only.

Bash
# .env.local — never commit, never ship to the browser
REDDIT_CLIENT_ID=...
REDDIT_CLIENT_SECRET=...
REDDIT_USERNAME=codeAmani-Labs
REDDIT_PASSWORD=...                 # only for the script-app password grant
# Descriptive User-Agent is MANDATORY — generic ones get throttled/blocked.
REDDIT_USER_AGENT="web:com.codeamanilabs.listener:v1.0 (by /u/codeAmani-Labs)"

User-Agent format (from the API rules): <platform>:<app ID>:<version> (by /u/<username>). Reddit aggressively rate-limits or blocks default library agents (axios/x.y, python-requests). Always send a unique, descriptive UA.


2. Get an OAuth2 token

The token endpoint is POST https://www.reddit.com/api/v1/access_token, authenticated with HTTP Basic (client_id:client_secret). Tokens last ~1 hour — cache and refresh before expiry.

Read-only "listening" token (application-only)

Best for the listen/analyse half of the loop — no account actions, just reads.

TypeScript
// lib/reddit-auth.ts
const TOKEN_URL = "https://www.reddit.com/api/v1/access_token";

export async function getAppToken(): Promise<string> {
  const basic = Buffer.from(
    `${process.env.REDDIT_CLIENT_ID}:${process.env.REDDIT_CLIENT_SECRET}`
  ).toString("base64");

  const res = await fetch(TOKEN_URL, {
    method: "POST",
    headers: {
      Authorization: `Basic ${basic}`,
      "Content-Type": "application/x-www-form-urlencoded",
      "User-Agent": process.env.REDDIT_USER_AGENT!,
    },
    // Confidential clients (script / web app) use client_credentials.
    // Public "installed" apps use grant_type=https://oauth.reddit.com/grants/installed_client&device_id=...
    body: new URLSearchParams({ grant_type: "client_credentials" }),
  });

  if (!res.ok) throw new Error(`Reddit token failed: ${res.status}`);
  const { access_token } = (await res.json()) as { access_token: string };
  return access_token;
}

Posting token (script app, password grant)

Use this to act as codeAmani-Labs (comment / submit). The password grant only works for the developer account that owns a script-type app.

TypeScript
// Swap the body for the password grant; everything else is identical.
body: new URLSearchParams({
  grant_type: "password",
  username: process.env.REDDIT_USERNAME!,
  password: process.env.REDDIT_PASSWORD!,
}),

For a robust long-running bot prefer a web app + authorization_code flow with duration=permanent to obtain a refresh token, so you never store the account password. The password grant is the quickest path for a single brand account.


3. Authenticated requests → oauth.reddit.com

Once you hold a token, all API calls go to https://oauth.reddit.com (not www.reddit.com), with a bearer token and your UA. Modhashes are not needed under OAuth.

TypeScript
// lib/reddit.ts
const API = "https://oauth.reddit.com";

async function redditGet(path: string, token: string) {
  const res = await fetch(`${API}${path}`, {
    headers: {
      Authorization: `bearer ${token}`,
      "User-Agent": process.env.REDDIT_USER_AGENT!,
    },
  });
  // Respect the budget — see §5.
  logRateLimit(res.headers);
  if (!res.ok) throw new Error(`Reddit GET ${path} → ${res.status}`);
  return res.json();
}

Listen: newest posts in a subreddit

TypeScript
// GET /r/:subreddit/new  — limit ≤ 100, paginate with `after`
const data = await redditGet("/r/SaaS/new?limit=50", token);
const posts = data.data.children.map((c: any) => ({
  fullname: c.data.name,        // e.g. "t3_abc123" — the post's fullname
  title: c.data.title,
  body: c.data.selftext,
  url: `https://reddit.com${c.data.permalink}`,
  score: c.data.score,
  numComments: c.data.num_comments,
}));

Listen: search for threads we can answer

TypeScript
// Search ALL of Reddit
await redditGet(`/search?q=${encodeURIComponent("m-pesa integration nextjs")}&sort=new&limit=25`, token);

// Search WITHIN one subreddit (restrict_sr=true)
await redditGet(`/r/Kenya/search?q=mpesa+api&restrict_sr=true&sort=relevance&limit=25`, token);

// Find relevant communities to monitor
await redditGet(`/subreddits/search?q=saas&sort=relevance&limit=10`, token);

Engage: comment on a thread

thing_id is the fullname of the parent (t3_ = post, t1_ = comment). Requires the submit scope and a posting token.

TypeScript
async function redditPost(path: string, token: string, form: Record<string, string>) {
  const res = await fetch(`https://oauth.reddit.com${path}`, {
    method: "POST",
    headers: {
      Authorization: `bearer ${token}`,
      "Content-Type": "application/x-www-form-urlencoded",
      "User-Agent": process.env.REDDIT_USER_AGENT!,
    },
    body: new URLSearchParams(form),
  });
  if (!res.ok) throw new Error(`Reddit POST ${path} → ${res.status}`);
  return res.json();
}

// Reply to a post
await redditPost("/api/comment", token, {
  api_type: "json",
  thing_id: "t3_abc123",
  text: "Here's how we solved the STK Push idempotency problem…", // raw markdown
});

Engage: submit a self-post

TypeScript
// kind=self for a text post; kind=link with `url` for a link post.
await redditPost("/api/submit", token, {
  api_type: "json",
  sr: "kenya",
  kind: "self",
  title: "We open-sourced a Daraja M-Pesa helper for Next.js",
  text: "After shipping a few M-Pesa flows, here's what we learned…",
});

Pre-validate before submitting. GET /api/v1/{subreddit}/post_requirements returns mod rules (min/max title length, required flair, blacklisted words, allowed domains). Check it first to avoid auto-removals — and to respect the community.


4. snoowrap quickstart (Node)

A wrapper can hand-roll less auth + pagination. snoowrap handles tokens, the rate-limit budget, and listings.

⚠️ snoowrap is deprecated. The npm package (snoowrap@1.23.0, last real release ~2020) is now flagged "no longer supported" and its bundled types track Reddit's older OAuth response shapes — the compiler will confidently lie as the API drifts. For new codeAmani code prefer the plain-fetch path in §2–§3 (one bearer token, your own types); reach for snoowrap only for a quick throwaway script.

Bash
npm install snoowrap   # deprecated — see the warning above
TypeScript
import Snoowrap from "snoowrap";

const r = new Snoowrap({
  userAgent: process.env.REDDIT_USER_AGENT!,
  clientId: process.env.REDDIT_CLIENT_ID!,
  clientSecret: process.env.REDDIT_CLIENT_SECRET!,
  username: process.env.REDDIT_USERNAME!,
  password: process.env.REDDIT_PASSWORD!,
});

// Listen
const newPosts = await r.getSubreddit("SaaS").getNew({ limit: 50 });
const hits = await r.search({ query: "mpesa api", sort: "new", time: "week" });

// Engage
await r.getSubmission("abc123").reply("Genuinely useful answer here…");

snoowrap is unmaintained (see the warning above), so on Node the plain-fetch path ages better. For pure data-collection / analysis pipelines, PRAW is the actively-maintained Python option and pairs naturally with pandas/Claude for theme clustering. PRAW is now on the 8.x line (pip install praw → praw==8.0.3, requires Python 3.10+); the 8.0 major dropped Python 3.8/3.9, made most listing/submit arguments keyword-only, and merged submit_image/submit_video/submit_gallery into a single submit().


5. Rate limits & resilience

OAuth clients get 100 queries/minute (QPM) per OAuth client ID, averaged over a rolling 10-minute window (so short bursts are fine). Unauthenticated / non-OAuth traffic is capped at 10 QPM and is effectively blocked for anything real — always send OAuth. (The old 60/min number you'll still see on the archived wiki predates the 2023 API changes; ignore it.) Every response carries the live budget — read it and back off rather than hammering:

TypeScript
function logRateLimit(h: Headers) {
  const remaining = Number(h.get("x-ratelimit-remaining") ?? "100");
  const reset = Number(h.get("x-ratelimit-reset") ?? "0"); // seconds until window reset
  if (remaining < 5) {
    // Sleep until the window resets instead of risking a 429.
    console.warn(`Reddit budget low: ${remaining} left, reset in ${reset}s`);
  }
}
HeaderMeaning
X-Ratelimit-UsedRequests used this window
X-Ratelimit-RemainingRequests left this window
X-Ratelimit-ResetSeconds until the window resets

On 429, honour X-Ratelimit-Reset (or Retry-After) and retry with backoff. Cache tokens (~1 h) and listing results — listening doesn't need to be real-time.

Access tiers & pricing (post-2023)

Since Reddit's 2023 API changes the Data API is tiered — the guide's default use (voice-of-customer research on one brand account) sits comfortably in the free tier, but know where the line is:

TierWhoLimit / cost
FreePersonal projects, bots, mod tools, non-commercial/academic researchSelf-serve, ≤100 QPM per OAuth client, no commercial use
Commercial / enterpriseAd-supported apps, paywalled or monetized products, bulk dataApproved contract required (manual review, ~weeks); Reddit's published enterprise rate is ~$0.24 per 1,000 API calls
  • Don't scrape at scale or resell Reddit content — that's the commercial line and needs approval. Store only what the research needs (see the Data API Terms).

codeAmani notes

Security

  • All credentials (client_id, client_secret, account password / refresh token) live server-side only — in .env.local / Vercel env vars, never in a client bundle or a NEXT_PUBLIC_* var. Do Reddit calls from API routes or background jobs.
  • Never log tokens or passwords. Treat the refresh token like a password.

Culture & ToS — this is the part that matters most

  • The brief is to "promote without explicitly saying so." On Reddit that means be genuinely helpful first; a relevant link is welcome only when it actually answers the question. Overt marketing, repeated drops of the same link, and thin self-promo get removed and can ban the account.
  • No sockpuppets, no vote manipulation, no astroturfing — all are site-wide-rule violations and the fastest way to lose the codeAmani-Labs account. One authentic account, real participation.
  • Respect each subreddit's self-promotion rules and Reddiquette (a common norm: keep self-promotion well under ~10% of your activity). Read the sidebar/rules before posting; use post_requirements to pre-check.
  • The Reddit Data API Terms govern commercial and bulk use — register your app, stay within rate limits, and store only the data you need. Don't redistribute scraped user content.

AI routing (ties to the AI Routing Policy)

  • Pipe collected titles/bodies/comments into Claude for sentiment, theme clustering, and pain-point extraction — turn raw threads into a ranked list of "what users keep asking for." Use OpenAI structured output if you want strict JSON tags per thread. This is the bridge from listening to roadmap.

Market fit (US-first, Kenya per-project)

  • codeAmani is US-first, so default monitoring to US-relevant communities — e.g. r/SaaS, r/smallbusiness, r/Entrepreneur, r/webdev — to learn what US consumers/SMBs want from AI tooling.
  • For Kenya-targeted projects, monitor r/Kenya, r/Nairobi, and fintech/dev threads to validate M-Pesa / low-bandwidth assumptions straight from users.