~ / guides / How to Scrape Reddit Images: Get and Download Image URLs

How to Scrape Reddit Images: Get and Download Image URLs

BS
Ben Shaw
Reddit data engineer · about the author
the short version
  • Scraping Reddit images is two steps: collect the image URLs from posts, then download the actual file bytes from each URL. The post is where the link lives; the download is a separate request.
  • By subreddit: each returned post carries its media link in an external_url field (direct i.redd.it images, imgur links, gallery and preview URLs). Collect those across the posts[] array.
  • By thread: a Reddit post object holds its own image link, and image URLs also appear inside comments[] bodies. Pull both and dedupe.
  • To save the files, fetch each image URL through the universal endpoint: GET /api/v1/universal?url=<image_url>. It pulls the binary through the proxy pool so the bytes land on disk. I ran this chain in June 2026.

I wanted a folder of images from a photo subreddit, and my first instinct was wrong: I went straight for a downloader before I had a single URL to download. Scraping Reddit images is two separate jobs, and conflating them is what makes people’s scripts brittle. First you collect the image URLs that the posts point to, then you fetch the actual file bytes from each of those URLs. The post tells you where the image lives, and getting the image is a second request.

A Reddit image scraper is just code that does those two things in order, and below is how I scrape images from Reddit both ways I actually use: by subreddit (sweep a community and grab every post’s media link) and by thread (pull one post plus the image links buried in its comments). Then I download the files through a proxy so the bytes land on disk. Everything here is the Python I ran in June 2026, with the request shapes confirmed against the live endpoints.

What does it mean to scrape Reddit images?

Scraping Reddit images means collecting the image URLs that Reddit posts point to, then downloading the image files from those URLs. The data you extract first is a list of links, and the image bytes come from a follow-up fetch of each link. Keeping those two stages distinct is the whole trick, because they fail for different reasons and scale differently.

The reason the split matters is that Reddit almost never hosts the pixels inside the post JSON. A post record carries a reference to the image: a direct upload on i.redd.it, an imgur link, an external site, or a gallery manifest. Reddit’s own media and content policy governs how you may pull that data, and the image hosts serve the file itself. So a working image scraper produces two artifacts: a deduped URL list, and the downloaded files.

There are three kinds of image post you will meet, and they expose the URL differently:

The next sections cover the two collection routes (by subreddit, then by thread), and then the download step that turns a URL into a saved file. The image host shows up in every route, so it is worth knowing the difference between a direct i.redd.it upload and a preview.redd.it thumbnail before you start.

How do you scrape images from a subreddit?

To scrape images from a subreddit, call a subreddit endpoint and read the media link off each post in the returned posts[] array. Every post that is an image post carries its image URL in an external_url field, so you sweep the community, collect those URLs, and download them. This is the route for “give me everything from r/EarthPorn,” and it is the one most people actually want.

I run it against the Reddit Subreddit Scraper API, which returns a subreddit’s posts as structured JSON without an OAuth app. The request is a plain GET with the subreddit name and your key:

curl "https://chocodata.com/api/v1/reddit/subreddit?subreddit=pics&sort=hot&limit=50&api_key=$CHOCO_API_KEY"

Each post in the response carries the fields that matter for images. external_url holds the post’s link target (the image link for an image post), domain tells you the host, and permalink points back to the Reddit thread. In the captured responses I checked, image posts came back with values like https://i.redd.it/f3or4p83sn0h1.png and domain: "i.redd.it", which is exactly the direct, full-resolution file. The Python sweeps the array and keeps the URLs that are images:

import re
import requests

CHOCO = "https://chocodata.com/api/v1"
KEY = "YOUR_CHOCO_API_KEY"

# Hosts and extensions that mean "this link is an image".
IMAGE_HOST = re.compile(r"(i\.redd\.it|preview\.redd\.it|i\.imgur\.com)", re.I)
IMAGE_EXT = re.compile(r"\.(jpe?g|png|gif|webp)(\?|$)", re.I)

def image_urls_from_subreddit(subreddit, sort="hot", limit=50):
    r = requests.get(
        f"{CHOCO}/reddit/subreddit",
        params={"subreddit": subreddit, "sort": sort, "limit": limit, "api_key": KEY},
        timeout=60,
    )
    r.raise_for_status()
    posts = r.json()["data"]["posts"]

    urls = []
    for post in posts:
        link = post.get("external_url")
        if not link:
            continue                      # self/text post, no media link
        if IMAGE_HOST.search(link) or IMAGE_EXT.search(link):
            urls.append(link)
    return list(dict.fromkeys(urls))      # dedupe, keep order

links = image_urls_from_subreddit("pics", limit=50)
print(f"found {len(links)} image URLs")
for u in links[:5]:
    print(" ", u)

That returns a clean, deduped list of direct image URLs ready to download. Two things to know before you scale it. Posts that are text or link-to-article come back with external_url set to a non-image URL or null, which the filter drops, so you are not chasing news links. And the listing is paginated: the response includes an after_cursor value, and passing it back as after= walks to the next page, which is how you go past the first ~25 posts toward the bulk pull that “download reddit images in bulk” implies. For monitoring many subreddits by keyword on a schedule, you page each one and append to the same URL set.

Reddit gallery posts expose several images through a media_metadata map instead of a single image URL, so you read each entry in that map and ignore the post’s single link field. A gallery is one post with many pictures, and the post-level external_url will not be a single file for it. The media_metadata object is keyed by image ID, and each entry carries the hosted URL for that image; gallery_data lists the IDs in display order. When the API surfaces those fields, iterate the map and add every image URL to the same list you built above, so a 12-image gallery contributes 12 files and not one. If you only need direct single-image posts, the host-and-extension filter already skips galleries cleanly, and you lose only the multi-image submissions.

How do you scrape images from a specific Reddit post?

To scrape images from a specific Reddit post, call a post endpoint with the thread URL (or its post_id plus subreddit) and read the image link off the post object, then pull any image links out of the comments[] bodies. A thread gives you two sources: the submission’s own media link, and the images people drop into the discussion. This is the route when you have one URL and want everything visual in it.

I use the Reddit Post Scraper API, which returns the post object and its nested comment tree in one call:

import re
import requests

CHOCO = "https://chocodata.com/api/v1"
KEY = "YOUR_CHOCO_API_KEY"
IMAGE_IN_TEXT = re.compile(
    r"https?://[^\s)\"]+?\.(?:jpe?g|png|gif|webp)(?:\?[^\s)\"]*)?", re.I
)

def image_urls_from_post(post_id, subreddit):
    r = requests.get(
        f"{CHOCO}/reddit/post",
        params={"post_id": post_id, "subreddit": subreddit, "api_key": KEY},
        timeout=60,
    )
    r.raise_for_status()
    data = r.json()["data"]

    urls = []
    # 1) the post's own image link
    post_link = data["post"].get("external_url")
    if post_link:
        urls.append(post_link)

    # 2) image links pasted into comments (walk the nested tree)
    def walk(comments):
        for c in comments:
            body = c.get("body") or ""
            urls.extend(IMAGE_IN_TEXT.findall(body))
            walk(c.get("replies", []))

    walk(data.get("comments", []))
    return list(dict.fromkeys(urls))      # dedupe

links = image_urls_from_post("1u68w0z", subreddit="pics")
print(links)

The post object’s external_url is the submission’s image (the same i.redd.it-style link you saw in the subreddit route), and the regex over comment body text catches the links people paste into the thread. Walking replies recursively matters because Reddit nests comments, so an image link three levels deep in a reply chain is still picked up. Deduping at the end collapses the case where the same image is linked by the post and quoted again in a comment.

You can also pass a full thread URL instead of the ID. The endpoint accepts url=https://www.reddit.com/r/pics/comments/<id>/<slug>/, resolves it to the same post, and returns the identical shape. If you care about the comment text around the images (captions, credits, sources), the Reddit Comment Thread Scraper API returns the same nested bodies on their own, which is handy when the images are incidental and the discussion is the point.

How do you download the actual image files?

To download the actual image files, fetch each image URL through the universal endpoint, which pulls the binary content through the proxy pool and hands you the bytes to write to disk. Collecting URLs is only half the job; the files still live on i.redd.it, imgur, or an external host, and some of those rate-limit or block direct datacenter hits. Routing the download through GET /api/v1/universal?url=<image_url> fetches the resource the same way a browser would and returns the file content.

The download writes the response body straight to a file. Python’s requests exposes the raw bytes on r.content, and for larger files you stream them with iter_content so you are not holding the whole image in memory, which is the pattern the requests docs recommend for saving a download:

import os
import requests

CHOCO = "https://chocodata.com/api/v1"
KEY = "YOUR_CHOCO_API_KEY"

def download_image(image_url, out_dir="reddit_images"):
    os.makedirs(out_dir, exist_ok=True)
    name = image_url.split("/")[-1].split("?")[0] or "image"
    path = os.path.join(out_dir, name)

    with requests.get(
        f"{CHOCO}/universal",
        params={"url": image_url, "api_key": KEY},
        stream=True,
        timeout=90,
    ) as r:
        r.raise_for_status()
        with open(path, "wb") as fd:
            for chunk in r.iter_content(chunk_size=8192):
                fd.write(chunk)
    return path

# Chain it: subreddit -> URLs -> files
links = image_urls_from_subreddit("pics", limit=50)
saved = [download_image(u) for u in links]
print(f"saved {len(saved)} images to ./reddit_images")

That is the complete chain: the subreddit (or post) call produces the URL list, and the universal call turns each URL into a saved file. Because the i.redd.it link is the full-resolution upload, the file you save is full quality with no watermark, which answers the “full quality” and “no watermark” variants people search for. The download is the expensive part at volume, so two habits keep a bulk run sane: store the post IDs you have already pulled and fetch only new ones, and cap concurrency so you are polite to the image hosts. If you need to skip adult content, the post object exposes an NSFW flag (over_18 in the raw API), so you can filter those out before downloading. If you would rather not run any of the proxy and retry logic yourself, you can get an API key on the ChocoData sign-up page and swap it into the snippets above; in my runs the managed routing returned the binary without a single block to debug.

Can you scrape Reddit images with PRAW instead?

Yes, you can scrape Reddit images with PRAW, the official Python Reddit API Wrapper, by reading submission.url for each post and downloading the ones that are images. PRAW is the credentialed route: it talks to Reddit’s Data API over OAuth, so it needs a registered app and obeys the rate limit, but it gives you the same image links. The image URL for a standard image post is submission.url, and you filter the same way, by host and extension.

import praw

reddit = praw.Reddit(
    client_id="your_id",
    client_secret="your_secret",
    user_agent="reddit-image-demo/1.0 by u/yourname",
)

for submission in reddit.subreddit("pics").hot(limit=50):
    url = submission.url
    if url.endswith((".jpg", ".jpeg", ".png", ".gif", ".webp")):
        print(submission.id, url)   # then download url

PRAW’s submission.url is the post’s link target, which for an image post is the direct file, exactly like the external_url field in the route above. The PRAW submission docs note that url holds the link target (or the permalink for a self post), so the same host-and-extension filter applies. Galleries are the catch: a gallery submission carries is_gallery = True and exposes its images through submission.media_metadata, with no single url for the set, so you handle those separately, the same as in the subreddit route.

The reason PRAW is not always the answer is the access cost. Reddit’s Data API allows free non-commercial clients 100 queries per minute per OAuth client ID, averaged over a 10-minute window, and since the 2025 change every app, including personal projects, needs pre-approval before it can call the API at all. Commercial use is charged; Reddit set the paid rate at $0.24 per 1,000 API calls when it announced pricing. So PRAW is clean if you have an approved app and modest volume, and a scraper API is the shortcut when you want the image URLs without registering, refreshing tokens, or pacing requests by hand. Either way, the legal line around the images themselves is worth a look before you pull at scale.

Collecting image URLs from publicly visible Reddit pages is generally treated as legal in the US, but downloading and reusing the images is a separate copyright question. The scraping of public data and the rights to the images are two different issues, and the second one trips people up more than the first.

On the scraping itself, the Ninth Circuit’s ruling in hiQ v. LinkedIn held that scraping publicly accessible data likely does not violate the Computer Fraud and Abuse Act, reasoning that a public site has “erected no gates” to bypass. The EFF’s summary of the hiQ ruling and the case background on Wikipedia both walk through it. That covers reading public Reddit pages to extract URLs.

The images are a different matter. A photo posted to Reddit is usually owned by the person who took or made it, so downloading it for redistribution or commercial use can infringe their copyright regardless of how you obtained the link. Reddit’s User Agreement also restricts automated access without permission, and Reddit’s API policy now bars using its content to train machine-learning or AI models without consent. Practically, that means scraping image URLs for analysis or archival of your own communities sits on solid ground, while reusing other people’s images needs the same permission any other copyrighted image would. I cover the wider picture, including robots.txt and the CFAA, in my guide on whether scraping Reddit is legal, and I rank the managed tools in my best Reddit scrapers in 2026 roundup.

FAQ

How do I scrape images from a whole subreddit?

Call a subreddit endpoint and read the media link off each returned post. In the ChocoData response, every post in posts[] carries an external_url field that holds the image link for image posts (a direct i.redd.it file, an Imgur link, or a gallery URL). Loop the array, keep the URLs that end in an image extension or sit on a known image host, then download each one. That is the full path from subreddit to a folder of images.

Where is the image URL in a Reddit post?

For an image post, the image URL is the post's link target, not the permalink. In PRAW that is submission.url; in the ChocoData post object it is external_url (mapped from Reddit's content-href attribute) alongside domain. A direct upload looks like https://i.redd.it/<id>.jpg. A gallery post exposes several images through Reddit's media_metadata map, so it has no single url for the whole post.

Can I download Reddit images in bulk and in full quality?

Yes. The direct i.redd.it URL on an image post is the full-resolution file, so downloading that URL gives you full quality with no watermark. For bulk, page through the subreddit, collect every external_url, dedupe the list, and fetch each file. The slow part is the download volume, not the listing, so caching post IDs you already have keeps repeat runs cheap.

How do I get images out of a specific Reddit thread and its comments?

Call a post endpoint with the thread URL or its post_id plus subreddit. The response gives you the post's own external_url and a nested comments[] tree. Image links posted inside comments live in each comment's body text, so run a regex for image URLs over those bodies, combine them with the post's link, and dedupe before downloading.

Is scraping Reddit images legal?

Collecting image URLs from publicly visible Reddit pages is generally treated as legal in the US after the Ninth Circuit's ruling in hiQ v. LinkedIn, which held that scraping public data likely does not violate the CFAA. The images themselves are usually owned by the people who posted them, so reuse is a separate copyright question. Reddit's User Agreement also restricts automated access, and its policy bars using Reddit content to train AI models without permission.

BS
Ben Shaw
I've built Reddit data pipelines for years. On redditscraperapi.com I run Reddit scraping methods against live pages and publish what actually holds up.