OpenAI Vision API Multimodal 9 min read

OpenAI Vision API Guide 2025: Analyze Images with GPT-4o

GPT-4o and GPT-4o-mini can analyze images natively — no separate model, no separate endpoint. Pass an image URL or base64-encoded data alongside your text prompt in the standard chat completions messages array, and the model returns a detailed analysis. This guide covers every aspect of the Vision API: content types, the detail parameter, OCR, screenshot analysis, chart reading, multi-image comparison, streaming, and cost optimization.

What Is OpenAI Vision?

OpenAI Vision is the multimodal capability built into GPT-4o and GPT-4o-mini. Unlike earlier approaches that required a separate vision model, GPT-4o is natively multimodal — it processes images and text in the same forward pass, enabling richer cross-modal reasoning.

Common use cases include:

  • OCR and document analysis — extract text from scanned documents, receipts, invoices, PDFs rendered as images
  • UI and screenshot analysis — describe what is on screen, find bugs in a UI, extract form field values
  • Chart and data extraction — read bar charts, line graphs, pie charts, and tables from images
  • Object detection and scene description — count objects, identify products, describe scenes
  • Medical and scientific imaging — analyze X-rays, microscopy images, lab results (with appropriate disclaimers)

Images are passed using the image_url content type in the messages array — the same endpoint as text completions (POST /v1/chat/completions).

Basic Image URL Example

The simplest way to use Vision is to pass a publicly accessible image URL. The model fetches and processes the image server-side — no upload required:

vision_url.py

from openai import OpenAI

client = OpenAI()  # uses OPENAI_API_KEY env var

response = client.chat.completions.create(
    model="gpt-4o",
    messages=[
        {
            "role": "user",
            "content": [
                {
                    "type": "text",
                    "text": "What is in this image? Describe it in detail."
                },
                {
                    "type": "image_url",
                    "image_url": {
                        "url": "https://upload.wikimedia.org/wikipedia/commons/thumb/4/47/PNG_transparency_demonstration_1.png/280px-PNG_transparency_demonstration_1.png"
                    }
                }
            ]
        }
    ],
    max_tokens=500,
)

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

The content array mixes text and image_url blocks freely. You can have multiple text and image blocks in any order.

Base64 Image Encoding

For local files or private images, encode them as base64 and pass as a data URI. Supported formats: PNG, JPEG, GIF, WebP. Maximum size per image: 20 MB.

vision_base64.py

import base64
from openai import OpenAI

client = OpenAI()

def encode_image(path: str) -> str:
    with open(path, "rb") as f:
        return base64.b64encode(f.read()).decode("utf-8")

image_b64 = encode_image("screenshot.png")

response = client.chat.completions.create(
    model="gpt-4o",
    messages=[
        {
            "role": "user",
            "content": [
                {
                    "type": "text",
                    "text": "Describe the UI elements visible in this screenshot."
                },
                {
                    "type": "image_url",
                    "image_url": {
                        "url": f"data:image/png;base64,{image_b64}"
                    }
                }
            ]
        }
    ],
    max_tokens=800,
)

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

Change the MIME type to match your file: image/jpeg, image/gif, or image/webp. The data URI scheme (data:image/png;base64,...) is the same format used in HTML and CSS.

Multiple Images in One Request

GPT-4o can process multiple images in a single request — useful for comparison, before/after analysis, or multi-page document processing:

multi_image.py

import base64
from openai import OpenAI

client = OpenAI()

def b64(path):
    with open(path, "rb") as f:
        return base64.b64encode(f.read()).decode("utf-8")

response = client.chat.completions.create(
    model="gpt-4o",
    messages=[
        {
            "role": "user",
            "content": [
                {"type": "text", "text": "Compare these two UI screenshots. What changed between v1 and v2?"},
                {
                    "type": "image_url",
                    "image_url": {"url": f"data:image/png;base64,{b64('ui_v1.png')}"}
                },
                {
                    "type": "image_url",
                    "image_url": {"url": f"data:image/png;base64,{b64('ui_v2.png')}"}
                }
            ]
        }
    ],
    max_tokens=1000,
)

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

Each additional image costs additional tokens based on its size and the detail setting. When passing many images, consider using detail: "low" to keep costs predictable.

The detail Parameter: low, high, auto

The detail field inside image_url controls how GPT-4o tiles and processes the image:

detail value Token cost Processing Best for
low Always 85 tokens Resized to 512×512 Quick classification, scene summary, cost-sensitive tasks
high 85 + 170 per 512×512 tile Adaptive tiling up to 2048px OCR, charts, fine text, detailed diagrams
auto (default) Varies Model decides General use when cost is not a primary concern

Setting detail explicitly

{
    "type": "image_url",
    "image_url": {
        "url": "https://example.com/chart.png",
        "detail": "high"   # or "low" or "auto"
    }
}

For a 1024×1024 image at high, the model creates four 512×512 tiles plus one 512×512 overview = 5 tiles × 170 tokens + 85 base = 935 tokens. At low, the same image costs only 85 tokens.

Practical Use Cases with Code

Three of the most common production use cases for GPT-4o Vision, each with a ready-to-run code snippet:

Screenshot / UI Analysis

ui_analysis.py

import base64
from openai import OpenAI

client = OpenAI()

with open("app_screenshot.png", "rb") as f:
    b64 = base64.b64encode(f.read()).decode()

response = client.chat.completions.create(
    model="gpt-4o",
    messages=[{
        "role": "user",
        "content": [
            {"type": "text", "text": (
                "You are a QA engineer. List every UI element visible in this screenshot "
                "and identify any visual bugs, misaligned elements, or UX issues."
            )},
            {"type": "image_url", "image_url": {
                "url": f"data:image/png;base64,{b64}",
                "detail": "high"
            }}
        ]
    }],
    max_tokens=1500,
)
print(response.choices[0].message.content)

OCR / Document Text Extraction

ocr_extract.py

import base64
from openai import OpenAI

client = OpenAI()

with open("invoice.jpg", "rb") as f:
    b64 = base64.b64encode(f.read()).decode()

response = client.chat.completions.create(
    model="gpt-4o",
    messages=[{
        "role": "user",
        "content": [
            {"type": "text", "text": (
                "Extract all text from this invoice image. "
                "Return a structured JSON with: vendor, date, line_items (list), total."
            )},
            {"type": "image_url", "image_url": {
                "url": f"data:image/jpeg;base64,{b64}",
                "detail": "high"
            }}
        ]
    }],
    max_tokens=1000,
    response_format={"type": "json_object"},
)
print(response.choices[0].message.content)

Chart Reading and Data Extraction

chart_extract.py

import base64
from openai import OpenAI

client = OpenAI()

with open("revenue_chart.png", "rb") as f:
    b64 = base64.b64encode(f.read()).decode()

response = client.chat.completions.create(
    model="gpt-4o",
    messages=[{
        "role": "user",
        "content": [
            {"type": "text", "text": (
                "Extract the data from this bar chart as JSON. "
                "Include: chart_title, x_axis_label, y_axis_label, "
                "and data_points as an array of {label, value} objects."
            )},
            {"type": "image_url", "image_url": {
                "url": f"data:image/png;base64,{b64}",
                "detail": "high"
            }}
        ]
    }],
    max_tokens=800,
    response_format={"type": "json_object"},
)
print(response.choices[0].message.content)

Streaming Vision Responses

Streaming works identically for vision requests — just add stream=True. This is especially useful for long descriptions or document analysis where you want the user to see partial results immediately:

vision_stream.py

import base64
from openai import OpenAI

client = OpenAI()

with open("diagram.png", "rb") as f:
    b64 = base64.b64encode(f.read()).decode()

stream = client.chat.completions.create(
    model="gpt-4o",
    messages=[{
        "role": "user",
        "content": [
            {"type": "text", "text": "Explain this architecture diagram step by step."},
            {"type": "image_url", "image_url": {
                "url": f"data:image/png;base64,{b64}",
                "detail": "high"
            }}
        ]
    }],
    max_tokens=1500,
    stream=True,
)

for chunk in stream:
    delta = chunk.choices[0].delta.content
    if delta:
        print(delta, end="", flush=True)
print()  # newline after stream ends

Streaming vision is available in both the Python SDK and via the raw HTTP API with Server-Sent Events. The image is processed before the first token streams — expect a brief pause at the start proportional to image size.

GPT-4o vs GPT-4o-mini Vision

Both models support the Vision API with identical syntax. The tradeoff is quality vs cost:

Model Input price Vision quality Best for
gpt-4o $2.50 / 1M tokens Excellent Complex charts, handwriting, multi-step reasoning, medical images
gpt-4o-mini $0.15 / 1M tokens Good Simple OCR, image classification, scene description, high-volume pipelines

GPT-4o-mini is approximately 16x cheaper on input tokens. For a pipeline processing 10,000 invoices/day at detail: "high" (avg ~600 image tokens each), switching from gpt-4o to gpt-4o-mini cuts image token costs from ~$15/day to ~$0.90/day. Always benchmark accuracy on your specific task before committing to the cheaper model.

Vision API Limits and Pricing

Key limits and pricing details to know before building vision pipelines:

  • Max image size: 20 MB per image (after base64 decoding)
  • Supported formats: PNG, JPEG, GIF, WebP only — no PDF, SVG, or video
  • Video: not supported — extract frames and pass as separate image blocks
  • Max images per request: no hard limit documented, but practical limit is ~10-20 before hitting context length
  • Image tokens at low detail: always 85 tokens regardless of image dimensions
  • Image tokens at high detail: 85 base + 170 per 512×512 tile; a 1024×1024 image = 935 tokens; a 2048×2048 image = ~1785 tokens
  • Rate limits: image requests count toward your standard TPM (tokens per minute) quota — large high-detail images can consume quota quickly
  • No storage: OpenAI does not store images after processing; images sent via URL are fetched in real time

Token estimation for a 1920×1080 screenshot at high detail

# 1920x1080 scaled to fit 2048x2048 -> stays 1920x1080
# Tiles: ceil(1920/512) x ceil(1080/512) = 4x3 = 12 tiles
# Plus 1 overview tile = 13 tiles total
# Token cost: 13 * 170 + 85 = 2295 image tokens
# At gpt-4o:      2295 * $2.50/1M  = ~$0.0057 per image
# At gpt-4o-mini: 2295 * $0.15/1M  = ~$0.00034 per image

Monitor OpenAI API Status in Real Time

GPT-4o Vision calls count against OpenAI's API quota. Prismix monitors OpenAI's status in real time — so when the Vision API has elevated latency or errors, you know before your users do.

Monitor OpenAI Status →