Python
image processing
URL
data extraction
code tutorial

How do I read image data from a URL in Python?

ML System Design practice on Codemia

Design recommenders, ranking systems and training pipelines the way ML interviews actually ask for them, with worked solutions.

Practice ML system design

Introduction

Reading an image from a URL in Python usually means two steps: download the bytes, then decode those bytes into an image object. Which library you use for the second step depends on whether you want general image handling, computer-vision processing, or just raw binary access.

The Usual Pattern: requests Plus Pillow

A simple and widely used combination is requests for HTTP and Pillow for decoding the image.

python
1from io import BytesIO
2import requests
3from PIL import Image
4
5url = "https://example.com/image.png"
6response = requests.get(url, timeout=10)
7response.raise_for_status()
8
9image = Image.open(BytesIO(response.content))
10print(image.size)
11print(image.mode)

This works well when you want a PIL.Image object for resizing, saving, or general processing.

Why BytesIO Is Used

Image.open expects a filename or a file-like object. response.content is just raw bytes, so BytesIO wraps those bytes in an in-memory stream that Pillow can read like a file.

That small detail is the bridge between the downloaded HTTP payload and the image library.

OpenCV Version

If your workflow is based on OpenCV, decode the bytes into a NumPy array and then call cv2.imdecode.

python
1import requests
2import numpy as np
3import cv2
4
5url = "https://example.com/image.jpg"
6response = requests.get(url, timeout=10)
7response.raise_for_status()
8
9arr = np.frombuffer(response.content, dtype=np.uint8)
10image = cv2.imdecode(arr, cv2.IMREAD_COLOR)
11
12print(image.shape)

This gives you an OpenCV image array rather than a Pillow object. That is useful when the next steps are computer-vision operations.

Save the Image to Disk if Needed

Sometimes you do not need to process the image immediately. You just want to download and save it.

python
1import requests
2
3url = "https://example.com/image.png"
4response = requests.get(url, timeout=10)
5response.raise_for_status()
6
7with open("downloaded.png", "wb") as f:
8    f.write(response.content)

This is the simplest option when decoding can happen later.

Add Basic Validation and Error Handling

URLs fail for normal reasons: timeouts, redirects, missing files, or responses that are not actually images. A small amount of validation makes the code more robust.

python
1from io import BytesIO
2import requests
3from PIL import Image, UnidentifiedImageError
4
5url = "https://example.com/image.png"
6
7try:
8    response = requests.get(url, timeout=10)
9    response.raise_for_status()
10
11    content_type = response.headers.get("Content-Type", "")
12    if not content_type.startswith("image/"):
13        raise ValueError(f"Unexpected content type: {content_type}")
14
15    image = Image.open(BytesIO(response.content))
16    print(image.size)
17except (requests.RequestException, UnidentifiedImageError, ValueError) as exc:
18    print(f"Failed to load image: {exc}")

This is especially important if the URL comes from user input or an external API.

Streaming Matters for Large Downloads

For very large images or bulk downloads, a streaming request can be more memory-efficient than loading everything into memory immediately.

That said, for ordinary single-image use, response.content is usually fine. Optimize only when the workload justifies it.

Repeated Downloads Benefit From Sessions

If you are downloading many images from the same host, a requests.Session() can reduce connection overhead and make retry behavior easier to centralize.

Common Pitfalls

  • Forgetting response.raise_for_status() and silently processing an error page as if it were an image.
  • Passing raw bytes directly to Pillow without wrapping them in BytesIO.
  • Assuming the URL always returns image content instead of validating the response.
  • Mixing Pillow and OpenCV objects without noticing they use different data layouts and conventions.
  • Ignoring timeout settings and letting network calls hang longer than expected.

Summary

  • Download the image bytes first, then decode them with the appropriate library.
  • Use requests plus Pillow for general image handling.
  • Use cv2.imdecode when your processing pipeline is based on OpenCV.
  • Validate HTTP status and content type for safer code.
  • Save to disk directly when you do not need immediate in-memory decoding.

Related reading
Free course
Beginner
7 lessons
2 hours
Tackling System Design Interview Problems

A short course that equips you with the skills to approach system design interviews methodically.

Start the free course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

ML System Design practice on Codemia

Design recommenders, ranking systems and training pipelines the way ML interviews actually ask for them, with worked solutions.

Practice ML system design

All Rights Reserved.