Skip to content

Nano Banana Series

Model Introduction

Google's Nano Banana series is the internal codename for its image generation technology, currently offering three generations of models:

  • gemini-2.5-flash-image (Nano Banana)
  • gemini-3-pro-image-preview (Nano Banana Pro)
  • gemini-3.1-flash-image-preview (Nano Banana 2)

🆕 Nano Banana 2 (Newest)

  • Model Name: gemini-3.1-flash-image-preview
  • Version: Preview
  • Technical Base: Based on Gemini 3 architecture, featuring stronger reasoning capabilities
  • Features: Latest technology, superior image quality and detail representation
  • Resolution Support: Supports 512, 1K, 2K, 4K
  • Scenarios: Users pursuing best effects, requiring high resolution, and willing to try new features

Endpoint Migration Notice

Note

Preview image generation endpoints will be removed on July 17. The platform will redirect requests that still use preview endpoints to the model endpoints to keep your service uninterrupted. We still recommend updating to the model endpoints before July 17, 2026 to avoid future service impact.

Preview EndpointModel Endpoint
gemini-3.1-flash-image-previewgemini-3.1-flash-image
gemini-3-pro-image-previewgemini-3-pro-image

Calling Method: Google Native Format

💡 Model Switching Instructions

In Google Native Format, the model name is specified in the URL path:

Nano Banana 2 (New): /v1beta/models/gemini-3.1-flash-image-preview:generateContent

Nano Banana Pro: /v1beta/models/gemini-3-pro-image-preview:generateContent

Nano Banana: /v1beta/models/gemini-2.5-flash-image:generateContent

Supported Aspect Ratios

TypeAspect Ratio Options
Landscape21:9 (Ultra Wide), 16:9 (Wide), 4:3, 3:2
Square1:1
Portrait9:16 (Vertical), 3:4, 2:3
Other5:4, 4:5

Supported Resolutions

ℹ️ Resolution Support Guide

  • Nano Banana 2 (gemini-3.1-flash-image-preview): Supports 512, 1K, 2K, 4K.
  • Nano Banana Pro (gemini-3-pro-image-preview): Supports 1K, 2K, 4K.
  • Nano Banana (gemini-2.5-flash-image): Fixed at 1K (1024px), does not support imageSize parameter.

Nano Banana 2 Resolution Options

Aspect Ratio512 Resolution1K Resolution2K Resolution4K Resolution
1:1512×5121024×10242048×20484096×4096
16:9688×3841376×7682752×15365504×3072
9:16384×688768×13761536×27523072×5504
4:3600×4481200×8962400×17924800×3584
3:4448×600896×12001792×24003584×4800
21:9792×3361584×6723168×13446336×2688
3:2624×4161248×8322496×16644992×3328
2:3416×624832×12481664×24963328×4992
5:4576×4481152×8962304×17924608×3584
4:5448×576896×11521792×23043584×4608

💡 Resolution Selection Advice

  • 1K: Suitable for web display, social media, quick preview.
  • 2K: Suitable for high-quality printing, professional display.
  • 4K: Suitable for large format printing, professional design, extreme detail.

Code Examples

Full Curl Example (Nano Banana 2 - 4K)

bash
#!/bin/bash

# 1. Set API Key
export API_KEY="sk-xxxx"

# 2. Send request
curl -s -X POST "https://api.exchangetoken.ai/v1beta/models/gemini-3.1-flash-image-preview:generateContent" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "contents": [
        {
            "role": "user",
            "parts": [
                {
                    "text": "A futuristic city skyline at sunset, high detailed, 4k"
                }
            ]
        }
    ],
    "generationConfig": {
        "responseModalities": ["IMAGE"]
    }
}' \
  | jq -r '.candidates[0].content.parts[0].inlineData.data' \
  | base64 --decode > output_4k.png

echo "✅ Image saved: output_4k.png"

Quick Start Curl Example (Nano Banana 1K)

For reference, here is the calling method for Nano Banana.

bash
# Use Nano Banana (1K)
curl -s -X POST "https://api.exchangetoken.ai/v1beta/models/gemini-2.5-flash-image:generateContent" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "contents": [{
      "role": "user",
      "parts": [{"text": "a beautiful sunset over mountains"}]
    }],
    "generationConfig": {
      "responseModalities": ["IMAGE"],
      "imageConfig": {"aspectRatio": "16:9"}
    }
  }' | jq -r '.candidates[0].content.parts[0].inlineData.data' | base64 --decode > output.png

More Code Scenarios

The following are advanced examples covering single image editing, multi-image synthesis, smart editing, and Base64 data processing.

1. Single Image Edit Example (Python)

Perform an edit operation on a single image.

python
import requests
import json
import base64
from Base64ImageHandler import Base64ImageHandler
def get_image_base64(url):
    """
    [Critical Step]
    The native Gemini interface does not support passing URLs directly;
    the image must be downloaded and converted to Base64 encoding.
    """
    print(f"[LOG] Downloading image to adapt to native interface: {url}")
    try:
        headers = {'User-Agent': 'Mozilla/5.0'}
        response = requests.get(url, headers=headers, timeout=30)
        if response.status_code == 200:
            # Get content-type, default is image/png
            mime_type = response.headers.get('Content-Type', 'image/png')
            # Convert to base64 string
            b64_data = base64.b64encode(response.content).decode('utf-8')
            print(f"[LOG] Image download successful, format: {mime_type}, data length: {len(b64_data)}")
            return mime_type, b64_data
        else:
            print(f"[ERROR] Image download failed, status code: {response.status_code}")
            return None, None
    except Exception as e:
        print(f"[ERROR] Image download exception: {e}")
        return None, None
def creative_merge(image_urls, merge_instruction):
    print("=" * 50)
    print("Starting image merge request processing (Gemini native protocol)")
    print("=" * 50)
    # 1. Prepare request content parts
    parts = []
    # Add text instruction
    parts.append({
        "text": merge_instruction + "\n\nPlease generate the image directly and return it in Base64 format."
    })
    # 2. Process images: Download and convert to inline_data format
    if image_urls:
        print(f"[LOG] Number of image URLs: {len(image_urls)}")
        for i, url in enumerate(image_urls, 1):
            mime_type, image_b64 = get_image_base64(url)
            if image_b64:
                parts.append({
                    "inline_data": {
                        "mime_type": mime_type,
                        "data": image_b64
                    }
                })
    # 3. Construct Gemini native Payload (contents structure)
    # Structure: contents -> parts -> [text, inline_data]
    data = {
        "contents": [
            {
                "role": "user",
                "parts": parts
            }
        ],
        "generationConfig": {
            "temperature": 0.5,
            "maxOutputTokens": 4096 
        }
    }
    # 4. Set native interface URL
    # Note: Using the :generateContent interface here
    api_endpoint = "https://api.exchangetoken.ai/v1beta/models/gemini-3.1-flash-image-preview:generateContent"
    headers = {
        "Authorization": f"Bearer sk-xxxxx",
        "Content-Type": "application/json"
    }
    print(f"\n[LOG] Request URL: {api_endpoint}")
    print(f"[LOG] Sending request...")
    try:
        response = requests.post(api_endpoint, headers=headers, json=data)
        print(f"[LOG] HTTP Status Code: {response.status_code}")
        # print(f"[LOG] Response Headers: {dict(response.headers)}")
        if response.status_code != 200:
            print(f"[ERROR] API request failed")
            print(f"[ERROR] Response content: {response.text}")
            return None, None
        response_json = response.json()
        print(f"\n[LOG] API call successful, starting to parse native response...")
        # 5. Parse Gemini native response structure
        # Successful response is usually in candidates[0].content.parts[0].text
        response_str = json.dumps(response_json, ensure_ascii=False)
        # Attempt to print response text
        try:
            candidates = response_json.get('candidates', [])
            if candidates:
                content = candidates[0].get('content', {})
                res_parts = content.get('parts', [])
                if res_parts and 'text' in res_parts[0]:
                    print(f"[LOG] Model response text snippet: {res_parts[0]['text'][:200]}...")
        except Exception as e:
            print(f"[WARNING] Simple text parsing failed: {e}")
        # Use utility class to search for Base64 in the entire response string
        result = Base64ImageHandler.extract_from_response(response_str)
        if result and result[0]:
            print(f"[LOG] Successfully extracted image data, length: {len(result[0])}")
        else:
            print(f"[WARNING] Failed to extract base64 data, please check model response content.")
        return result
    except requests.exceptions.RequestException as e:
        print(f"[ERROR] Request exception: {e}")
        return None, None
    except Exception as e:
        print(f"[ERROR] Unknown error: {e}")
        import traceback
        traceback.print_exc()
        return None, None
if __name__ == "__main__":
    images = [
        "https://your_image.png",
    ]
    # Prompt: Image-to-Text-to-Image technique for multimodal models
    prompt = (
        "You are an image editing assistant. Please modify the person in the image to wear a stylish hat.\n"
        "[Key]: Please output the modified image data directly, do not just output a description.\n"
        "If you can generate the image directly, please return the image.\n"
        "If you can only return text, please encode the resulting image as a Base64 string and return it in JSON format,"
        "for example: {\"image_base64\": \"...\"}."
    )
    result = creative_merge(images, prompt)
    print("\n" + "=" * 50)
    print("Save Image Result")
    print("=" * 50)
    if result and result[0]:
        base64_data, image_format = result
        print(f"[LOG] Preparing to save image, format: {image_format}")
        saved_filename = Base64ImageHandler.save_to_file(base64_data)
        print(f"[SUCCESS] Image saved as: {saved_filename}")
    else:
        print("[ERROR] Process ended, image not saved.")

2. Multi-Image Synthesis - Creative Merge (Python)

Take multiple images as input and perform creative fusion through Prompt instructions.

python
import requests
import json
import base64
from Base64ImageHandler import Base64ImageHandler
def get_image_base64(url):
    """
    [Critical Step]
    The native Gemini interface does not support passing URLs directly;
    the image must be downloaded and converted to Base64 encoding.
    """
    print(f"[LOG] Downloading image to adapt to native interface: {url}")
    try:
        headers = {'User-Agent': 'Mozilla/5.0'}
        response = requests.get(url, headers=headers, timeout=30)
        if response.status_code == 200:
            # Get content-type, default is image/png
            mime_type = response.headers.get('Content-Type', 'image/png')
            # Convert to base64 string
            b64_data = base64.b64encode(response.content).decode('utf-8')
            print(f"[LOG] Image download successful, format: {mime_type}, data length: {len(b64_data)}")
            return mime_type, b64_data
        else:
            print(f"[ERROR] Image download failed, status code: {response.status_code}")
            return None, None
    except Exception as e:
        print(f"[ERROR] Image download exception: {e}")
        return None, None
def creative_merge(image_urls, merge_instruction):
    print("=" * 50)
    print("Starting image merge request processing (Gemini native protocol)")
    print("=" * 50)
    # 1. Prepare request content parts
    parts = []
    # Add text instruction
    parts.append({
        "text": merge_instruction + "\n\nPlease generate the image directly and return it in Base64 format."
    })
    # 2. Process images: Download and convert to inline_data format
    if image_urls:
        print(f"[LOG] Number of image URLs: {len(image_urls)}")
        for i, url in enumerate(image_urls, 1):
            mime_type, image_b64 = get_image_base64(url)
            if image_b64:
                parts.append({
                    "inline_data": {
                        "mime_type": mime_type,
                        "data": image_b64
                    }
                })
    # 3. Construct Gemini native Payload (contents structure)
    # Structure: contents -> parts -> [text, inline_data]
    data = {
        "contents": [
            {
                "role": "user",
                "parts": parts
            }
        ],
        "generationConfig": {
            "temperature": 0.5,
            "maxOutputTokens": 4096 
        }
    }
    # 4. Set native interface URL
    # Note: Using the :generateContent interface here
    api_endpoint = "https://api.exchangetoken.ai/v1beta/models/gemini-3.1-flash-image-preview:generateContent"
    headers = {
        "Authorization": f"Bearer sk-xxxxx",
        "Content-Type": "application/json"
    }
    print(f"\n[LOG] Request URL: {api_endpoint}")
    print(f"[LOG] Sending request...")
    try:
        response = requests.post(api_endpoint, headers=headers, json=data)
        print(f"[LOG] HTTP Status Code: {response.status_code}")
        # print(f"[LOG] Response Headers: {dict(response.headers)}")
        if response.status_code != 200:
            print(f"[ERROR] API request failed")
            print(f"[ERROR] Response content: {response.text}")
            return None, None
        response_json = response.json()
        print(f"\n[LOG] API call successful, starting to parse native response...")
        # 5. Parse Gemini native response structure
        # Successful response is usually in candidates[0].content.parts[0].text
        response_str = json.dumps(response_json, ensure_ascii=False)
        # Attempt to print response text
        try:
            candidates = response_json.get('candidates', [])
            if candidates:
                content = candidates[0].get('content', {})
                res_parts = content.get('parts', [])
                if res_parts and 'text' in res_parts[0]:
                    print(f"[LOG] Model response text snippet: {res_parts[0]['text'][:200]}...")
        except Exception as e:
            print(f"[WARNING] Simple text parsing failed: {e}")
        # Use utility class to search for Base64 in the entire response string
        result = Base64ImageHandler.extract_from_response(response_str)
        if result and result[0]:
            print(f"[LOG] Successfully extracted image data, length: {len(result[0])}")
        else:
            print(f"[WARNING] Failed to extract base64 data, please check model response content.")
        return result
    except requests.exceptions.RequestException as e:
        print(f"[ERROR] Request exception: {e}")
        return None, None
    except Exception as e:
        print(f"[ERROR] Unknown error: {e}")
        import traceback
        traceback.print_exc()
        return None, None
if __name__ == "__main__":
    images = [
        "https://image_one.png",
        "https://image_two.png"
    ]
    # Example Prompt: Image-to-Text-to-Image technique for multimodal models
    prompt = (
        "You are an image editing assistant. Please add a stylish hat to the person in the first image. "
        "Also, place the gold necklace from the second image onto the person in the first image.\n"
        "[Key]: Please output the modified image data directly, do not just output a description.\n"
        "If you can generate the image directly, please return the image.\n"
        "If you can only return text, please encode the resulting image as a Base64 string and return it in JSON format,"
        "for example: {\"image_base64\": \"...\"}."
    )
    result = creative_merge(images, prompt)
    print("\n" + "=" * 50)
    print("Save Image Result")
    print("=" * 50)
    if result and result[0]:
        base64_data, image_format = result
        print(f"[LOG] Preparing to save image, format: {image_format}")
        saved_filename = Base64ImageHandler.save_to_file(base64_data)
        print(f"[SUCCESS] Image saved as: {saved_filename}")
    else:
        print("[ERROR] Process ended, image not saved.")

3. Smart Multi-Image Processing Strategy (Python)

Automatically adjust Prompt strategy based on the number of input images to implement single-image editing or multi-image synthesis.

python
def smart_multi_image_edit(images, instruction):
    """Smart multi-image editing, automatically handles different numbers of images"""
    
    if len(images) == 1:
        # Single image editing
        prompt = f"Edit this image: {instruction}"
    elif len(images) == 2:
        # Two-image synthesis
        prompt = f"Combine these two images creatively: {instruction}"
    else:
        # Multi-image processing
        prompt = f"Process these {len(images)} images together: {instruction}"
    
    # Build content
    content = [{"type": "text", "text": prompt}]
    for img in images:
        content.append({
            "type": "image_url",
            "image_url": {"url": img}
        })
    
    # Send request...
    return send_edit_request(content)

4. Base64 Data Processing Utilities (Python)

Utilities for processing Base64 image data returned by the API, supporting extraction, saving, and PIL format conversion.

python
import base64
import io
import re
from datetime import datetime
from PIL import Image

class Base64ImageHandler:
    """Base64 Image Handler Class"""
    
    @staticmethod
    def extract_from_response(response_content):
        """Extract base64 data from API response"""
        patterns = [
            r'data:image/([^;]+);base64,([A-Za-z0-9+/=]+)',
            r'([A-Za-z0-9+/=]{100,})'
        ]
        
        for pattern in patterns:
            match = re.search(pattern, response_content)
            if match:
                if len(match.groups()) == 2:
                    return match.group(2), match.group(1)  # data, format
                else:
                    return match.group(1), 'png'  # default to png
        
        return None, None
    
    @staticmethod
    def save_to_file(base64_data, filename=None):
        """Save base64 data as file"""
        if not filename:
            filename = f"image_{datetime.now().strftime('%Y%m%d_%H%M%S')}.png"
        
        image_data = base64.b64decode(base64_data)
        with open(filename, 'wb') as f:
            f.write(image_data)
        
        return filename
    
    @staticmethod
    def to_pil_image(base64_data):
        """Convert to PIL Image object"""
        image_data = base64.b64decode(base64_data)
        return Image.open(io.BytesIO(image_data))
    
    @staticmethod
    def from_pil_image(pil_image, format='PNG'):
        """Convert from PIL Image to base64"""
        buffer = io.BytesIO()
        pil_image.save(buffer, format=format)
        img_str = base64.b64encode(buffer.getvalue()).decode()
        return f"data:image/{format.lower()};base64,{img_str}"