Use with PRAW
Already reading Reddit with PRAW? Point it at FetchLayer and keep your code: subreddit listings, submissions with their comments, search, redditors and communities work as before, in the same objects.
Switch in five lines
Put this in a praw.ini in your user configuration directory (~/.config/praw.ini on Linux and macOS, %APPDATA%\praw.ini on Windows) or next to your script:
[DEFAULT]
oauth_url=https://praw.fetchlayer.dev
reddit_url=https://praw.fetchlayer.dev
timeout=120
client_id=ss-your-fetchlayer-key
client_secret=unusedYour code then runs unchanged, for example:
import praw
reddit = praw.Reddit(user_agent="my-app")
for submission in reddit.subreddit("python").hot(limit=25):
print(submission.score, submission.title)
submission = reddit.submission(id="1xehnro")
submission.comments.replace_more(limit=32)
for comment in submission.comments.list():
print(comment.depth, comment.author, comment.body[:80])- Your FetchLayer key is the credential. It works as
client_id, asclient_secretor aspassword, so put it in whichever one your code reads from configuration. Environment variables work too, e.g.praw_client_id=ss-your-fetchlayer-key. Your Reddit username and password are not needed. - Keep
timeout=120. A submission with a large comment tree can take longer than PRAW's default of 16 seconds. oauth_url,reddit_urlandtimeoutmust be inpraw.ini(or passed topraw.Reddit(...)): PRAW does not read them from environment variables.- A
praw.ininext to your script that changes these URLs makes PRAW print a warning. Use the user configuration directory, or setPRAW_ALLOW_ENDPOINT_OVERRIDE=1. - Prefer one host for everything?
https://api.fetchlayer.dev/v1/praw/works the same. Keep the trailing slash onoauth_url, or PRAW drops the path.
What works, and what it costs
Credits are the same as the matching REST endpoints. PRAW asks for 100 items per page by default, which is 4 credits for a full page of posts. Pass the limit you actually need.
| PRAW | Cost |
|---|---|
| reddit.subreddit("x").hot() / .new() / .top() / .rising() / .controversial() | 1 credit per 25 posts |
| reddit.subreddit("all") and reddit.front listings | 1 credit per 25 posts |
| reddit.subreddit("x").search(...), reddit.subreddit("all").search(...) | 1 credit per 25 results |
| reddit.submission(id) / reddit.submission(url=...), with .comments | 1 credit (post and comment tree) |
| submission.comments.replace_more(limit=n) | 1 credit per "load more comments" expanded (up to n) |
| submission.comments.list() | free |
| reddit.comment(id) attributes, reddit.info(fullnames=[...]) | 1 credit per request (up to 100 ids) |
| reddit.info(subreddits=[...]) | 1 credit per community |
| reddit.subreddit("x") attributes (title, subscribers, created_utc, ...) | 1 credit |
| reddit.redditor("x") attributes (link_karma, comment_karma, created_utc, ...) | 1 credit |
| reddit.redditor("x").submissions.new() / .hot() / .top() / .controversial() | 1 credit per 25 posts |
| reddit.redditor("x").comments.new() / .hot() / .top() / .controversial() | 1 credit per 25 comments |
| reddit.redditor("x").new() / .hot() / .top(), redditor.overview.* | 1 credit per 25 posts plus 1 per 25 comments (one call to each per page) |
| reddit.info(url=...) | 1 credit |
| reddit.subreddits.search(...), reddit.redditors.search(...) | 1 credit per 10 results |
| subreddit.stream.submissions(), redditor.stream.submissions() | 1 credit per poll, plus 1 per further 25 new posts since the last poll |
Large threads
replace_more(limit=None) on a thread with thousands of comments can mean many calls, each billed. PRAW's default replace_more() stops after 32; pass a limit that fits what you need.
Attributes PRAW loads on demand
Read-only
Voting, submitting, commenting, editing, saving, subscribing, messages, moderation and anything about a signed-in account (reddit.user.me(), inbox, preferences) answer 403, which PRAW raises as prawcore.exceptions.Forbidden.
Known differences from Reddit's API
- A submission's comments arrive as Reddit shows them in place, about 4 levels deep, with "load more comments" and "continue this thread" points as MoreComments. replace_more() loads them, about 10 replies or 9 more levels each. A MoreComments object's children are opaque handles, not comment ids, and count is at least 1.
- comment_sort random keeps Reddit's default order. A small comment_limit makes replace_more(limit=None) take more calls (each trimmed thread is its own MoreComments), so leave the default unless you want a capped first fetch.
- Reddit reorders a live thread while replace_more() runs. A comment already loaded is not sent again, best effort; on a rare occasion (a service restart mid-run) one can repeat, which PRAW reports as DuplicateReplaceException.
- upvote_ratio, total_awards_received and a comment's edited come with a submission and its comments. A post from a listing or search has upvote_ratio None and total_awards_received 0, and a comment from a user's history has edited False; fetch the submission for them.
- Not available: a community's sidebar description (None) and accounts_active (None). subscribers is None for NSFW communities.
- An account's created_utc is the day the account was created, at 00:00 UTC.
- reddit.info(url=...) finds posts linking to that exact URL among the first 25 search matches, so a URL posted very many times may not return every post.
- Reddit leaves removed comments that have no replies out of a thread, so a thread can hold fewer comments than num_comments.
Errors and limits
- An invalid key fails at the first request with
prawcore.exceptions.ResponseException: received 401 HTTP response. - Out of credits:
403(Forbidden), with the reason in the response. - Your plan's rate limit:
429withRetry-After, which PRAW waits for. - Something that does not exist:
404(NotFound). A brief outage:503, which PRAW retries by itself.