Nano Banana 2 API Developer Guide: Building with Gemini 2.5 Flash Image Complete developer guide to integrating Nano Banana 2's powerful image generation API into your applications. Learn authentication, API endpoints, best practices, and production deployment strategies.
Nano Banana 2, powered by Google's Gemini 2.5 Flash Image model, offers developers a powerful API for integrating advanced AI image generation and editing capabilities into applications. This comprehensive guide covers everything you need to know to build with Nano Banana 2's API.
Lightning-Fast Performance
Average response time: 2-3 seconds per image
Up to 8× faster than competing models
Optimized for real-time applications
$0.039 per generated image through API
Volume discounts available for enterprise
No minimum monthly commitment
Production-Ready Reliability
99.9% uptime SLA
Global CDN for image delivery
Automatic failover and retry mechanisms
Character consistency across generations
Multi-image blending and composition
Natural language editing
Style reference support
Before you begin, ensure you have:
Node.js 18+ or Python 3.9+ installed
Basic understanding of REST APIs
API key from nano-banana2.com
Development environment set up
Nano Banana 2 uses API key authentication. Include your key in the Authorization header:
Authorization: Bearer YOUR_API_KEY
Sign up at nano-banana2.com
Navigate to Dashboard → Settings → API
Generate new API key
Store securely in environment variables
// ❌ Never hardcode API keys
const apiKey = 'nb2_abc123...' ;
// ✅ Use environment variables
const apiKey = process.env. NANO_BANANA_API_KEY ; Generate images from text descriptions.
Endpoint: POST /api/v1/generate
const response = await fetch ( 'https://api.nano-banana2.com/api/v1/generate' , {
method: 'POST' ,
headers: {
'Authorization' : `Bearer ${ apiKey }` ,
'Content-Type' : 'application/json'
},
body: JSON . stringify ({
{
"id" : "gen_abc123xyz" ,
"status" : "completed" ,
"created_at" : "2025-11-12T10:30:00Z" ,
"outputs" : [
{
"url" : "https://cdn.nano-banana2.com/outputs/abc123.png" ,
"width" : 1920 ,
"height" : 1080
}
],
"credits_used" : 4
Edit existing images with natural language.
Endpoint: POST /api/v1/edit
const formData = new FormData ();
formData. append ( 'image' , imageFile);
formData. append ( 'prompt' , 'Change the background to a sunset beach scene' );
formData. append ( 'aspect_ratio' , 'auto' );
formData. append ( 'output_format'
Blend multiple images into cohesive composition.
Endpoint: POST /api/v1/blend
const formData = new FormData ();
formData. append ( 'image1' , firstImage);
formData. append ( 'image2' , secondImage);
formData. append ( 'prompt' , 'Blend these images with natural lighting and unified style' );
formData. append ( 'blend_strength' ,
Check status of async generation jobs.
Endpoint: GET /api/v1/status/{job_id}
const response = await fetch (
`https://api.nano-banana2.com/api/v1/status/${ jobId }` ,
{
headers: {
'Authorization' : `Bearer ${ apiKey }`
}
}
);
const status = await response. json
import { NanoBanana2Client } from '@nano-banana2/sdk' ;
const client = new NanoBanana2Client ({
apiKey: process.env. NANO_BANANA_API_KEY
});
// Generate image
async function generateImage () {
try {
from nano_banana2 import NanoBanana2Client
import os
import asyncio
client = NanoBanana2Client( api_key = os.getenv( 'NANO_BANANA_API_KEY' ))
# Generate image
async def
Maintain consistent characters across multiple generations:
// Step 1: Generate base character
const baseCharacter = await client. generate ({
prompt: "A friendly robot character with yellow banana-shaped head, blue uniform" ,
aspectRatio: "1:1"
});
const characterRefUrl = baseCharacter.outputs[ 0 ].url;
// Step 2: Generate scenes with consistent character
Apply consistent artistic style across generations:
// Generate style reference
const styleRef = await client. generate ({
prompt: "Abstract art style, vibrant colors, geometric shapes" ,
aspectRatio: "1:1"
});
// Apply style to new generations
const styledImage = await client. generate ({
For long-running operations, use webhooks:
// Register webhook endpoint
app. post ( '/webhooks/nano-banana' , ( req , res ) => {
const { job_id , status , outputs } = req.body;
if (status ===
Implement client-side rate limiting:
class RateLimitedClient {
private queue : Array <() => Promise < any >> = [];
private processing = false ;
private requestsPerSecond = 10 ;
Robust error handling for production:
async function generateWithRetry (
params : GenerateParams ,
maxRetries = 3
) : Promise < GenerateResponse > {
for ( let attempt = 0 ; attempt <
Cache generated images to reduce costs:
import Redis from 'ioredis' ;
import crypto from 'crypto' ;
const redis = new Redis (process.env. REDIS_URL );
async function getCachedOrGenerate ( params :
Monitor and manage credit usage:
class CreditManager {
async checkBalance () : Promise < number > {
const response = await fetch (
'https://api.nano-banana2.com/api/v1/credits/balance' ,
{
headers: { 'Authorization' :
Comprehensive monitoring setup:
import { Logger } from 'winston' ;
class MonitoredClient {
private logger : Logger ;
private metrics : MetricsCollector ;
async generate ( params : GenerateParams
// Express.js API endpoint
app. post ( '/api/images/generate' , authenticate, async ( req , res ) => {
try {
const { prompt , aspectRatio } = req.body;
async function generateProductVariants (
productImage : string ,
backgrounds : string []
) {
const variants = await Promise . all (
backgrounds. map (
async function moderateAndGenerate ( prompt : string ) {
// Pre-generation moderation
const moderationResult = await moderatePrompt (prompt);
if ( ! moderationResult.safe) {
throw new Error (
async function generateMultiplePrompts ( prompts : string []) {
// Process in batches to avoid rate limits
const batchSize = 10 ;
const results = [];
for ( let i = 0
import sharp from 'sharp' ;
async function optimizeGeneratedImage ( imageUrl : string ) {
// Download image
const response = await fetch (imageUrl);
const buffer = await
import { describe, it, expect, vi } from 'vitest' ;
describe ( 'NanoBanana2Client' , () => {
it ( 'should generate image successfully' , async () => {
const mockClient = {
generate: vi.
describe ( 'Nano Banana 2 Integration' , () => {
it ( 'should generate real image' , async () => {
const client = new NanoBanana2Client ({
apiKey: process.env. TEST_API_KEY
});
# .env.production
NANO_BANANA_API_KEY = your_production_key
NANO_BANANA_API_URL = https://api.nano-banana2.com
REDIS_URL = redis://your-redis-url
CDN_URL = https://your-cdn.com
MAX_CONCURRENT_REQUESTS = 10
REQUEST_TIMEOUT = 30000 FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
ENV NODE_ENV=production
ENV PORT=3000
EXPOSE 3000
CMD [ "node" , "dist/server.js" ] apiVersion : apps/v1
kind : Deployment
metadata :
name : nano-banana-app
spec :
replicas : 3
template :
spec :
containers :
- name : app
image : your-app:latest
env :
-
// Solution: Increase timeout and implement retry
const result = await client. generate ({
prompt: "complex scene" ,
timeout: 60000 , // 60 seconds
retries: 3
}); Issue 2: Rate Limit Exceeded
// Solution: Implement exponential backoff
if (error.status === 429 ) {
const retryAfter = error.headers[ 'retry-after' ];
await sleep (retryAfter * 1000 );
return await client. generate (params);
Issue 3: Inconsistent Character Results
// Solution: Use seed for reproducibility
const result = await client. generate ({
prompt: "character description" ,
seed: 42 ,
referenceImages: [characterUrl]
}); Cache similar prompts to avoid regeneration:
const promptSimilarity = calculateSimilarity (newPrompt, cachedPrompt);
if (promptSimilarity > 0.95 ) {
return getCachedResult (cachedPrompt);
} Group multiple requests to save on API overhead:
const results = await client. generateBatch ({
prompts: arrayOfPrompts,
sharedParams: {
aspectRatio: "16:9" ,
outputFormat: "jpeg"
}
}); Leverage generated images across multiple contexts:
// Generate base scenes once
const baseScenes = await generateBaseScenes ();
// Reuse in different contexts
const variations = await Promise . all (
baseScenes. map ( scene =>
client. edit
E-commerce product visualizer
Social media content generator
Real-time image editing app
AI avatar creator
Nano Banana 2's API provides developers with powerful, flexible, and performant tools for integrating AI image generation into applications. With competitive pricing, fast response times, and advanced features like character consistency and multi-image blending, it's an excellent choice for production applications.
Use SDK wrappers for simplified integration
Implement proper error handling and retry logic
Cache aggressively to optimize costs
Monitor usage and performance metrics
Follow security best practices for API key management
Test thoroughly before production deployment
Whether you're building a SaaS product, e-commerce platform, content creation tool, or innovative AI application, Nano Banana 2's API offers the performance and capabilities you need to deliver exceptional user experiences.
prompt: "A yellow banana robot in a futuristic city with neon lights, cyberpunk style, highly detailed" ,
aspect_ratio: "16:9" ,
num_outputs: 1 ,
output_format: "png" ,
guidance_scale: 7.5 ,
seed: 42 // Optional: for reproducible results
})
});
const data = await response. json ();
,
"processing_time_ms" : 2150
}
,
'jpeg'
);
const response = await fetch ( 'https://api.nano-banana2.com/api/v1/edit' , {
method: 'POST' ,
headers: {
'Authorization' : `Bearer ${ apiKey }`
},
body: formData
});
'0.7'
);
// 0.0 to 1.0
const response = await fetch ( 'https://api.nano-banana2.com/api/v1/blend' , {
method: 'POST' ,
headers: {
'Authorization' : `Bearer ${ apiKey }`
},
body: formData
});
();
// Returns: { status: 'pending' | 'processing' | 'completed' | 'failed' }
const
result
=
await
client.
generate
({
prompt: "A serene mountain landscape at sunset" ,
aspectRatio: "16:9" ,
numOutputs: 1
});
console. log ( 'Generated image:' , result.outputs[ 0 ].url);
console. log ( 'Credits used:' , result.creditsUsed);
} catch (error) {
console. error ( 'Generation failed:' , error);
}
}
// Edit image with retry logic
async function editImageWithRetry ( imageUrl : string , prompt : string ) {
const maxRetries = 3 ;
let attempt = 0 ;
while (attempt < maxRetries) {
try {
const result = await client. edit ({
imageUrl,
prompt,
timeout: 30000 // 30 seconds
});
return result;
} catch (error) {
attempt ++ ;
if (attempt >= maxRetries) throw error;
// Exponential backoff
await new Promise ( resolve =>
setTimeout (resolve, Math. pow ( 2 , attempt) * 1000 )
);
}
}
}
// Batch generation
async function batchGenerate ( prompts : string []) {
const results = await Promise . all (
prompts. map ( prompt =>
client. generate ({ prompt, aspectRatio: "1:1" })
)
);
return results;
}
generate_image
():
try :
result = await client.generate(
prompt = "A yellow banana robot in a futuristic city" ,
aspect_ratio = "16:9" ,
num_outputs = 1
)
print ( f "Generated image: { result[ 'outputs' ][ 0 ][ 'url' ] } " )
print ( f "Credits used: { result[ 'credits_used' ] } " )
except Exception as e:
print ( f "Generation failed: { e } " )
# Edit image
async def edit_image (image_path: str , prompt: str ):
with open (image_path, 'rb' ) as f:
result = await client.edit(
image = f,
prompt = prompt,
output_format = "png"
)
return result
# Character consistency
async def generate_character_series (character_ref_url: str ):
scenes = [
"in a coffee shop" ,
"at the beach" ,
"in outer space" ,
"in a forest"
]
results = []
for scene in scenes:
result = await client.generate(
prompt = f "Character from reference image { scene } " ,
reference_images = [character_ref_url],
aspect_ratio = "1:1"
)
results.append(result)
return results
# Run async functions
asyncio.run(generate_image())
const scenes = [
{ prompt: "coffee shop interior, warm lighting" , name: "scene1" },
{ prompt: "city street at night, neon lights" , name: "scene2" },
{ prompt: "beach during sunset, golden hour" , name: "scene3" }
];
const results = await Promise . all (
scenes. map ( scene =>
client. generate ({
prompt: `Character from ${ characterRefUrl } ${ scene . prompt }` ,
referenceImages: [characterRefUrl],
aspectRatio: "16:9"
})
)
);
prompt: "A mountain landscape" ,
styleReferenceUrl: styleRef.outputs[ 0 ].url,
styleStrength: 0.8 , // 0.0 to 1.0
aspectRatio: "16:9"
});
'completed'
) {
console. log ( 'Generation completed:' , outputs);
// Process completed images
saveToDatabase (job_id, outputs);
} else if (status === 'failed' ) {
console. error ( 'Generation failed:' , job_id);
handleFailure (job_id);
}
res. sendStatus ( 200 );
});
// Submit async job
const job = await client. generateAsync ({
prompt: "Complex scene with multiple elements" ,
webhookUrl: "https://your-app.com/webhooks/nano-banana" ,
numOutputs: 4
});
console. log ( 'Job submitted:' , job.id);
async generate ( params : GenerateParams ) {
return new Promise (( resolve , reject ) => {
this .queue. push ( async () => {
try {
const result = await client. generate (params);
resolve (result);
} catch (error) {
reject (error);
}
});
this . processQueue ();
});
}
private async processQueue () {
if ( this .processing || this .queue. length === 0 ) return ;
this .processing = true ;
const task = this .queue. shift () ! ;
await task ();
await new Promise ( resolve =>
setTimeout (resolve, 1000 / this .requestsPerSecond)
);
this .processing = false ;
this . processQueue ();
}
}
maxRetries; attempt
++
) {
try {
return await client. generate (params);
} catch (error) {
const isRetriable =
error.status === 429 || // Rate limit
error.status === 503 || // Service unavailable
error.status >= 500 ; // Server errors
if ( ! isRetriable || attempt === maxRetries - 1 ) {
throw error;
}
// Exponential backoff with jitter
const delay = Math. min (
1000 * Math. pow ( 2 , attempt) + Math. random () * 1000 ,
30000 // Max 30 seconds
);
await new Promise ( resolve => setTimeout (resolve, delay));
}
}
throw new Error ( 'Max retries exceeded' );
}
GenerateParams
) {
// Create cache key from params
const cacheKey = crypto
. createHash ( 'md5' )
. update ( JSON . stringify (params))
. digest ( 'hex' );
// Check cache
const cached = await redis. get ( `img:${ cacheKey }` );
if (cached) {
return JSON . parse (cached);
}
// Generate if not cached
const result = await client. generate (params);
// Cache for 7 days
await redis. setex (
`img:${ cacheKey }` ,
60 * 60 * 24 * 7 ,
JSON . stringify (result)
);
return result;
}
`Bearer ${
apiKey
}`
}
}
);
const { balance } = await response. json ();
return balance;
}
async estimateCost ( params : GenerateParams ) : Promise < number > {
const creditsPerImage = 4 ;
return creditsPerImage * (params.numOutputs || 1 );
}
async generateIfSufficient ( params : GenerateParams ) {
const balance = await this . checkBalance ();
const cost = await this . estimateCost (params);
if (balance < cost) {
throw new Error (
`Insufficient credits. Need ${ cost }, have ${ balance }`
);
}
return await client. generate (params);
}
}
) {
const startTime = Date. now ();
const requestId = generateRequestId ();
this .logger. info ( 'Generation started' , {
requestId,
prompt: params.prompt,
aspectRatio: params.aspectRatio
});
try {
const result = await client. generate (params);
const duration = Date. now () - startTime;
this .metrics. recordSuccess ({
duration,
creditsUsed: result.creditsUsed,
requestId
});
this .logger. info ( 'Generation completed' , {
requestId,
duration,
creditsUsed: result.creditsUsed
});
return result;
} catch (error) {
const duration = Date. now () - startTime;
this .metrics. recordFailure ({
duration,
error: error.message,
requestId
});
this .logger. error ( 'Generation failed' , {
requestId,
duration,
error: error.message,
stack: error.stack
});
throw error;
}
}
}
const
userId
=
req.user.id;
// Check user credits
const userCredits = await getUserCredits (userId);
if (userCredits < 4 ) {
return res. status ( 402 ). json ({
error: 'Insufficient credits'
});
}
// Generate image
const result = await client. generate ({
prompt,
aspectRatio,
numOutputs: 1
});
// Deduct credits
await deductUserCredits (userId, result.creditsUsed);
// Save to user's gallery
await saveToGallery (userId, result.outputs[ 0 ].url);
res. json ({
imageUrl: result.outputs[ 0 ].url,
creditsUsed: result.creditsUsed,
remainingCredits: userCredits - result.creditsUsed
});
} catch (error) {
res. status ( 500 ). json ({ error: error.message });
}
});
async
(
bgPrompt
)
=>
{
return await client. edit ({
imageUrl: productImage,
prompt: `Place product in ${ bgPrompt }, professional product photography` ,
aspectRatio: "1:1"
});
})
);
return variants. map ( v => v.outputs[ 0 ].url);
}
// Usage
const productUrl = "https://shop.com/products/shoe-1.jpg" ;
const variants = await generateProductVariants (productUrl, [
"modern white studio background" ,
"outdoor nature scene" ,
"urban street setting" ,
"luxury interior design"
]);
'Prompt violates content policy'
);
}
// Generate with safety settings
const result = await client. generate ({
prompt,
safetySettings: 'strict' ,
aspectRatio: "16:9"
});
// Post-generation moderation
const imageModeration = await moderateImage (
result.outputs[ 0 ].url
);
if ( ! imageModeration.safe) {
await deleteImage (result.outputs[ 0 ].url);
throw new Error ( 'Generated image violates content policy' );
}
return result;
}
; i
<
prompts.
length
; i
+=
batchSize) {
const batch = prompts. slice (i, i + batchSize);
const batchResults = await Promise . all (
batch. map ( prompt =>
client. generate ({ prompt, aspectRatio: "1:1" })
)
);
results. push ( ... batchResults);
}
return results;
}
response.
arrayBuffer
();
// Optimize with sharp
const optimized = await sharp (Buffer. from (buffer))
. resize ( 1920 , 1080 , { fit: 'inside' })
. jpeg ({ quality: 85 , progressive: true })
. toBuffer ();
// Upload to your CDN
return await uploadToCDN (optimized);
}
fn
().
mockResolvedValue
({
outputs: [{ url: 'https://example.com/image.png' }],
creditsUsed: 4
})
};
const result = await mockClient. generate ({
prompt: "Test prompt" ,
aspectRatio: "16:9"
});
expect (result.outputs[ 0 ].url). toBeDefined ();
expect (result.creditsUsed). toBe ( 4 );
});
it ( 'should handle rate limiting' , async () => {
const mockClient = {
generate: vi. fn ()
. mockRejectedValueOnce ({ status: 429 })
. mockResolvedValueOnce ({ outputs: [{ url: 'test.png' }] })
};
const result = await generateWithRetry ({
client: mockClient,
params: { prompt: "test" }
});
expect (mockClient.generate). toHaveBeenCalledTimes ( 2 );
expect (result.outputs[ 0 ].url). toBe ( 'test.png' );
});
});
const result = await client. generate ({
prompt: "A simple test image" ,
aspectRatio: "1:1"
});
expect (result.outputs[ 0 ].url). toMatch ( / ^ https: \/\/ / );
expect (result.creditsUsed). toBeGreaterThan ( 0 );
// Verify image is accessible
const imageResponse = await fetch (result.outputs[ 0 ].url);
expect (imageResponse.ok). toBe ( true );
}, 30000 ); // 30 second timeout
});
name
:
NANO_BANANA_API_KEY
valueFrom :
secretKeyRef :
name : nano-banana-secrets
key : api-key
resources :
requests :
memory : "256Mi"
cpu : "250m"
limits :
memory : "512Mi"
cpu : "500m"
}
({
imageUrl: scene.url,
prompt: "add subtle variations"
})
)
);
Nano Banana 2 API Developer Guide: Building with Gemini 2.5 Flash Image | Nano Banana 2 - AI 驱动平台