Migrating to PRAW 8
PRAW 8 completes the deprecation cycle started during the 7.x series. This guide covers
every removed or changed item, with examples showing how to update your code.
Python Version Support
PRAW 8 requires Python 3.10 or newer. Support for Python 3.8 and 3.9, both of which are
end-of-life, has been dropped, and support for Python 3.13 and 3.14 has been added.
Unified submit method
Subreddit.submit_gallery, Subreddit.submit_image, Subreddit.submit_poll, and
Subreddit.submit_video have been merged into Subreddit.submit(). The kind of
submission is selected with the gallery, image, poll, url, or video
keyword argument. At least one of those, or selftext, must be provided, and they are
mutually exclusive, while selftext may accompany any of them as optional
Markdown-formatted body text.
Submitting an image
Old:
reddit.subreddit("test").submit_image("My Title", "/path/to/image.png")
New:
from praw.models import PostMedia
reddit.subreddit("test").submit("My Title", image=PostMedia("/path/to/image.png"))
Submitting a video
Video-specific options, such as a custom thumbnail or submitting the video as a
videogif, are provided by passing a dict instead of a bare PostMedia.
Old:
reddit.subreddit("test").submit_video(
"My Title", "/path/to/video.mp4", thumbnail_path="/path/to/thumbnail.png"
)
New:
from praw.models import PostMedia
reddit.subreddit("test").submit(
"My Title",
video={
"media": PostMedia("/path/to/video.mp4"),
"thumbnail": PostMedia("/path/to/thumbnail.png"),
},
)
The videogif argument is now the "gif" key of the video dict:
Old:
reddit.subreddit("test").submit_video("My Title", "/path/to/video.mp4", videogif=True)
New:
from praw.models import PostMedia
reddit.subreddit("test").submit(
"My Title", video={"gif": True, "media": PostMedia("/path/to/video.mp4")}
)
Submitting a gallery
Gallery items are either a bare PostMedia, or a dict with a media key
(replacing image_path) when a caption or outbound_url is desired.
Old:
images = [
{"image_path": "/path/to/image.png"},
{"image_path": "/path/to/image2.png", "caption": "a caption"},
]
reddit.subreddit("test").submit_gallery("My Title", images)
New:
from praw.models import PostMedia
gallery = [
PostMedia("/path/to/image.png"),
{"caption": "a caption", "media": PostMedia("/path/to/image2.png")},
]
reddit.subreddit("test").submit("My Title", gallery=gallery)
Submitting a poll
The options and duration arguments are now keys of the poll dict.
selftext is no longer required for polls.
Old:
reddit.subreddit("test").submit_poll(
"Do you like PRAW?", duration=3, options=["Yes", "No"], selftext=""
)
New:
reddit.subreddit("test").submit(
"Do you like PRAW?", poll={"duration": 3, "options": ["Yes", "No"]}
)
user.me() in read-only mode
Calling reddit.user.me() in read_only mode previously returned None
with a deprecation warning. It now raises ReadOnlyException.
Old:
if reddit.user.me() is None:
print("Not authenticated")
New:
from praw.exceptions import ReadOnlyException
try:
reddit.user.me()
except ReadOnlyException:
print("Not authenticated")
Redditor.subreddit is a UserSubreddit
The subreddit attribute of Redditor is a UserSubreddit instance.
Its values are accessed as attributes; the dict interface has been removed.
Old:
title = redditor.subreddit["title"]
New:
title = redditor.subreddit.title
Link submissions with body text
The selftext and url arguments to Subreddit.submit() are no longer
mutually exclusive. When url is provided, selftext is used as optional
Markdown-formatted body text to accompany the link submission. The same applies to
gallery, image, poll, and video submissions.
One exception: combining inline_media with selftext for a url submission
raises an exception, because Reddit does not support inline media in body text for link
submissions.
Arguments that must be passed by keyword
The following arguments must now be passed by keyword:
Old modmail
Reddit retired the old modmail system, so Subreddit.mod.inbox,
Subreddit.mod.unread, Subreddit.mod.stream.unread, SubredditMessage.mute,
and SubredditMessage.unmute have been removed. Use the new modmail interface
instead.
Old:
for message in reddit.subreddit("test").mod.unread():
print(message.subject)
New:
for conversation in reddit.subreddit("test").modmail.conversations(state="new"):
print(conversation.subject)
To stream conversations, use SubredditModerationStream.modmail_conversations()
(subreddit.mod.stream.modmail_conversations()). Muting and unmuting users is
available on ModmailConversation via ModmailConversation.mute() and
ModmailConversation.unmute().
The after argument for conversations() has also been removed. To resume a
listing from a known conversation, pass after through params:
conversations = reddit.subreddit("test").modmail.conversations(params={"after": "2gmz"})
Token managers
The token_manager keyword argument to Reddit, along with
BaseTokenManager, FileTokenManager, and SQLiteTokenManager, has been
removed. Refresh tokens no longer rotate, so a static refresh_token can be provided
directly.
Old:
from praw.util.token_manager import FileTokenManager
reddit = praw.Reddit(..., token_manager=FileTokenManager("token.txt"))
New:
reddit = praw.Reddit(..., refresh_token="...")
See Working with Refresh Tokens for the complete guide to obtaining and using refresh tokens.
Removed without replacement
The following were removed because Reddit no longer supports the underlying endpoints or
features:
Reddit.random_subreddit, Subreddit.random, and Subreddit.random_rising —
Reddit removed the random subreddit and random submission endpoints.
Comment.award, Submission.award, and the gild aliases on
Comment, Redditor, and Submission — Reddit removed the
awards API.
Redditor.gilded, Subreddit.gilded, and Redditor.gildings — gilded listings
were removed along with the awards API.
Subreddits.search_by_topic — the endpoint has returned 404 since 2020.
Reddit.validate_on_submit — Reddit now always validates posts on submission, so
the setting no longer has any effect. Remove any assignments to it.
WebSocketException.original_exception.
InboxableMixin.unblock_subreddit.
The reset_timestamp key in the dictionary returned by limits() — the
remaining keys are remaining and used.