Skip to main content
Lesson 4 of 12 · Vision and Image Analysis via API

Send an Image via URL

Lesson 4 of 12 Updated November 2, 2026 Developer
On this page
  1. The simplest method
  2. Message format
  3. Complete Python example
  4. Adding a system prompt
  5. URL requirements
  6. Images from cloud storage
  7. Error handling
  8. Key takeaways

The simplest method

Sending an image via URL is the most direct way to use the Vision API: you provide the address of a publicly accessible image, and Mistral downloads it for analysis. Your request stays lightweight—a few hundred bytes—since it carries only a link.

Prioritize this method when your images are already hosted on a CDN, cloud storage, or website, or when you’re working with public web images. A team that already stores product visuals on a CDN can start analyzing them without changing their infrastructure.

Message format

A multimodal message with an image URL follows this structure:

message = {
    "role": "user",
    "content": [
        {
            "type": "text",
            "text": "Your question about the image"
        },
        {
            "type": "image_url",
            "image_url": "https://example.com/my-image.jpg"
        }
    ]
}

The image_url field accepts any HTTP or HTTPS URL pointing to a valid image file in JPEG, PNG, GIF, or WebP format.

Complete Python example

Here is a complete, ready-to-run script that analyzes a public image from Wikimedia, assuming your API key is set in the environment:

from mistralai.client import Mistral
import os

# Initialize the client
client = Mistral(api_key=os.environ["MISTRAL_API_KEY"])

# Public image to analyze
url_image = "https://upload.wikimedia.org/wikipedia/commons/thumb/a/a7/Camponotus_flavomarginatus_ant.jpg/800px-Camponotus_flavomarginatus_ant.jpg"

# Call the Vision API
response = client.chat.complete(
    model="mistral-small-latest",
    messages=[
        {
            "role": "user",
            "content": [
                {
                    "type": "text",
                    "text": "Describe precisely what you see in this image."
                },
                {
                    "type": "image_url",
                    "image_url": url_image
                }
            ]
        }
    ]
)

print(response.choices[0].message.content)

Note that we pass the key via os.environ rather than hardcoding it. This is the only acceptable approach once the script leaves your local machine.

Adding a system prompt

The response above will be correct but generic—“a black ant on a blurry background.” A system message changes the nature of the analysis by assigning a role to the model before it views the image:

response = client.chat.complete(
    model="mistral-small-latest",
    messages=[
        {
            "role": "system",
            "content": "You are an entomology expert. Analyze insect images with scientific precision, identifying the species, family, and visible morphological characteristics."
        },
        {
            "role": "user",
            "content": [
                {"type": "text", "text": "Identify this insect."},
                {"type": "image_url", "image_url": url_image}
            ]
        }
    ]
)

With this framing, the same image yields a taxonomic identification and a morphological description. The system prompt guides how the model analyzes the image—a powerful lever for obtaining specialized responses at the cost of just a few dozen tokens.

URL requirements

The API must be able to download the image without any context from your session. This leads to four requirements: the URL must be publicly accessible (no pages behind authentication); it must point directly to the file, not to an HTML page containing the image; the format must be supported (JPEG, PNG, GIF where only the first frame is read, or WebP); and the size must remain reasonable, under 20 MB.

The most common confusion involves share links. A Google Drive or Dropbox link “shared with everyone” returns an HTML page, not an image file: the call fails, or the model describes a web interface. The best practice is to verify the Content-Type before sending:

import requests

def verify_image_url(url):
    """Verify that a URL points to an accessible image."""
    try:
        response = requests.head(url, timeout=5, allow_redirects=True)
        content_type = response.headers.get("Content-Type", "")

        if response.status_code == 200 and "image" in content_type:
            size = int(response.headers.get("Content-Length", 0))
            print(f"Image accessible: {content_type}, {size / 1024:.0f} KB")
            return True
        else:
            print(f"Issue: status={response.status_code}, type={content_type}")
            return False
    except requests.RequestException as e:
        print(f"Connection error: {e}")
        return False

verify_image_url("https://example.com/photo.jpg")

A HEAD request is sufficient: it retrieves headers without downloading the file.

Images from cloud storage

This public accessibility requirement seems incompatible with confidential documents. The solution lies in pre-signed URLs: if your images are in an S3 bucket, Google Cloud Storage, or Azure Blob, you generate a temporary address that grants access to a single object for a duration you specify.

import boto3

s3 = boto3.client("s3")

# Generate a pre-signed URL valid for 1 hour
presigned_url = s3.generate_presigned_url(
    "get_object",
    Params={"Bucket": "my-bucket", "Key": "images/invoice.png"},
    ExpiresIn=3600
)

# Use this URL with the Vision API
response = client.chat.complete(
    model="mistral-small-latest",
    messages=[
        {
            "role": "user",
            "content": [
                {"type": "text", "text": "Extract the total amount from this invoice."},
                {"type": "image_url", "image_url": presigned_url}
            ]
        }
    ]
)

Your bucket remains private, the URL expires after the specified delay, and you retain the lightweight nature of the request. Set ExpiresIn as short as possible: a few minutes are more than enough for an API call.

Error handling

A call that depends on a remote download can fail for reasons beyond your control—the image was deleted, the URL expired, or the hosting server is unavailable. In production, never let the raw exception bubble up to the user:

from mistralai.client import Mistral

client = Mistral(api_key=os.environ["MISTRAL_API_KEY"])

try:
    response = client.chat.complete(
        model="mistral-small-latest",
        messages=[
            {
                "role": "user",
                "content": [
                    {"type": "text", "text": "Describe this image."},
                    {"type": "image_url", "image_url": "https://example.com/image.jpg"}
                ]
            }
        ]
    )
    print(response.choices[0].message.content)
except Exception as e:
    print(f"Error: {e}")

Test your error handling with a deliberately invalid URL before deploying: this is the most frequent scenario and the one least often tested.

Key takeaways

  • Sending via URL is the simplest method—a public URL is all you need
  • The message content field is an array containing text and image_url elements
  • The URL must point directly to an accessible image file
  • Pre-signed URLs allow you to use private images securely
  • Always add a system prompt to guide the model’s analysis