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'}}
Stack trace
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'}} 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
Uncompressed high-resolution photos (12MP+ smartphone photos, DSLR RAW converted to PNG/BMP)
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
Base64-encoded image in messages is already over 20MB before transmission
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
Using detail='high' with images larger than 2048×2048 pixels
Either resize to ≤2048×2048 with PIL Image.resize(), or switch to detail='low' which accepts larger images (but provides less visual detail)
URL-based image points to unoptimized remote file (full-resolution CDN image, PNG instead of JPEG)
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
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) 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 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.