Reddit¶
Reddit hosts millions of media posts across thousands of subreddits. The xanax Reddit and AsyncReddit clients provide typed, paginated access to subreddit media feeds.
Note
Reddit requires OAuth2 client credentials (script app) for all API access. Register an application at reddit.com/prefs/apps and select the “script” type to obtain a client ID and secret.
Authentication¶
Pass your credentials directly or use environment variables:
from xanax.sources.reddit import Reddit
# Explicit credentials
client = Reddit(
client_id="your-client-id",
client_secret="your-client-secret",
user_agent="python:myapp/1.0 (by u/yourusername)",
)
# From environment variables:
# REDDIT_CLIENT_ID, REDDIT_CLIENT_SECRET, REDDIT_USER_AGENT
client = Reddit()
Tokens are fetched automatically using the OAuth2 client_credentials flow and cached for up to 3600 seconds. Refresh is transparent — no action required.
Fetching media¶
iter_media() iterates over media posts (images, GIFs, videos) from a subreddit:
from xanax.sources.reddit.params import RedditParams
from xanax.sources.reddit.enums import RedditSort
for post in client.iter_media(RedditParams(subreddit="EarthPorn")):
print(post.id, post.media_type, post.url)
Filter by media type¶
from xanax.enums import MediaType
# Images only
for post in client.iter_media(RedditParams(
subreddit="EarthPorn",
media_type=MediaType.IMAGE,
)):
client.download(post, path=f"{post.id}.jpg")
Sort and time filter¶
from xanax.sources.reddit.enums import RedditSort, RedditTimeFilter
for post in client.iter_media(RedditParams(
subreddit="pics",
sort=RedditSort.TOP,
time_filter=RedditTimeFilter.WEEK,
limit=25,
)):
print(post.title, post.score)
Downloading¶
download() returns raw bytes and optionally saves to disk:
post = next(client.iter_media(RedditParams(subreddit="EarthPorn")))
# Memory only
data: bytes = client.download(post)
# Save to disk
from pathlib import Path
client.download(post, path=Path(f"{post.id}.jpg"))
Note
Video posts (media_type=MediaType.VIDEO) download the video-only MP4 stream via fallback_url. Audio is a separate DASH stream — merging audio and video requires ffmpeg and is not handled by xanax.
Gallery posts¶
Reddit gallery posts contain multiple images. iter_media() expands gallery posts into individual RedditPost objects automatically, one per image:
for post in client.iter_media(RedditParams(subreddit="WidescreenWallpaper")):
# Each gallery image appears as a separate post
print(post.id, post.url, post.gallery_index)
Getting a single post¶
post = client.post("abc123")
if post:
print(post.title, post.media_type, post.url)
Async client¶
import asyncio
from xanax.sources.reddit import AsyncReddit
from xanax.sources.reddit.params import RedditParams
async def main():
async with AsyncReddit(
client_id="your-client-id",
client_secret="your-client-secret",
user_agent="python:myapp/1.0 (by u/yourusername)",
) as client:
async for post in client.aiter_media(RedditParams(subreddit="EarthPorn")):
data = await client.download(post)
asyncio.run(main())
NSFW content¶
NSFW posts are excluded by default. To include them:
params = RedditParams(subreddit="pics", include_nsfw=True)
Rate limiting¶
Reddit enforces rate limits via X-Ratelimit-* response headers. Enable automatic retry:
client = Reddit(client_id="...", client_secret="...", user_agent="...", max_retries=3)
Post model reference¶
RedditPost contains:
Field |
Type |
Notes |
|---|---|---|
|
|
Unique post ID |
|
|
Post title |
|
|
Subreddit name |
|
|
Direct media URL |
|
|
|
|
|
Reddit page URL |
|
|
Upvotes minus downvotes |
|
|
Ratio of upvotes to total votes |
|
|
Post author username |
|
|
Post creation time |
|
|
NSFW flag |
|
|
Fallback MP4 URL for video posts |
|
|
Position within gallery (0-based) |
See also¶
Clients — complete method signatures
Search Parameters —
RedditParamsreferenceError Handling — exception hierarchy