Claude 4.5 API: A Guide to Calculating and Optimizing Image Input Tokens

Claude 4.5 API: A Guide to Calculating and Optimizing Image Input Tokens
Photo by pine watt / Unsplash

Hello!

In this article, we take a detailed look at how image token counts are calculated when using Claude 4.5 Sonnet/Haiku and Claude 4.1 Opus via the API.

How Image Token Counts Are Calculated

Images sent to the Claude 4.5 API are counted as tokens, just like text, and form the basis of billing. If an image is within the API's size limits and needs no resizing, you can estimate its token count with the following simple formula.

Basic formula
tokens = (width px × height px) ÷ 750

Using this formula, you can predict cost before uploading and optimize images as needed. For example, a 1000×1000-pixel image consumes about 1,334 tokens, so under Claude 4.5's pricing you can calculate the cost per image in advance. A 1092×1092-pixel image (1.19 megapixels) comes to about 1,590 tokens, which you can also use as a baseline for estimating batch-processing costs.

Image Size Limits and Optimization

The Claude 4.5 API has several important limits on image size. A single API request can include up to 100 images, but the following constraints apply to their dimensions. As a baseline, images larger than 8000×8000 pixels are rejected, and when sending more than 20 images at once, the limit shrinks to 2000×2000 pixels. In addition, the total request size cannot exceed 32 MB.

From a performance standpoint, if an image's long edge exceeds 1,568 pixels, or if it exceeds roughly 1,600 tokens, the API automatically scales the image down while preserving its aspect ratio. This automatic resizing adds processing time, so resizing appropriately in advance can significantly improve response times.

Code Examples

1. Sending images as Base64

The most basic implementation encodes the image as Base64 and sends it to the API. This approach works well when sending local files or in-memory image data directly.

import base64
import anthropic
from pathlib import Path

client = anthropic.Anthropic(api_key="your-api-key")

# Encode the image to Base64
def encode_image(image_path):
    with open(image_path, "rb") as image_file:
        return base64.b64encode(image_file.read()).decode('utf-8')

# Estimate the token count in advance
def estimate_tokens(image_path):
    from PIL import Image
    img = Image.open(image_path)
    width, height = img.size
    tokens = (width * height) / 750
    return int(tokens)

image_path = "sample.jpg"
image_data = encode_image(image_path)
estimated_tokens = estimate_tokens(image_path)

print(f"Estimated tokens: {estimated_tokens}")

# Request to the Claude 4.5 API
message = client.messages.create(
    model="claude-3-opus-20240229",  # Specify the Claude 4.5 model
    max_tokens=1024,
    messages=[
        {
            "role": "user",
            "content": [
                {
                    "type": "image",
                    "source": {
                        "type": "base64",
                        "media_type": "image/jpeg",
                        "data": image_data
                    }
                },
                {
                    "type": "text",
                    "text": "Please analyze this image in detail"
                }
            ]
        }
    ]
)

print(message.content)

2. Sending images by URL reference

When processing images hosted online, specifying a URL directly avoids the encoding overhead. This approach is especially efficient when processing large numbers of images or integrating with external services.

const Anthropic = require('@anthropic-ai/sdk');

const anthropic = new Anthropic({
    apiKey: process.env.ANTHROPIC_API_KEY,
});

// Estimate token count from an image URL (size info required in advance)
async function estimateTokensFromURL(imageUrl) {
    // In a real implementation, fetch the image metadata
    const assumedWidth = 1200;
    const assumedHeight = 800;
    return Math.floor((assumedWidth * assumedHeight) / 750);
}

async function analyzeImageFromURL() {
    const imageUrl = "https://example.com/image.jpg";
    const estimatedTokens = await estimateTokensFromURL(imageUrl);
    
    console.log(`Estimated tokens: ${estimatedTokens}`);
    
    const message = await anthropic.messages.create({
        model: 'claude-3-opus-20240229',  // Claude 4.5 model
        max_tokens: 1024,
        messages: [{
            role: 'user',
            content: [
                {
                    type: 'image',
                    source: {
                        type: 'url',
                        url: imageUrl
                    }
                },
                {
                    type: 'text',
                    text: 'Identify the objects in the image and describe their layout'
                }
            ]
        }]
    });
    
    console.log(message.content);
}

analyzeImageFromURL();

3. Efficient multi-image processing and token optimization

When processing multiple images at once, you can optimize both cost and performance by tracking the total token count and resizing images as needed.

import anthropic
from PIL import Image
import io
import base64

class ImageTokenOptimizer:
    def __init__(self, api_key):
        self.client = anthropic.Anthropic(api_key=api_key)
        self.max_dimension = 1568  # Recommended maximum dimension
        self.target_megapixels = 1.15  # Optimal megapixel count
    
    def optimize_image(self, image_path):
        """Resize the image to the optimal size to reduce token count"""
        img = Image.open(image_path)
        width, height = img.size
        
        # Calculate the current token count
        current_tokens = (width * height) / 750
        
        # Check whether resizing is needed
        if max(width, height) > self.max_dimension:
            # Resize while preserving aspect ratio
            ratio = self.max_dimension / max(width, height)
            new_width = int(width * ratio)
            new_height = int(height * ratio)
            
            img = img.resize((new_width, new_height), Image.LANCZOS)
            optimized_tokens = (new_width * new_height) / 750
            
            print(f"Image resized: {width}x{height} → {new_width}x{new_height}")
            print(f"Tokens reduced: {int(current_tokens)} → {int(optimized_tokens)}")
        else:
            optimized_tokens = current_tokens
            print(f"No resize needed: tokens {int(optimized_tokens)}")
        
        # Base64 encode
        buffer = io.BytesIO()
        img.save(buffer, format='JPEG', quality=95)
        img_str = base64.b64encode(buffer.getvalue()).decode()
        
        return img_str, int(optimized_tokens)
    
    def batch_process_images(self, image_paths, prompt):
        """Batch-process multiple images"""
        total_tokens = 0
        image_contents = []
        
        for path in image_paths:
            img_data, tokens = self.optimize_image(path)
            total_tokens += tokens
            
            image_contents.append({
                "type": "image",
                "source": {
                    "type": "base64",
                    "media_type": "image/jpeg",
                    "data": img_data
                }
            })
        
        print(f"\nTotal estimated tokens: {total_tokens}")
        
        # Add the text prompt
        image_contents.append({
            "type": "text",
            "text": prompt
        })
        
        # Send the request to the Claude 4.5 API
        message = self.client.messages.create(
            model="claude-3-opus-20240229",
            max_tokens=2048,
            messages=[{
                "role": "user",
                "content": image_contents
            }]
        )
        
        return message.content, total_tokens

# Usage example
optimizer = ImageTokenOptimizer(api_key="your-api-key")

image_files = ["image1.jpg", "image2.jpg", "image3.jpg"]
prompt = "Analyze the similarities and differences among these images"

result, total_tokens = optimizer.batch_process_images(image_files, prompt)
print(f"\nAnalysis result: {result}")
print(f"Tokens used: {total_tokens}")

When processing images of different aspect ratios, the following recommended sizes help achieve optimal token consumption: 1092×1092 pixels for square (1:1) images, 819×1456 pixels for portrait (9:16) images, and 1456×819 pixels for landscape (16:9) images. These sizes are designed to maximize image quality within the 1.15-megapixel limit while optimizing processing efficiency.

Best Practices

When doing image processing with the Claude 4.5 API, image quality comes first. Blurry or pixelated images reduce recognition accuracy, so sharp images are recommended. If an image contains text, make sure the text is large enough to be legible.

For cost optimization, check image dimensions before processing and resize as needed. When batch processing, we recommend calculating the total token count in advance and confirming the job fits your budget. For frequently used images, you can upload once via the Files API and reference the file multiple times, cutting the encoding overhead.

For performance tuning, pre-resizing images to 1,568 pixels or less avoids the API-side automatic resize and shortens response times. When processing multiple images, well-structured batching — rather than blind parallelism — lets you work efficiently while respecting API rate limits.

For optimal performance, Claude 4.5 recommends resizing images to 1.15 megapixels or less (with both edges within 1,568 pixels).

By aspect ratio, the recommended sizes are 1092×1092 pixels for square 1:1 images, 951×1268 pixels for portrait 3:4 images, and 819×1456 pixels for wide 16:9 images.

Example 1: Sending an image with Base64 encoding

The most basic way to send an image is Base64 encoding. Because the image data is embedded directly in the request, no external hosting is needed, making this approach well suited to secure environments.

import anthropic
import base64
from PIL import Image
import io

# Resize the image and calculate tokens
def prepare_image(image_path, max_dimension=1568):
    with Image.open(image_path) as img:
        # Get the image size
        width, height = img.size
        
        # Calculate the token count
        estimated_tokens = (width * height) / 750
        print(f"Original image size: {width}x{height}px")
        print(f"Estimated tokens: {estimated_tokens:.0f}")
        
        # Resize if needed
        if max(width, height) > max_dimension:
            ratio = max_dimension / max(width, height)
            new_width = int(width * ratio)
            new_height = int(height * ratio)
            img = img.resize((new_width, new_height), Image.LANCZOS)
            
            # Recalculate tokens after resizing
            new_tokens = (new_width * new_height) / 750
            print(f"After resize: {new_width}x{new_height}px")
            print(f"New token count: {new_tokens:.0f}")
        
        # Base64 encode
        buffered = io.BytesIO()
        img.save(buffered, format="PNG")
        return base64.b64encode(buffered.getvalue()).decode()

# Send to the Claude 4.5 API
client = anthropic.Anthropic(api_key="your-api-key")

image_base64 = prepare_image("sample.jpg")

response = client.messages.create(
    model="claude-3-5-sonnet-20241022",  # Model equivalent to Claude 4.5
    max_tokens=1000,
    messages=[
        {
            "role": "user",
            "content": [
                {
                    "type": "image",
                    "source": {
                        "type": "base64",
                        "media_type": "image/png",
                        "data": image_base64
                    }
                },
                {
                    "type": "text",
                    "text": "Please analyze the elements in this image in detail"
                }
            ]
        }
    ]
)

Example 2: Sending an image by URL reference

Using publicly hosted image URLs is an efficient technique that saves bandwidth when processing large numbers of images. Since the image data is not included in the request body, it is easier to stay within the request size limit.

const Anthropic = require('@anthropic-ai/sdk');

// Function to pre-calculate the token count
async function calculateImageTokens(imageUrl) {
    // Fetch the image metadata (example implementation)
    const response = await fetch(imageUrl, { method: 'HEAD' });
    const contentLength = response.headers.get('content-length');
    
    // Rough size estimate (in practice, analyze the image to get exact pixel counts)
    const estimatedPixels = contentLength * 0.3; // approximate value
    const estimatedTokens = estimatedPixels / 750;
    
    console.log(`Image URL: ${imageUrl}`);
    console.log(`Estimated tokens: ${Math.round(estimatedTokens)}`);
    
    return estimatedTokens;
}

// Send multiple images to the Claude 4.5 API
async function analyzeMultipleImages() {
    const anthropic = new Anthropic({
        apiKey: process.env.ANTHROPIC_API_KEY,
    });
    
    const imageUrls = [
        'https://example.com/image1.jpg',
        'https://example.com/image2.jpg'
    ];
    
    // Calculate the total token count
    let totalTokens = 0;
    for (const url of imageUrls) {
        totalTokens += await calculateImageTokens(url);
    }
    console.log(`Total estimated tokens: ${Math.round(totalTokens)}`);
    
    // Build the API request
    const message = await anthropic.messages.create({
        model: 'claude-3-5-sonnet-20241022',
        max_tokens: 1500,
        messages: [{
            role: 'user',
            content: [
                {
                    type: 'text',
                    text: 'Compare and analyze these images'
                },
                ...imageUrls.map(url => ({
                    type: 'image',
                    source: {
                        type: 'url',
                        url: url
                    }
                }))
            ]
        }]
    });
    
    return message;
}

Example 3: Optimizing with batch processing

When processing large numbers of images, combining batch processing with appropriate size adjustments optimizes both cost and performance.

import asyncio
import aiohttp
from typing import List, Dict
import numpy as np
from PIL import Image
import anthropic

class ImageTokenOptimizer:
    def __init__(self, api_key: str):
        self.client = anthropic.Anthropic(api_key=api_key)
        self.token_budget = 10000  # Set the token budget
        
    def calculate_optimal_size(self, original_width: int, original_height: int, 
                              target_tokens: int = 1500) -> tuple:
        """Calculate the optimal image size based on a target token count"""
        current_tokens = (original_width * original_height) / 750
        
        if current_tokens <= target_tokens:
            return original_width, original_height
        
        # Calculate the scaling factor
        scale_factor = np.sqrt(target_tokens * 750 / (original_width * original_height))
        
        new_width = int(original_width * scale_factor)
        new_height = int(original_height * scale_factor)
        
        # Check the maximum size limit
        max_dim = 1568
        if max(new_width, new_height) > max_dim:
            ratio = max_dim / max(new_width, new_height)
            new_width = int(new_width * ratio)
            new_height = int(new_height * ratio)
        
        return new_width, new_height
    
    async def process_image_batch(self, image_paths: List[str]) -> Dict:
        """Process a batch of images and optimize token usage"""
        processed_images = []
        total_tokens = 0
        
        for path in image_paths:
            with Image.open(path) as img:
                width, height = img.size
                
                # Calculate the optimal size
                optimal_width, optimal_height = self.calculate_optimal_size(
                    width, height, 
                    target_tokens=self.token_budget // len(image_paths)
                )
                
                # Resize
                if (optimal_width, optimal_height) != (width, height):
                    img = img.resize((optimal_width, optimal_height), Image.LANCZOS)
                
                # Record the token count
                image_tokens = (optimal_width * optimal_height) / 750
                total_tokens += image_tokens
                
                processed_images.append({
                    'path': path,
                    'original_size': (width, height),
                    'optimized_size': (optimal_width, optimal_height),
                    'tokens': image_tokens,
                    'image': img
                })
        
        print(f"Batch processing complete:")
        print(f"  Images processed: {len(processed_images)}")
        print(f"  Total tokens: {total_tokens:.0f}")
        print(f"  Average tokens/image: {total_tokens/len(processed_images):.0f}")
        
        return {
            'images': processed_images,
            'total_tokens': total_tokens,
            'within_budget': total_tokens <= self.token_budget
        }
    
    async def send_optimized_batch(self, batch_data: Dict) -> str:
        """Send the optimized image batch to Claude 4.5"""
        # Implement the actual API send logic here
        # Base64-encode and send each image in batch_data['images']
        pass

# Usage example
async def main():
    optimizer = ImageTokenOptimizer(api_key="your-api-key")
    
    image_paths = [
        "image1.jpg", 
        "image2.jpg", 
        "image3.jpg"
    ]
    
    # Batch processing and optimization
    batch_result = await optimizer.process_image_batch(image_paths)
    
    # Send the optimized images to the API
    if batch_result['within_budget']:
        response = await optimizer.send_optimized_batch(batch_result)
        print("Batch sent successfully")
    else:
        print("Token budget exceeded. Split the images and process them separately")

# Run
asyncio.run(main())
Qualiteg Technology Consulting

Token and cost optimization, starting from the design of your LLM strategy.

Model selection, cost optimization, production implementation — adopting LLMs and generative AI involves many decision points beyond comparisons and calculations.

We develop and operate our own LLM products. From model selection to cost optimization and production implementation, our support is grounded in hands-on implementation experience — not armchair theory.

Explore our LLM adoption consulting →

See you next time!

- Calculating image token consumption for the OpenAI GPT series

OpenAI API: How Vision-Enabled LLMs Calculate Image Tokens (2025 Edition)
Hello! OpenAI's vision-capable models (that is, LLMs that accept image input) use two different methods for converting images into tokens. The latest GPT-5 and…

- Calculating image token consumption for the Google Gemini series

A Guide to Multimodal Token Calculation (Image, Video, and Audio Tokens) in Gemini 2.5 Pro/Flash
Hello! When you use Gemini 2.5 Pro and Gemini 2.5 Flash, accurately tracking token counts is critical for cost calculation and context window management. In thi…

Read more