Reddit API — Use with PRAW — FetchLayer Docs
Documentation menu
FetchLayer FetchLayer API

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:

ini
[DEFAULT]
oauth_url=https://praw.fetchlayer.dev
reddit_url=https://praw.fetchlayer.dev
timeout=120
client_id=ss-your-fetchlayer-key
client_secret=unused

Your code then runs unchanged, for example:

python
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, as client_secret or as password, 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_url and timeout must be in praw.ini (or passed to praw.Reddit(...)): PRAW does not read them from environment variables.
  • A praw.ini next to your script that changes these URLs makes PRAW print a warning. Use the user configuration directory, or set PRAW_ALLOW_ENDPOINT_OVERRIDE=1.
  • Prefer one host for everything? https://api.fetchlayer.dev/v1/praw/ works the same. Keep the trailing slash on oauth_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.

PRAWCost
reddit.subreddit("x").hot() / .new() / .top() / .rising() / .controversial()1 credit per 25 posts
reddit.subreddit("all") and reddit.front listings1 credit per 25 posts
reddit.subreddit("x").search(...), reddit.subreddit("all").search(...)1 credit per 25 results
reddit.submission(id) / reddit.submission(url=...), with .comments1 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

A post from a listing comes with its usual fields. Reading an attribute Reddit does not send for that post makes PRAW fetch the post with its comments, which costs 1 credit, just as it would cost a request against Reddit.

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: 429 with Retry-After, which PRAW waits for.
  • Something that does not exist: 404 (NotFound). A brief outage: 503, which PRAW retries by itself.