Use Image URLs
Referencing an Image by URL
Instead of sending image data, you can give Claude a URL, and the API fetches the image itself:
{
"type": "image",
"source": {
"type": "url",
"url": "https://platform.claude.com/docs/images/vision-example.jpg",
},
}
The request stays small, because it contains only the link. This is convenient for public images, such as product photos on a website, diagrams in documentation, or images that already live in a content management system.
When to Use a URL, and When Not To
| Situation | Best source type |
|---|---|
| A public image that will not change | url |
| An image uploaded by a user | base64 or file_id, never a URL the user supplies |
| The same image used in many requests | file_id (Lesson 116) |
| A private image behind a login | Not possible by URL; send base64 or upload (Lessons 113 and 115) |
A URL is a reference, not a copy. Three consequences follow:
- The content can change. The image at a URL may be replaced tomorrow. If you need to reproduce a result, keep the image itself, or a file ID.
- The image must be reachable. The URL must be public, and the request fails if the server cannot fetch it.
- You do not control what is fetched. If you accept arbitrary URLs from users, anyone can point your application at any image on the web, including content you would not want to process or store.
Restricting URLs
When URLs come from users or from external content, restrict them before sending. An allowlist of trusted hosts is the simplest safeguard:
from urllib.parse import urlparse
ALLOWED_HOSTS = {"platform.claude.com", "images.example.com"}
def check_image_url(url: str) -> str:
parts = urlparse(url)
if parts.scheme != "https":
raise ValueError("image URLs must use https")
if parts.hostname not in ALLOWED_HOSTS:
raise ValueError(f"images may only come from approved hosts, not {parts.hostname!r}")
return url
An allowlist is stricter than a blocklist. A blocklist tries to list everything bad and inevitably misses something; an allowlist accepts only what you have approved.
Practical Example: Describing an Image from a URL
This example checks the URL against the allowlist, then asks Claude about the image.
"""url_image.py: ask about an image referenced by an approved URL."""
from urllib.parse import urlparse
from anthropic import Anthropic
client = Anthropic()
ALLOWED_HOSTS = {"platform.claude.com"}
IMAGE_URL = "https://platform.claude.com/docs/images/vision-example.jpg"
def check_image_url(url: str) -> str:
parts = urlparse(url)
if parts.scheme != "https":
raise ValueError("image URLs must use https")
if parts.hostname not in ALLOWED_HOSTS:
raise ValueError(f"images may only come from approved hosts, not {parts.hostname!r}")
return url
def ask_about_url(url: str, question: str) -> str:
message = client.messages.create(
model="claude-opus-5-5",
max_tokens=1024,
messages=[{"role": "user", "content": [
{"type": "image", "source": {"type": "url", "url": check_image_url(url)}},
{"type": "text", "text": question},
]}],
)
return "".join(block.text for block in message.content if block.type == "text").strip()
if __name__ == "__main__":
print(ask_about_url(IMAGE_URL, "Describe this image in two sentences."))
try:
check_image_url("https://example.com/cat.jpg")
except ValueError as error:
print("Rejected:", error)
Example output (the description depends on the image at the URL):
<A two-sentence description of the image>
Rejected: images may only come from approved hosts, not 'example.com'
How It Works
check_image_url()runs before any request is made. It requireshttps, and checks the host against the allowlist, so a rejected URL never reaches the API.urlparse()splits the URL into its parts, andparts.hostnamegives the host without any port or credentials. Comparing parts is safer than searching the string, because a string search can be fooled by URLs such ashttps://platform.claude.com.evil.example/.- The URL source is used exactly like the base64 source, only the
sourcedictionary differs. - The second call shows the rejection path, which is what a real application would do with a user-supplied URL: refuse it with a clear message.
Common Mistakes
Passing User URLs Straight Through
What happens: users can make your application process arbitrary images from the web.
Fix: allowlist the hosts, or accept uploads instead.
Checking URLs by Substring
What happens: "platform.claude.com" in url accepts https://platform.claude.com.attacker.example/.
Fix: compare the parsed host name exactly.
Assuming the URL Always Works
What happens: links break, return errors, or point to pages instead of images.
Fix: handle request failures with clear errors (Lesson 120), and store images you need to keep.
Relying on a URL for Reproducibility
What happens: the same request gives different results after the image changes.
Fix: keep the image, or its file ID, alongside your results.
Best Practices
- Prefer uploads or base64 for images that users provide.
- Use an allowlist of hosts, and require
https. - Compare parsed hostnames, never substrings.
- Store the image, not just its link, when results must be reproducible.
- Handle fetch failures as ordinary errors.
Exercise
- Add a second approved host and test a URL on each host.
- Try
http://platform.claude.com/...(without thes). What happens, and why does the check reject it? - Write a test with
https://platform.claude.com@evil.example/image.jpg. Which host doesurlparse()report, and does the check reject the URL? - Explain why a URL reference is not suitable for an image that a user uploaded to your application.
Step 3: urlparse() reports evil.example as the hostname. The part before the @ is interpreted as user information, not as the host. Because the check compares the parsed hostname, the URL is rejected. A substring check would wrongly accept it. This is exactly why comparing parsed parts is the safer approach.
Step 4: a URL reference depends on the uploaded file remaining reachable at that address, which is not guaranteed. The file may be deleted, moved behind a login, or replaced. Also, the URL would have to be public, which conflicts with the privacy expectations of a user's upload. Base64 or the Files API keeps the image under your control.