High severity HTTP 400 intermediate · Fix: 5-10 min

image_size_exceeds_limit

openai.BadRequestError: Error code: 400: {'error': {'message': 'Image size exceeds maximum limit', 'type': 'invalid_request_error', 'code': 'image_size_exceeds_limit'}}

What this error means
OpenAI's vision API rejects images that exceed size limits: base64 images over 20MB or URLs pointing to images larger than 20MB, or images with dimensions exceeding 2048x2048 pixels for detail='high'.

Stack trace

traceback
openai.BadRequestError: Error code: 400 — {'error': {'message': 'Image size exceeds maximum limit', 'type': 'invalid_request_error', 'code': 'image_size_exceeds_limit'}}

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "vision_script.py", line 42, in <module>
    response = client.chat.completions.create(
  File "/usr/local/lib/python3.11/site-packages/openai/resources/chat/completions.py", line 680, in create
    return self._post(
  File "/usr/local/lib/python3.11/site-packages/openai/base_client.py", line 1236, in post
    return self._request(
  File "/usr/local/lib/python3.11/site-packages/openai/base_client.py", line 930, in _request
    raise self._make_status_error_from_response(err.response) from None
openai.BadRequestError: Error code: 400 — {'error': {'message': 'Image size exceeds maximum limit', 'type': 'invalid_request_error', 'code': 'image_size_exceeds_limit'}}
QUICK FIX
Use PIL to resize and compress: Image.open('large.jpg').resize((1280, 960)).save('fixed.jpg', quality=85, optimize=True), then base64 encode and send: reduces size to <5MB in seconds.

Why it happens

OpenAI's vision models (GPT-4o, gpt-4o-mini, o4-mini) enforce strict size limits to prevent excessive API costs and latency. Base64-encoded images must not exceed 20MB in size; URL-based images must point to files under 20MB; and for detail='high', images cannot exceed 2048×2048 pixels. Uncompressed high-resolution photos, scanned documents, or multi-page PDFs converted to images easily exceed these thresholds. The API validates image dimensions and byte size before processing, failing fast rather than timing out.

Detection

Before sending images to OpenAI, validate file size with os.path.getsize() (base64 source must be <20MB) and check dimensions with PIL/Pillow. Log rejected images with their sizes to identify compression needs early. For URL-based images, test HEAD requests to get Content-Length headers without downloading the full image.

Causes & fixes

1

Uncompressed high-resolution photos (12MP+ smartphone photos, DSLR RAW converted to PNG/BMP)

✓ Fix

Compress images to JPEG with PIL: Image.open('photo.jpg').save('compressed.jpg', quality=85, optimize=True): reduces size by 60-80% with minimal quality loss for vision tasks

2

Base64-encoded image in messages is already over 20MB before transmission

✓ Fix

Check encoded size with len(base64_string) * 0.75 to get approximate byte size; if over 20MB, resize with PIL: Image.open('image.jpg').resize((1024, 768)).save('small.jpg') before encoding

3

Using detail='high' with images larger than 2048×2048 pixels

✓ Fix

Either resize to ≤2048×2048 with PIL Image.resize(), or switch to detail='low' which accepts larger images (but provides less visual detail)

4

URL-based image points to unoptimized remote file (full-resolution CDN image, PNG instead of JPEG)

✓ Fix

Implement server-side image optimization: resize and compress on your CDN before passing URL to OpenAI, or download, compress locally, then base64 encode and embed directly

Code: broken vs fixed

Broken - triggers the error
python
import os
from openai import OpenAI
import base64

client = OpenAI(api_key=os.environ.get('OPENAI_API_KEY'))

# Read large uncompressed image (15MB)
with open('large_photo.png', 'rb') as f:
    image_data = base64.standard_b64encode(f.read()).decode('utf-8')

# This fails with image_size_exceeds_limit because PNG is uncompressed
response = client.chat.completions.create(
    model='gpt-4o',
    messages=[
        {
            'role': 'user',
            'content': [
                {'type': 'text', 'text': 'Describe this image'},
                {
                    'type': 'image_url',
                    'image_url': {'url': f'data:image/png;base64,{image_data}'}
                }
            ]
        }
    ]
)
print(response.choices[0].message.content)
Fixed - works correctly
python
import os
from openai import OpenAI, BadRequestError
from PIL import Image
import base64
from io import BytesIO

client = OpenAI(api_key=os.environ.get('OPENAI_API_KEY'))

# Compress and resize large image before encoding
with Image.open('large_photo.png') as img:
    # Resize to max 1280x960 and convert RGBA to RGB if needed
    img.thumbnail((1280, 960), Image.Resampling.LANCZOS)
    if img.mode == 'RGBA':
        img = img.convert('RGB')
    
    # Save as JPEG with quality=85 to reduce size from 15MB to ~800KB
    buffer = BytesIO()
    img.save(buffer, format='JPEG', quality=85, optimize=True)
    image_data = base64.standard_b64encode(buffer.getvalue()).decode('utf-8')

print(f'Compressed image size: {len(image_data) * 0.75 / 1024 / 1024:.2f}MB')  # Verify <20MB

try:
    response = client.chat.completions.create(
        model='gpt-4o',
        messages=[
            {
                'role': 'user',
                'content': [
                    {'type': 'text', 'text': 'Describe this image'},
                    {
                        'type': 'image_url',
                        'image_url': {'url': f'data:image/jpeg;base64,{image_data}'}
                    }
                ]
            }
        ]
    )
    print(response.choices[0].message.content)
except BadRequestError as e:
    if 'image_size_exceeds_limit' in str(e):
        print('Image still too large after compression — resize smaller or use detail=low')
    raise
Added PIL image compression (resize to 1280×960, save as JPEG with quality=85) before base64 encoding — reduces 15MB PNG to ~800KB JPEG, well under the 20MB limit. Also added size verification and proper exception handling for future-proofing.

Workaround

If you cannot install PIL/Pillow, use an external image compression API: POST raw image bytes to TinyPNG or Compressor.io's API, get back compressed bytes, base64 encode, and send to OpenAI. Trade-off: adds 1-2 seconds latency and small API costs, but requires zero local dependencies.

Prevention

Build a vision preprocessing pipeline into your application: (1) validate image dimensions on upload with PIL; (2) auto-resize images >1280px width to max 1280×960; (3) convert PNG/BMP to JPEG with quality=85 before Vision API calls; (4) log all image metadata (original size, compressed size, duration) to catch future regressions. For URL-based images, implement CDN-level image optimization via CloudFlare Image Optimization or AWS CloudFront Lambda@Edge to serve pre-compressed versions.

Python 3.9+ · openai >=1.0.0 · tested on 1.40.0+
Verified 2026-04 · gpt-4o, gpt-4o-mini, o4-mini
Verify ↗

Community Notes

No notes yetBe the first to share a version-specific fix or tip.