Reddit Integration Guide

Technology: reddit · Category: comms · Last reviewed: 2026-08-23

Source: https://tech-stack.codeamanilabs.org/guide/reddit

Insight:

Reddit is a social-listening + community-engagement channel, not a payment or auth rail. The core flow is a plain OAuth2 REST API (no SDK required) — the real constraints are operational and cultural: a mandatory descriptive User-Agent, a 100 QPM per-OAuth-client budget exposed via X-Ratelimit-* headers, and oauth.reddit.com as the base for authed calls. Since the 2023 paid-API shift the free tier is non-commercial only (commercial use needs an approved contract), so treat Reddit as a research/engagement channel, not bulk data. For codeAmani the goal is value-first participation to learn what US users actually need — never astroturf: sockpuppets, vote manipulation, and spammy self-promotion are bannable and burn the codeAmani-Labs account.

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

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).

flowchart LR
  A["Register app<br/>reddit.com/prefs/apps"] -->|"client_id + secret"| B["OAuth2 token<br/>/api/v1/access_token"]
  B -->|"bearer token"| C["oauth.reddit.com"]
  C -->|"GET /r/sub/new · /search"| D["Listen: threads + comments"]
  D --> E["Analyse with Claude<br/>themes · sentiment · pain-points"]
  E --> F["Roadmap / product decisions"]
  C -->|"POST /api/comment · /api/submit"| G["Engage as codeAmani-Labs"]
  E -.->|"value-first reply"| G

Official Documentation

Resource URL
API reference https://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 app https://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 type Use it for Grant
script Server-side bot acting as one account (our brand account) password
web app Acting as other users via OAuth consent; long-lived bots authorization_code (+ refresh token)
installed app Public clients (mobile/SPA), no secret installed_client

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

# .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.

// 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.

// 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.

// 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

// 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

// 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.

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

// 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.

npm install snoowrap   # deprecated — see the warning above
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:

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`);
  }
}
Header Meaning
X-Ratelimit-Used Requests used this window
X-Ratelimit-Remaining Requests left this window
X-Ratelimit-Reset Seconds 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:

Tier Who Limit / cost
Free Personal projects, bots, mod tools, non-commercial/academic research Self-serve, ≤100 QPM per OAuth client, no commercial use
Commercial / enterprise Ad-supported apps, paywalled or monetized products, bulk data Approved contract required (manual review, ~weeks); Reddit's published enterprise rate is ~$0.24 per 1,000 API calls

codeAmani notes

Security

Culture & ToS — this is the part that matters most

AI routing (ties to the AI Routing Policy)

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

Official docs: