Reddit APIPythonTutorialPRAW Alternative

Reddit API in Python: The Complete No-PRAW Tutorial

Use the Reddit API in Python without PRAW — plain HTTP with requests or httpx. Code examples for posts, comments, search, votes, and DMs from $0.002 per call.

RedditAPI·
Reddit API in Python tutorial — no-PRAW, no-OAuth path using plain requests

Most Python tutorials on Reddit start the same way: install PRAW, register a Reddit developer app, copy a client ID and secret into your code, then bolt on an OAuth flow. That works fine for hobby projects. It breaks at the production line.

Reddit's free OAuth tier explicitly forbids commercial use, throttles you to 100 queries per minute, and caps you at 10,000 calls per month. Going commercial means signing the Standard tier — $12,000 per year minimum plus roughly $0.024 per call at scale. Most teams hit one of those walls long before they ship.

This tutorial does the same thing the other guides do — fetch posts, walk comment trees, search, comment, vote, send DMs — but with plain requests and one bearer token. No developer app. No OAuth dance. No 10K monthly cap. Code is copy-paste runnable at $0.002 per read call with $0.50 in free credits on every new account.

Why not just use PRAW?

PRAW (the Python Reddit API Wrapper) is well-maintained and handles a real problem: Reddit's API is verbose, the auth flow is fiddly, and the rate-limit headers are easy to misread. PRAW papers all of that over with import praw.

What PRAW doesn't paper over is the layer underneath it — Reddit's API itself. PRAW sits on Reddit's OAuth tier, which means:

  • Free tier: 100 QPM with OAuth · 10K monthly cap · no commercial use allowed
  • Standard tier: $12K/year minimum · 100 RPM at base · scales with payment tier
  • Commercial calls beyond Standard: roughly $0.24 per 1,000 calls
  • App-review queue: 2–4 weeks for write scopes (post, vote, DM)

If you're building anything that touches money — a SaaS, an internal tool with paying users, an agency dashboard — the free tier is off the table by Reddit's own terms. And Standard at $12K/year is steep for any project that hasn't validated revenue yet.

The alternative is to skip Reddit's OAuth layer entirely and call a third-party Reddit API. That's what this tutorial covers. RedditAPI exposes the same endpoints PRAW would call, but with one bearer token, pay-per-call pricing from $0.002, no developer-app review, and no monthly cap.

Quick start: Reddit API in Python in 5 lines

You don't need a wrapper, an SDK, or an async runtime. Plain requests works:

import os
import requests

API_KEY = os.environ["REDDITAPI_KEY"]
BASE = "https://api.redditapis.com"

response = requests.get(
    f"{BASE}/api/reddit/posts",
    params={"sub": "python", "sort": "top", "t": "week"},
    headers={"Authorization": f"Bearer {API_KEY}"},
)
posts = response.json()
print(f"Fetched {len(posts.get('data', []))} top posts from r/python this week")

Five lines of actual work. No client ID, no client secret, no OAuth refresh tokens. Your REDDITAPI_KEY is the only credential — generate one at signup and pass it as a bearer token on every request.

If you prefer httpx for async or HTTP/2 support, swap the import — the request shape is identical:

import os
import httpx

API_KEY = os.environ["REDDITAPI_KEY"]
BASE = "https://api.redditapis.com"

async def fetch_top():
    async with httpx.AsyncClient() as client:
        r = await client.get(
            f"{BASE}/api/reddit/posts",
            params={"sub": "python", "sort": "top", "t": "week"},
            headers={"Authorization": f"Bearer {API_KEY}"},
        )
        return r.json()

Same for aiohttp, urllib3, or whatever HTTP client your codebase already uses. The Reddit API in Python doesn't need a dedicated wrapper — it's just JSON over HTTPS.

Start building with RedditAPI

Reads $0.002, votes $0.005, writes $0.012, DMs $0.025. $0.50 free credits.

Reddit API Python examples

These are the operations every Reddit-API project ends up doing. Each is one HTTP call, returning structured JSON.

Fetch posts from a subreddit

def get_subreddit_posts(sub: str, sort: str = "hot", limit: int = 25):
    r = requests.get(
        f"{BASE}/api/reddit/posts",
        params={"sub": sub, "sort": sort, "limit": limit},
        headers={"Authorization": f"Bearer {API_KEY}"},
    )
    r.raise_for_status()
    return r.json()["data"]

posts = get_subreddit_posts("MachineLearning", sort="top")
for p in posts:
    print(f"{p['score']:>6}  {p['title']}")

sort accepts hot, new, top, rising, controversial. For top and controversial, pass t (timeframe) as hour, day, week, month, year, or all.

Walk a comment tree

def get_comments(post_id: str):
    r = requests.get(
        f"{BASE}/api/reddit/comments",
        params={"post_id": post_id},
        headers={"Authorization": f"Bearer {API_KEY}"},
    )
    return r.json()

tree = get_comments("1abc123")
def walk(comments, depth=0):
    for c in comments:
        print(f"{'  ' * depth}{c['author']}: {c['body'][:80]}")
        walk(c.get("replies", []), depth + 1)
walk(tree["data"])

The endpoint returns the full nested tree in one response — no pagination required for typical threads.

Search across subreddits

def search_reddit(query: str, sort: str = "relevance"):
    r = requests.get(
        f"{BASE}/api/reddit/search",
        params={"q": query, "sort": sort, "limit": 100},
        headers={"Authorization": f"Bearer {API_KEY}"},
    )
    return r.json()["data"]

mentions = search_reddit("\"my product name\"")

sort accepts relevance, top, new, comments. For brand monitoring, combine new sort with a polling loop.

Get a user profile

def get_user(username: str):
    r = requests.get(
        f"{BASE}/api/reddit/user/{username}",
        headers={"Authorization": f"Bearer {API_KEY}"},
    )
    return r.json()["data"]

Returns karma, account age, comment count, and recent activity timestamps — enough to filter out spam accounts in a lead-gen pipeline.

Post a comment

def post_comment(parent_id: str, body: str):
    r = requests.post(
        f"{BASE}/api/reddit/comment",
        json={"parent_id": parent_id, "body": body},
        headers={"Authorization": f"Bearer {API_KEY}"},
    )
    r.raise_for_status()
    return r.json()

parent_id is either a post ID (t3_xxxxx) or a comment ID (t1_yyyyy). Comments cost $0.012 per call. No developer-app approval needed.

Vote on a post or comment

def vote(thing_id: str, direction: int):
    """direction: 1 (upvote), -1 (downvote), 0 (clear)"""
    r = requests.post(
        f"{BASE}/api/reddit/vote",
        json={"id": thing_id, "dir": direction},
        headers={"Authorization": f"Bearer {API_KEY}"},
    )
    return r.json()

Send a DM

def send_dm(to: str, subject: str, body: str):
    r = requests.post(
        f"{BASE}/api/reddit/dm",
        json={"to": to, "subject": subject, "text": body},
        headers={"Authorization": f"Bearer {API_KEY}"},
    )
    return r.json()

DMs are $0.025 per call. PRAW can technically do this too, but you'd need a Reddit account with verified email and the privatemessages OAuth scope — RedditAPI handles those headers for you.

Comparing Python Reddit API wrappers

Three real-world options for using the Reddit API in Python:

Approach Setup time Commercial use Monthly cap Write scopes
PRAW + Reddit free tier ~30 min (dev-app, OAuth) ❌ forbidden by Reddit ToS 10,000 calls needs scope review
PRAW + Reddit Standard tier ~2–4 weeks (dev-app approval) ✅ allowed metered included
RedditAPI + requests ~2 min (signup) ✅ allowed none included with vote/write/DM tiers

PRAW is the right choice for academic research, personal bots, and learning. For anything that touches paying users, a managed third-party API is faster to ship and cheaper to scale at low-to-medium volume. Above ~5M calls/month, it's worth re-running the cost calculator to compare both paths at your specific volume.

The cheapest Reddit API. Try it free.

Reads from $0.002 per call. $0.50 free credits. No credit card required.

How much will this cost?

Four tiers, one bearer token. Cost per call:

  • Reads (any GET): $0.002
  • Votes (POST /vote): $0.005
  • Writes (comments, login, profile updates): $0.012
  • DMs (POST /dm*): $0.025

Three concrete scenarios for a typical Python pipeline:

# Scenario 1: brand monitoring (read-heavy)
# 500 search calls / day × 30 days = 15,000 reads
monthly_cost = 15_000 * 0.002
print(f"Brand monitoring: ${monthly_cost:.2f}/month")  # $30.00

# Scenario 2: ML training corpus collection (read-only, one-shot)
# 200,000 posts + 1,000,000 comment-tree fetches
one_shot_cost = (200_000 * 0.002) + (1_000_000 * 0.002)
print(f"ML corpus: ${one_shot_cost:.2f} one-time")  # $2,400

# Scenario 3: comment-reply bot (mixed)
# 50,000 reads + 500 comment posts / month
mixed_cost = (50_000 * 0.002) + (500 * 0.012)
print(f"Reply bot: ${mixed_cost:.2f}/month")  # $106

The official Reddit Data API at the Commercial tier costs roughly $0.024 per call regardless of method on top of the $12K/year minimum. At the comment-bot scenario above, Reddit's official path would cost roughly $12,000 + (50,500 × $0.024) = $13,212. The same workload on RedditAPI runs $106.

If your volume is high enough that those numbers cross, the calculator does the math both ways.

Production patterns: retries, async, error handling

Three things every production Reddit API Python project ends up needing.

Idempotent retries on transient failures

Reddit's upstream occasionally returns 502 or 503 under load. RedditAPI surfaces those transparently — wrap your call site:

import time
from requests.exceptions import HTTPError

def reddit_get_with_retry(path: str, params: dict, max_retries: int = 3):
    for attempt in range(max_retries):
        try:
            r = requests.get(
                f"{BASE}{path}",
                params=params,
                headers={"Authorization": f"Bearer {API_KEY}"},
                timeout=15,
            )
            r.raise_for_status()
            return r.json()
        except HTTPError as e:
            if e.response.status_code in (502, 503, 504) and attempt < max_retries - 1:
                time.sleep(2 ** attempt)  # exponential backoff
                continue
            raise

GET calls are safe to retry. POST calls (comment, vote, DM) are not automatically idempotent — re-running a comment-post creates a duplicate comment. Track the request ID server-side or use an in-app dedupe key before retrying.

Async at scale

For corpus collection or sustained search polling, plain requests blocks the event loop. Use httpx.AsyncClient with asyncio.gather:

import asyncio
import httpx
import os

API_KEY = os.environ["REDDITAPI_KEY"]
BASE = "https://api.redditapis.com"
HEADERS = {"Authorization": f"Bearer {API_KEY}"}

async def fetch_one(client, sub):
    r = await client.get(f"{BASE}/api/reddit/posts", params={"sub": sub, "limit": 100}, headers=HEADERS)
    return sub, r.json()

async def fetch_many(subs):
    async with httpx.AsyncClient(timeout=30) as client:
        results = await asyncio.gather(*(fetch_one(client, s) for s in subs))
        return dict(results)

subs = ["python", "MachineLearning", "datascience", "learnpython"]
data = asyncio.run(fetch_many(subs))

Four subreddits, one round trip. No platform-level rate caps means you can scale this to 50+ concurrent fetches without hitting a wall.

Logging cost as you go

Track spend per script so you don't surprise yourself at month-end:

import json
from pathlib import Path

COST_LOG = Path(".reddit_cost.json")

def log_call(method: str, path: str):
    cost = {"GET": 0.002, "VOTE": 0.005, "WRITE": 0.012, "DM": 0.025}
    normalized_path = path.rstrip("/")
    if method == "GET":
        tier = "GET"
    elif normalized_path.endswith("/vote"):
        tier = "VOTE"
    elif "/dm" in normalized_path:
        tier = "DM"
    else:
        tier = "WRITE"

    delta = cost[tier]
    total = json.loads(COST_LOG.read_text()) if COST_LOG.exists() else {"total": 0}
    total["total"] += delta
    COST_LOG.write_text(json.dumps(total))
    return delta

Or skip the local file and use the X-Credits-Remaining header that every RedditAPI response includes — it tells you the running balance after each call.

Where to go from here

This tutorial covers the read and write surface of the Reddit API in Python with plain HTTP. A few links for what's next:

Frequently asked questions.

You don't need one. The Reddit API in Python is just JSON over HTTPS — any HTTP library works. `requests` for sync code, `httpx` or `aiohttp` for async. RedditAPI exposes a stable bearer-token interface, so a 5-line wrapper around `requests.get` covers everything PRAW does without the OAuth flow or developer-app review.

This page is a working Reddit API Python example — every code block above is copy-paste runnable. Set `REDDITAPI_KEY` from your environment, install `requests`, and you can fetch posts, walk comment trees, search subreddits, post comments, vote, and send DMs in plain Python. Each call is one HTTP request returning JSON.

For hobby projects, PRAW is fine. For production — anything with paying customers or commercial intent — Reddit's free OAuth tier is forbidden by their terms of service, which rules PRAW out unless you sign the $12,000-per-year Standard tier. A managed third-party API like RedditAPI is typically 5–80× cheaper at production volume and ships in two minutes instead of four weeks.

Similar reads.

More guides on the Reddit API, scraping, pricing, and MCP servers.