Skip to content

Stream Request Mode ​

When mode is "stream", the server uses Server-Sent Events (SSE) to push real-time generation progress and the final result. Best for frontend integrations that need to show progress to users.

Request Example ​

sh
curl -N -X POST https://www.nananobanana.com/api/v1/generate \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer nb_your_api_key_here" \
  -d '{
    "prompt": "Cyberpunk style futuristic city at night",
    "selectedModel": "nano-banana",
    "mode": "stream"
  }'

TIP

When using curl, add -N to disable buffering so you can see streaming output in real-time.

Response Format ​

Returns Content-Type: text/event-stream, pushing multiple events via SSE protocol:

text
data: {"type":"status","message":"Starting generation...","progress":0}

data: {"type":"status","message":"Processing...","progress":50}

data: {"type":"complete","message":"Generation complete","progress":100,"data":{"success":true,"imageUrls":["https://..."]}}

Event Types ​

typeDescriptionKey Fields
statusProgress updatemessage, progress (0-100)
completeGeneration completeddata (contains success, imageUrls, etc.)
errorGeneration failedmessage (error info), data (contains success: false)

Event Data Structure ​

typescript
interface ProgressEvent {
  type: 'status' | 'complete' | 'error'
  message: string
  progress: number          // 0 - 100
  data?: {
    success: boolean
    imageUrls?: string[]
    error?: string
  }
}

Code Examples ​

python
import requests
import json

API_KEY = "nb_your_api_key_here"
BASE_URL = "https://www.nananobanana.com/api/v1"

# Stream mode — receive generation progress in real-time
response = requests.post(
    f"{BASE_URL}/generate",
    headers={
        "Content-Type": "application/json",
        "Authorization": f"Bearer {API_KEY}"
    },
    json={
        "prompt": "Cyberpunk style futuristic city at night",
        "selectedModel": "nano-banana",
        "mode": "stream"
    },
    stream=True  # Enable streaming
)

for line in response.iter_lines():
    if line:
        decoded = line.decode("utf-8")
        if decoded.startswith("data: "):
            event = json.loads(decoded[6:])
            
            if event["type"] == "status":
                print(f"Progress: {event.get('progress', 0)}% - {event['message']}")
            elif event["type"] == "complete":
                print("Generation complete!")
                print("Image URLs:", event["data"]["imageUrls"])
            elif event["type"] == "error":
                print("Error:", event["message"])
                break
javascript
const API_KEY = "nb_your_api_key_here";
const BASE_URL = "https://www.nananobanana.com/api/v1";

// Stream mode — receive SSE events via fetch
async function generateWithStream() {
  const response = await fetch(`${BASE_URL}/generate`, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "Authorization": `Bearer ${API_KEY}`
    },
    body: JSON.stringify({
      prompt: "Cyberpunk style futuristic city at night",
      selectedModel: "nano-banana",
      mode: "stream"
    })
  });

  const reader = response.body.getReader();
  const decoder = new TextDecoder();

  while (true) {
    const { done, value } = await reader.read();
    if (done) break;

    const chunk = decoder.decode(value);
    const lines = chunk.split("\n").filter(line => line.startsWith("data: "));

    for (const line of lines) {
      const event = JSON.parse(line.slice(6));

      switch (event.type) {
        case "status":
          console.log(`Progress: ${event.progress}% - ${event.message}`);
          break;
        case "complete":
          console.log("Done! Images:", event.data.imageUrls);
          break;
        case "error":
          console.error("Error:", event.message);
          break;
      }
    }
  }
}

generateWithStream();
javascript
const API_KEY = "nb_your_api_key_here";
const BASE_URL = "https://www.nananobanana.com/api/v1";

// Browser implementation using fetch + ReadableStream
async function generateWithProgress(promptText, onProgress, onComplete, onError) {
  const response = await fetch(`${BASE_URL}/generate`, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "Authorization": `Bearer ${API_KEY}`
    },
    body: JSON.stringify({
      prompt: promptText,
      selectedModel: "nano-banana",
      mode: "stream"
    })
  });

  if (!response.ok) {
    const err = await response.json();
    onError(err.error || "Request failed");
    return;
  }

  const reader = response.body.getReader();
  const decoder = new TextDecoder();
  let buffer = "";

  while (true) {
    const { done, value } = await reader.read();
    if (done) break;

    buffer += decoder.decode(value, { stream: true });
    const lines = buffer.split("\n");
    buffer = lines.pop() || "";  // Keep incomplete lines

    for (const line of lines) {
      if (line.startsWith("data: ")) {
        try {
          const event = JSON.parse(line.slice(6));
          switch (event.type) {
            case "status":
              onProgress(event.progress, event.message);
              break;
            case "complete":
              onComplete(event.data);
              break;
            case "error":
              onError(event.message);
              return;
          }
        } catch (e) {
          // Ignore parse errors (e.g., heartbeat events)
        }
      }
    }
  }
}

// Usage example
generateWithProgress(
  "A cute orange cat",
  (progress, message) => {
    document.getElementById("progress").style.width = `${progress}%`;
    document.getElementById("status").textContent = message;
  },
  (data) => {
    document.getElementById("result").src = data.imageUrls[0];
  },
  (error) => {
    alert("Generation failed: " + error);
  }
);

Connection Mechanics ​

Heartbeat ​

The server sends a heartbeat event every 15 seconds to keep the connection alive:

text
data: {"type":"status","message":"Processing...","progress":0}

Connection Closing ​

After generation completes (complete) or fails (error), the server automatically closes the SSE connection.

Client Disconnect ​

If the client disconnects, the server detects this and stops pushing events. The generation task continues in the background, and the result can be retrieved via GET /api/v1/generate?id={generationId}.

Best For ​

  • ✅ Frontend UI with real-time progress bars
  • ✅ Providing users with processing status feedback
  • ✅ Long generation scenarios where you want to avoid timeout

Important Notes ​

WARNING

  • Stream requests use the SSE protocol (text/event-stream). Some proxy servers or CDNs may buffer SSE data, causing delays. Ensure your network environment supports SSE.
  • Clients should implement proper disconnection handling and reconnection logic.
  • Each SSE event starts with data: and ends with two newline characters \n\n.

Released under the MIT License.