Driver FixRecommendedSound, Wi-Fi or graphics acting up? Check drivers firstFind missing or outdated drivers fast.Check DriversOctober DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsSlow PC?RecommendedPC slow today? Run a repair scan before it gets worseResolve common Windows issues and optimize system performance.Scan Now×
Skip to content
image download

How to Download an Image With Python

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Downloading an image in Python means retrieving an HTTP response and writing its binary body to a file. Open the destination in binary write mode (wb), not text mode, or the bytes can be changed. For a dependency-free script use Python’s built-in urllib.request; for timeouts, status checks, and large files, Requests provides a more convenient streaming interface.

Download one image with Python’s standard library

urllib.request is included with Python. Its URL retrieval functions return the server’s raw response data, which can be binary image data. The shortest practical example is:

from urllib.request import urlretrieve

url = "https://example.com/image.jpg"
urlretrieve(url, "image.jpg")
print("Saved image.jpg")

This is suitable for a trusted, one-off URL. The filename in the URL does not prove that the response is actually a JPEG: a server can return an HTML error page, a redirect destination, or another content type. When you need control over headers, status handling, timeouts, or memory use, use an explicit response instead.

Write the response bytes yourself

from urllib.request import urlopen

url = "https://example.com/image.png"
output_path = "image.png"

with urlopen(url, timeout=30) as response:
    data = response.read()

with open(output_path, "wb") as image_file:
    image_file.write(data)

print(f"Saved {output_path}")

The wb mode is essential. Text mode may perform newline or encoding conversions that are meaningless for an image. The response’s Content-Type header can help you identify what the server says it returned, but it is not a complete validation of untrusted data.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Stream large images with Requests

Reading an entire response with response.content keeps all downloaded bytes in memory. Requests’ documented streaming pattern uses stream=True and iter_content to write incremental chunks. This is preferable for large images or downloads in a service.

import requests

url = "https://example.com/image.jpg"
output_path = "image.jpg"

with requests.get(url, stream=True, timeout=30) as response:
    response.raise_for_status()
    with open(output_path, "wb") as image_file:
        for chunk in response.iter_content(chunk_size=8192):
            if chunk:                 # skip keep-alive chunks
                image_file.write(chunk)

print(f"Saved {output_path}")

Install Requests first if it is not present:

python -m pip install requests

The timeout prevents a connection from waiting forever, and raise_for_status() stops the script before saving a response that carries an HTTP error status. Requests verifies TLS certificates by default; do not disable certificate verification merely to make a failing connection appear to work. When using a streamed response, consume the body as shown or close the response so the connection can return to Requests’ connection pool.

Choose a chunk size

An 8,192-byte chunk is a reasonable general default. Larger chunks can reduce Python overhead for very large files; smaller chunks reduce the amount held temporarily in memory. Chunk size does not change the final file. Always skip empty chunks, which can occur with keep-alive transfers.

Use headers, cookies, or an authorization token

Some image endpoints require a browser-like header or an API token. Supply only credentials that you are authorized to use, and avoid printing them.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
import requests

url = "https://example.com/private/image"
headers = {
    "Authorization": "Bearer YOUR_TOKEN",
    "User-Agent": "my-image-downloader/1.0",
}

with requests.get(url, headers=headers, stream=True, timeout=30) as response:
    response.raise_for_status()
    with open("private-image.bin", "wb") as image_file:
        for chunk in response.iter_content(8192):
            if chunk:
                image_file.write(chunk)

For a session that must retain cookies across requests, create requests.Session() and make the request through that session. Keep the URL, credentials, and downloaded data within the access rules of the site or API.

Save safely and verify what arrived

Do not infer the type from the extension

A URL ending in .jpg can still return an error document. Inspect the response’s Content-Type when deciding whether to continue:

content_type = response.headers.get("Content-Type", "")
print(content_type)

Header values are clues, not a substitute for validating data before processing it. The basic download recipe does not establish a universal maximum file size, URL allowlist, retry policy, or security policy for hostile image files; add those controls when your application handles untrusted URLs.

Prevent accidental overwrites

Use a destination chosen by your application rather than directly trusting a filename supplied by a remote URL. Create parent directories before opening the file if necessary:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
from pathlib import Path

path = Path("downloads") / "image.jpg"
path.parent.mkdir(parents=True, exist_ok=True)

For an atomic-style workflow, write to a temporary path in the same directory and rename it only after the response has completed. That keeps a later reader from seeing a partially written file.

Open or process the downloaded image with Pillow

Downloading and image processing are separate jobs. Add Pillow only when you need to open, inspect, resize, convert, or otherwise transform the file.

from PIL import Image

with Image.open("image.jpg") as image:
    print(image.format, image.size, image.mode)
    image.thumbnail((1200, 1200))
    image.save("image-small.jpg")

Pillow’s Image.open accepts a filename/path or a file-like object. Keeping the download on disk first makes retries, auditing, and later processing simpler; processing an in-memory response is also possible when the file is small and you have already checked the response.

Download several images

For a small, known list, call the same function for each URL and give every item a controlled destination:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
import requests
from pathlib import Path

items = [
    ("https://example.com/a.jpg", "a.jpg"),
    ("https://example.com/b.png", "b.png"),
]

Path("downloads").mkdir(exist_ok=True)
for url, name in items:
    destination = Path("downloads") / name
    with requests.get(url, stream=True, timeout=30) as response:
        response.raise_for_status()
        with destination.open("wb") as image_file:
            for chunk in response.iter_content(8192):
                if chunk:
                    image_file.write(chunk)

For production batch work, add an explicit concurrency limit, per-file size limit, retry rules appropriate to the API, logging, and cancellation. Those policies depend on the service and are not guaranteed by urllib.request or Requests.

urllib.request versus Requests

Approach Extra package Best fit Large-response handling
urllib.request.urlretrieve None; standard library A compact one-off download Convenient, but less explicit control
urllib.request.urlopen None; standard library Dependency-free code needing headers or a timeout Read and write bytes yourself
Requests Install Requests Readable request options, status checks, sessions, and streaming stream=True with iter_content
Requests plus Pillow Install both Download followed by image inspection or transformation Stream to disk, then open the path

Python’s urlretrieve can raise ContentTooShortError when fewer bytes arrive than the server’s declared Content-Length, such as after an interrupted transfer. Catch the exception if you need to report or retry that case.

Troubleshooting

“ModuleNotFoundError: No module named requests”

Install Requests in the same Python environment that runs the script: python -m pip install requests. Virtual environments help prevent installing into a different interpreter.

The script hangs

Set a timeout on the request. A timeout is not a guarantee that a whole download completes within that many seconds; it prevents indefinite waiting for network operations. For slow large files, choose a value that matches the service and your expected transfer time.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

An HTTP error is saved as an image

Call response.raise_for_status() before opening the output file, and inspect Content-Type. With urlretrieve, use an explicit urlopen request when you need this level of control.

The file is unreadable or corrupted

Confirm that the file was opened with wb, that the response completed, and that the URL did not return HTML or JSON. For interrupted urlretrieve downloads, handle ContentTooShortError and retry according to the server’s rules.

Connections accumulate during streaming

Use Requests as a context manager, consume every chunk, or explicitly close the response. This releases the connection for reuse.

TLS certificate errors

Check the hostname, system clock, certificate chain, and local trust store. Keep certificate verification enabled. Disabling it exposes the download to interception and should not be a routine fix.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Or skip the browser setup

If what you really need is a screenshot of a web page rather than an image file already exposed by an image URL, ScreenshotNeo returns a PNG, JPEG, WebP, or PDF from one GET request. It accepts cookie and consent banners like a visitor, then removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture. Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers identify the page verdict and billing result.

Using its API from Python:

import requests

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
    timeout=90,
)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)

See the complete option list and request details in the ScreenshotNeo documentation. The same endpoint also works with cURL:

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

Or Node.js:

const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

ScreenshotNeo includes full-page and element capture, device and retina settings, dark mode, PDF controls, custom CSS and JavaScript, waits, request blocking, headers and cookies, geolocation, resizing, caching, signed links, asynchronous webhooks, bulk capture, a usage API, and an MCP server with take_screenshot, get_page_info, and capture_pdf for AI clients. Every feature is on every plan. The Free plan includes 1,000 screenshots per month without a card; paid plans start at $5 for 3,000. Create a free ScreenshotNeo account.

FAQ

Can I download an image without installing anything?

Yes. urllib.request is part of Python’s standard library. Install Requests or Pillow only when their additional interfaces are useful.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Should I use urlretrieve in a production service?

It can be adequate for simple trusted downloads, but an explicit Requests or urlopen loop makes timeout, status, streaming, and cleanup behavior easier to control.

Does a successful HTTP response prove the file is safe?

No. HTTP success and a plausible content type do not establish that untrusted image data is harmless. Apply the validation, size limits, isolation, and processing policy required by your application.

Frequently Asked Questions

Can I download an image without installing anything?

Yes. Python’s built-in urllib.request handles basic downloads; Requests and Pillow are optional.

Does a successful HTTP response prove the file is safe?

No. Validate untrusted content and apply appropriate size, isolation, and processing controls.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Leave a Reply

Your email address will not be published. Required fields are marked *

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Read next

Recommended PC Tool
Recommended PC Tool
Outdated Drivers Are Slowing You DownFree scan - exact matches
PC Slower Than It Used to Be?Free scan - under a minute

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.