| name | reddit-api |
| description | Reddit API with PRAW (Python) and Snoowrap (Node.js) |
| disable-model-invocation | false |
Reddit API Skill
Load with: base.md + [language].md
For integrating Reddit data into applications - fetching posts, comments, subreddits, and user data.
Sources: Reddit API Docs | OAuth2 Wiki | PRAW Docs
Setup
1. Create Reddit App
- Go to https://www.reddit.com/prefs/apps
- Click "Create App" or "Create Another App"
- Fill in:
- Name: Your app name
- App type:
script - For personal use / bots you control
web app - For server-side apps with user auth
installed app - For mobile/desktop apps
- Redirect URI:
http://localhost:8000/callback (for dev)
- Note your
client_id (under app name) and client_secret
2. Environment Variables
REDDIT_CLIENT_ID=your_client_id
REDDIT_CLIENT_SECRET=your_client_secret
REDDIT_USER_AGENT=YourApp/1.0 by YourUsername
REDDIT_USERNAME=your_username
REDDIT_PASSWORD=your_password
User-Agent Format: <platform>:<app_id>:<version> (by /u/<username>)
Rate Limits
| Tier | Limit | Notes |
|---|
| OAuth authenticated | 100 QPM | Per OAuth client ID |
| Non-authenticated | Blocked | Must use OAuth |
- Limits averaged over 10-minute window
- Include
User-Agent header to avoid blocks
- Respect
X-Ratelimit-* response headers
Python: PRAW (Recommended)
Installation
pip install praw
uv add praw
Script App (Personal Use / Bots)
import praw
from pydantic_settings import BaseSettings
class RedditSettings(BaseSettings):
reddit_client_id: str
reddit_client_secret: str
reddit_user_agent: str
reddit_username: str
reddit_password: str
class Config:
env_file = ".env"
settings = RedditSettings()
reddit = praw.Reddit(
client_id=settings.reddit_client_id,
client_secret=settings.reddit_client_secret,
user_agent=settings.reddit_user_agent,
username=settings.reddit_username,
password=settings.reddit_password,
)
print(f"Logged in as: {reddit.user.me()}")
Read-Only (No User Auth)
import praw
reddit = praw.Reddit(
client_id="your_client_id",
client_secret="your_client_secret",
user_agent="YourApp/1.0 by YourUsername",
)
reddit.read_only = True
Common Operations
subreddit = reddit.subreddit("python")
for post in subreddit.hot(limit=10):
print(f"{post.title} - {post.score} upvotes")
for post in subreddit.new(limit=10):
print(post.title)
for post in subreddit.search("pydantic", limit=5):
print(post.title)
submission = reddit.submission(id="abc123")
print(submission.title)
print(submission.selftext)
submission.comments.replace_more(limit=0)
for comment in submission.comments.list():
print(f"{comment.author}: {comment.body[:100]}")
Posting & Voting (Requires Auth)
subreddit = reddit.subreddit("test")
submission = subreddit.submit(
title="Test Post",
selftext="This is the body of my post."
)
submission = subreddit.submit(
title="Check this out",
url="https://example.com"
)
submission.upvote()
submission.downvote()
submission.clear_vote()
submission.reply("Great post!")
comment = reddit.comment(id="xyz789")
comment.reply("I agree!")
Streaming (Real-time)
for post in reddit.subreddit("python").stream.submissions():
print(f"New post: {post.title}")
for comment in reddit.subreddit("python").stream.comments():
print(f"New comment by {comment.author}: {comment.body[:50]}")
User Data
user = reddit.redditor("spez")
print(f"Karma: {user.link_karma + user.comment_karma}")
for post in user.submissions.new(limit=5):
print(post.title)
for comment in user.comments.new(limit=5):
print(comment.body[:100])
TypeScript / Node.js: Snoowrap
Installation
npm install snoowrap
pnpm add snoowrap
Setup
import Snoowrap from "snoowrap";
const reddit = new Snoowrap({
userAgent: "YourApp/1.0 by YourUsername",
clientId: process.env.REDDIT_CLIENT_ID!,
clientSecret: process.env.REDDIT_CLIENT_SECRET!,
username: process.env.REDDIT_USERNAME!,
password: process.env.REDDIT_PASSWORD!,
});
reddit.config({
requestDelay: 1000,
continueAfterRatelimitError: true,
});
Common Operations
const posts = await reddit.getSubreddit("typescript").getHot({ limit: 10 });
posts.forEach((post) => {
console.log(`${post.title} - ${post.score} upvotes`);
});
const results = await reddit.getSubreddit("programming").search({
query: "typescript",
sort: "relevance",
time: "month",
limit: 10,
});
const submission = await reddit.getSubmission("abc123").fetch();
console.log(submission.title);
const comments = await submission.comments.fetchAll();
comments.forEach((comment) => {
console.log(`${comment.author.name}: `);
});
Posting
const post = await reddit.getSubreddit("test").submitSelfpost({
title: "Test Post",
text: "This is the body.",
});
const linkPost = await reddit.getSubreddit("test").submitLink({
title: "Check this out",
url: "https://example.com",
});
await post.upvote();
await post.reply("Great post!");
Direct API (No Library)
Python with httpx
import httpx
import base64
from pydantic import BaseModel
class RedditClient:
def __init__(self, client_id: str, client_secret: str, user_agent: str):
self.client_id = client_id
self.client_secret = client_secret
self.user_agent = user_agent
self.access_token: str | None = None
self.client = httpx.AsyncClient()
async def authenticate(self) -> None:
"""Get application-only OAuth token."""
auth = base64.b64encode(
f"{self.client_id}:{self.client_secret}".encode()
).decode()
response = await self.client.post(
"https://www.reddit.com/api/v1/access_token",
headers={
"Authorization": f"Basic {auth}",
"User-Agent": self.user_agent,
},
data={
"grant_type": "client_credentials",
},
)
response.raise_for_status()
self.access_token = response.json()["access_token"]
async def () -> []:
.access_token:
.authenticate()
response = .client.get(
,
headers={
: ,
: .user_agent,
},
params={: limit},
)
response.raise_for_status()
[post[] post response.json()[][]]
() -> :
.client.aclose()
():
client = RedditClient(
client_id=,
client_secret=,
user_agent=,
)
:
posts = client.get_posts(, limit=)
post posts:
()
:
client.close()
TypeScript with fetch
interface RedditPost {
title: string;
score: number;
url: string;
selftext: string;
author: string;
created_utc: number;
}
class RedditClient {
private accessToken: string | null = null;
constructor(
private clientId: string,
private clientSecret: string,
private userAgent: string
) {}
async authenticate(): Promise<void> {
const auth = Buffer.from(`${this.clientId}:${this.clientSecret}`).toString("base64");
const response = await fetch("https://www.reddit.com/api/v1/access_token", {
method: "POST",
headers: {
: ,
: .,
: ,
},
: ,
});
data = response.();
. = data.;
}
(: , sort = , limit = ): <[]> {
(!.) .();
response = (
,
{
: {
: ,
: .,
},
}
);
data = response.();
data...( child.);
}
}
OAuth2 Web Flow (User Authorization)
For apps where users log in with their Reddit account:
from fastapi import FastAPI, Request
from fastapi.responses import RedirectResponse
import httpx
import secrets
app = FastAPI()
state_store: dict[str, bool] = {}
REDDIT_CLIENT_ID = "your_client_id"
REDDIT_CLIENT_SECRET = "your_client_secret"
REDIRECT_URI = "http://localhost:8000/callback"
@app.get("/login")
async def login():
state = secrets.token_urlsafe(16)
state_store[state] = True
auth_url = (
f"https://www.reddit.com/api/v1/authorize"
f"?client_id={REDDIT_CLIENT_ID}"
f"&response_type=code"
f"&state={state}"
f"&redirect_uri={REDIRECT_URI}"
f"&duration=permanent"
f"&scope=identity read submit vote"
)
return RedirectResponse(auth_url)
@app.get("/callback")
async def callback(code: str, state: str):
if state not in state_store:
return {"error": "Invalid state"}
del state_store[state]
httpx.AsyncClient() client:
response = client.post(
,
auth=(REDDIT_CLIENT_ID, REDDIT_CLIENT_SECRET),
data={
: ,
: code,
: REDIRECT_URI,
},
headers={: },
)
tokens = response.json()
{: tokens[][:] + }
Available Scopes
| Scope | Description |
|---|
identity | Access username and signup date |
read | Access posts and comments |
submit | Submit links and comments |
vote | Upvote/downvote content |
edit | Edit posts and comments |
history | Access voting history |
subscribe | Manage subreddit subscriptions |
mysubreddits | Access subscribed subreddits |
privatemessages | Access private messages |
save | Save/unsave content |
Full list: https://www.reddit.com/api/v1/scopes
Project Structure
project/
├── src/
│ ├── reddit/
│ │ ├── __init__.py
│ │ ├── client.py # Reddit client wrapper
│ │ ├── models.py # Pydantic models for posts/comments
│ │ └── scraper.py # Data collection logic
│ └── main.py
├── .env
└── pyproject.toml
Pydantic Models
from pydantic import BaseModel
from datetime import datetime
class RedditPost(BaseModel):
id: str
title: str
author: str
subreddit: str
score: int
upvote_ratio: float
url: str
selftext: str
created_utc: datetime
num_comments: int
is_self: bool
@classmethod
def from_praw(cls, submission) -> "RedditPost":
return cls(
id=submission.id,
title=submission.title,
author=str(submission.author),
subreddit=submission.subreddit.display_name,
score=submission.score,
upvote_ratio=submission.upvote_ratio,
url=submission.url,
selftext=submission.selftext,
created_utc=datetime.fromtimestamp(submission.created_utc),
num_comments=submission.num_comments,
is_self=submission.is_self,
)
class RedditComment(BaseModel):
id: str
author: str
body: str
score: int
created_utc: datetime
parent_id: str
is_submitter: bool
Anti-Patterns
- No User-Agent - Reddit blocks requests without proper User-Agent
- Ignoring rate limits - Respect 100 QPM, check
X-Ratelimit-* headers
- Storing credentials in code - Use environment variables
- Not handling
MoreComments - Use replace_more() in PRAW
- Polling instead of streaming - Use
.stream for real-time data
- No error handling - Handle 429 (rate limit), 403 (forbidden), 404 (not found)
Quick Reference
pip install praw
npm install snoowrap
python -c "import praw; r = praw.Reddit(...); print(r.user.me())"
Endpoints
| Operation | Endpoint |
|---|
| Auth token | POST https://www.reddit.com/api/v1/access_token |
| API requests | https://oauth.reddit.com/... |
| Subreddit posts | GET /r/{subreddit}/{sort} |
| Submission | GET /comments/{id} |
| User info | GET /user/{username}/about |
| Submit post | POST /api/submit |
| Vote | POST /api/vote |