mcpField
R
reddit-api

reddit-api

github.com/alinaqiデータ・データベース711315 回インストール2 週間前に更新
リポジトリ

reddit-api について

reddit-apiは、アプリにRedditのデータを取り込む開発者向けのリファレンススキルです。Python、TypeScript/Node.js、HTTPによる認証、データ取得、投稿方法を扱います。

主な機能

  1. 01OAuth2によるAPI認証
  2. 02投稿、コメント、ユーザーデータの取得
  3. 03subredditコンテンツの検索
  4. 04新しい投稿やコメントのストリーミング
  5. 05投稿の送信と投票

活用シーン

  1. 01アプリでsubredditの人気投稿を表示
  2. 02新しい投稿やコメントをリアルタイム処理
  3. 03Redditのユーザー認証フローを構築
  4. 04スクリプトから投稿や投票を実行

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/alinaqi/maggy --skill reddit-api

インストール後、Claude Code で/reddit-apiを入力して読み込まれたか確認します。

詳細

Reddit API スキル

アプリケーションに Reddit のデータを統合するための機能です。投稿、コメント、サブレディット、ユーザーデータを取得できます。

参照元: Reddit API ドキュメント | OAuth2 Wiki | PRAW ドキュメント


セットアップ

1. Reddit アプリを作成する
  1. https://www.reddit.com/prefs/apps にアクセスします
  2. 「Create App」または「Create Another App」をクリックします
  3. 次の項目を入力します。
    • 名前:アプリ名
    • アプリの種類:
      • script - 個人利用または自分で管理するボット向け
      • web app - ユーザー認証を行うサーバーサイドアプリ向け
      • installed app - モバイルアプリ/デスクトップアプリ向け
    • リダイレクト URI:http://localhost:8000/callback(開発用)
  4. client_id(アプリ名の下に表示)と client_secret を控えます
2. 環境変数
# .env
REDDIT_CLIENT_ID=your_client_id
REDDIT_CLIENT_SECRET=your_client_secret
REDDIT_USER_AGENT=YourApp/1.0 by YourUsername
REDDIT_USERNAME=your_username        # For script apps only
REDDIT_PASSWORD=your_password        # For script apps only

User-Agent の形式:<platform>:<app_id>:<version> (by /u/<username>)


レート制限

ティア制限備考
OAuth 認証済み100 QPMOAuth クライアント ID ごと
未認証ブロックOAuth の使用が必須
  • 制限は10分間のウィンドウで平均化されます
  • ブロックを避けるため、User-Agent ヘッダーを含めてください
  • X-Ratelimit-* レスポンスヘッダーを確認してください

Python:PRAW(推奨)

インストール
pip install praw
# or
uv add praw
スクリプトアプリ(個人利用/ボット)
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,
)

# Verify authentication
print(f"Logged in as: {reddit.user.me()}")
読み取り専用(ユーザー認証なし)
import praw

reddit = praw.Reddit(
    client_id="your_client_id",
    client_secret="your_client_secret",
    user_agent="YourApp/1.0 by YourUsername",
)

# Read-only mode - can browse, can't post/vote
reddit.read_only = True
一般的な操作
# Get subreddit posts
subreddit = reddit.subreddit("python")

# Hot posts
for post in subreddit.hot(limit=10):
    print(f"{post.title} - {post.score} upvotes")

# New posts
for post in subreddit.new(limit=10):
    print(post.title)

# Search posts
for post in subreddit.search("pydantic", limit=5):
    print(post.title)

# Get specific post
submission = reddit.submission(id="abc123")
print(submission.title)
print(submission.selftext)

# Get comments
submission.comments.replace_more(limit=0)  # Flatten comment tree
for comment in submission.comments.list():
    print(f"{comment.author}: {comment.body[:100]}")
投稿と投票(認証が必要)
# Submit text post
subreddit = reddit.subreddit("test")
submission = subreddit.submit(
    title="Test Post",
    selftext="This is the body of my post."
)

# Submit link post
submission = subreddit.submit(
    title="Check this out",
    url="https://example.com"
)

# Vote
submission.upvote()
submission.downvote()
submission.clear_vote()

# Comment
submission.reply("Great post!")

# Reply to comment
comment = reddit.comment(id="xyz789")
comment.reply("I agree!")
ストリーミング(リアルタイム)
# Stream new posts
for post in reddit.subreddit("python").stream.submissions():
    print(f"New post: {post.title}")
    # Process post...

# Stream new comments
for comment in reddit.subreddit("python").stream.comments():
    print(f"New comment by {comment.author}: {comment.body[:50]}")
ユーザーデータ
# Get user info
user = reddit.redditor("spez")
print(f"Karma: {user.link_karma + user.comment_karma}")

# User's posts
for post in user.submissions.new(limit=5):
    print(post.title)

# User's comments
for comment in user.comments.new(limit=5):
    print(comment.body[:100])

TypeScript/Node.js:Snoowrap

インストール
npm install snoowrap
# or
pnpm add snoowrap
セットアップ
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!,
});

// Configure rate limiting
reddit.config({
  requestDelay: 1000,  // 1 second between requests
  continueAfterRatelimitError: true,
});
一般的な操作
// Get hot posts from subreddit
const posts = await reddit.getSubreddit("typescript").getHot({ limit: 10 });
posts.forEach((post) => {
  console.log(`${post.title} - ${post.score} upvotes`);
});

// Search posts
const results = await reddit.getSubreddit("programming").search({
  query: "typescript",
  sort: "relevance",
  time: "month",
  limit: 10,
});

// Get specific post
const submission = await reddit.getSubmission("abc123").fetch();
console.log(submission.title);

// Get comments
const comments = await submission.comments.fetchAll();
comments.forEach((comment) => {
  console.log(`${comment.author.name}: ${comment.body.slice(0, 100)}`);
});
投稿
// Submit text post
const post = await reddit.getSubreddit("test").submitSelfpost({
  title: "Test Post",
  text: "This is the body.",
});

// Submit link
const linkPost = await reddit.getSubreddit("test").submitLink({
  title: "Check this out",
  url: "https://example.com",
});

// Vote and comment
await post.upvote();
await post.reply("Great post!");

直接 API を利用する(ライブラリなし)

httpx を使用した Python
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 get_posts(self, subreddit: str, sort: str = "hot", limit: int = 10) -> list[dict]:
        """Get posts from a subreddit."""
        if not self.access_token:
            await self.authenticate()

        response = await self.client.get(
            f"https://oauth.reddit.com/r/{subreddit}/{sort}",
            headers={
                "Authorization": f"Bearer {self.access_token}",
                "User-Agent": self.user_agent,
            },
            params={"limit": limit},
        )
        response.raise_for_status()
        return [post["data"] for post in response.json()["data"]["children"]]

    async def close(self) -> None:
        await self.client.aclose()


# Usage
async def main():
    client = RedditClient(
        client_id="your_id",
        client_secret="your_secret",
        user_agent="YourApp/1.0",
    )
    try:
        posts = await client.get_posts("python", limit=5)
        for post in posts:
            print(f"{post['title']} - {post['score']} upvotes")
    finally:
        await client.close()
fetch を使用した TypeScript
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: {
        Authorization: `Basic ${auth}`,
        "User-Agent": this.userAgent,
        "Content-Type": "application/x-www-form-urlencoded",
      },
      body: "grant_type=client_credentials",
    });

    const data = await response.json();
    this.accessToken = data.access_token;
  }

  async getPosts(subreddit: string, sort = "hot", limit = 10): Promise<RedditPost[]> {
    if (!this.accessToken) await this.authenticate();

    const response = await fetch(
      `https://oauth.reddit.com/r/${subreddit}/${sort}?limit=${limit}`,
      {
        headers: {
          Authorization: `Bearer ${this.accessToken}`,
          "User-Agent": this.userAgent,
        },
      }
    );

    const data = await response.json();
    return data.data.children.map((child: any) => child.data);
  }
}

OAuth2 Web フロー(ユーザー認証)

ユーザーが Reddit アカウントでログインするアプリ向けです。

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]

    # Exchange code for token
    async with httpx.AsyncClient() as client:
        response = await client.post(
            "https://www.reddit.com/api/v1/access_token",
            auth=(REDDIT_CLIENT_ID, REDDIT_CLIENT_SECRET),
            data={
                "grant_type": "authorization_code",
                "code": code,
                "redirect_uri": REDIRECT_URI,
            },
            headers={"User-Agent": "YourApp/1.0"},
        )

    tokens = response.json()
    # Store tokens securely, associate with user session
    return {"access_token": tokens["access_token"][:10] + "..."}

利用可能なスコープ

スコープ説明
identityユーザー名と登録日へのアクセス
read投稿とコメントへのアクセス
submitリンクとコメントの投稿
voteコンテンツへの賛成票/反対票
edit投稿とコメントの編集
history投票履歴へのアクセス
subscribeサブレディットの購読管理
mysubreddits購読中のサブレディットへのアクセス
privatemessagesプライベートメッセージへのアクセス
saveコンテンツの保存/保存解除

全一覧:https://www.reddit.com/api/v1/scopes


プロジェクト構成

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 モデル

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

アンチパターン

  • User-Agent を指定しない - 適切な User-Agent がないリクエストは Reddit にブロックされます
  • レート制限を無視する - 100 QPM を守り、X-Ratelimit-* ヘッダーを確認してください
  • 認証情報をコードに保存する - 環境変数を使用してください
  • MoreComments を処理しない - PRAW で replace_more() を使用してください
  • ストリーミングではなくポーリングする - リアルタイムデータには .stream を使用してください
  • エラー処理を行わない - 429(レート制限)、403(禁止)、404(見つからない)を処理してください

クイックリファレンス

# PRAW installation
pip install praw

# Snoowrap installation
npm install snoowrap

# Test authentication
python -c "import praw; r = praw.Reddit(...); print(r.user.me())"
エンドポイント
操作エンドポイント
認証トークンPOST https://www.reddit.com/api/v1/access_token
API リクエストhttps://oauth.reddit.com/...
サブレディットの投稿GET /r/{subreddit}/{sort}
投稿GET /comments/{id}
ユーザー情報GET /user/{username}/about
投稿を送信POST /api/submit
投票POST /api/vote

コメント