> ## Documentation Index
> Fetch the complete documentation index at: https://docs.mixroute.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Gemini Native (Image, Streaming)

> Gemini native image generation models, streaming text-to-image and image-to-image with streamGenerateContent

## Introduction

`streamGenerateContent` is the streaming version of Gemini native `generateContent`. Nano Banana maps to Google Gemini's native image generation capability, including Nano Banana (`gemini-2.5-flash-image`), Nano Banana 2 (`gemini-3.1-flash-image`, `gemini-3.1-flash-image-preview`), and Nano Banana Pro (`gemini-3-pro-image`). When calling the MixRoute native streaming API, use the endpoint `POST /v1/models/<model>:streamGenerateContent?alt=sse` and pass a JSON Payload that follows the Gemini native structure. It supports text-to-image, image-to-image, and multi-image fusion.

The streaming API returns Server-Sent Events (SSE). Clients should read the `data:` event content line by line instead of parsing the entire response body as a single JSON object.

For complete parameters and multi-model details, see [Image generation](/cn/api-reference/endpoint/image-generation).

## Authentication

Bearer Token, such as `Bearer sk-xxxxxxxxxx`

## Supported Models

| Model ID                         | Description                                                                                         |
| -------------------------------- | --------------------------------------------------------------------------------------------------- |
| `gemini-2.5-flash-image`         | Nano Banana, cost-effective text-to-image/image-to-image, with fixed 1024px-class output dimensions |
| `gemini-3.1-flash-image`         | Nano Banana 2, an excellent balance of image quality and speed, supporting 1K, 2K, and 4K output    |
| `gemini-3.1-flash-image-preview` | Nano Banana 2 preview, supports image generation testing                                            |
| `gemini-3-pro-image`             | Nano Banana Pro, higher image quality and strong detail control, supporting 1K, 2K, and 4K output   |

## Basic Examples

<Tabs>
  <Tab title="Text-to-image">
    ```bash theme={null}
    curl -X POST "https://api.mixroute.ai/v1/models/gemini-3.1-flash-image:streamGenerateContent?alt=sse" \
      -H "Authorization: Bearer sk-xxxxxxxxxx" \
      -H "Content-Type: application/json" \
      --no-buffer \
      -d '{
        "contents": [
          {
            "role": "user",
            "parts": [
              {
                "text": "A cute orange kitten sitting in a garden, bright sunshine, high-quality photography"
              }
            ]
          }
        ],
        "generationConfig": {
          "responseModalities": [
            "TEXT",
            "IMAGE"
          ],
          "imageConfig": {
            "aspectRatio": "16:9",
            "imageSize": "2K"
          }
        }
      }'
    ```
  </Tab>

  <Tab title="Image-to-image">
    ```bash theme={null}
    curl -X POST "https://api.mixroute.ai/v1/models/gemini-3.1-flash-image:streamGenerateContent?alt=sse" \
      -H "Authorization: Bearer sk-xxxxxxxxxx" \
      -H "Content-Type: application/json" \
      --no-buffer \
      -d '{
        "contents": [
          {
            "role": "user",
            "parts": [
              {
                "text": "Generate an image overlooking Canton Tower based on this image"
              },
              {
                "inlineData": {
                  "mimeType": "image/png",
                  "data": "iVBORw0KGgoAAA..."
                }
              }
            ]
          }
        ],
        "generationConfig": {
          "responseModalities": [
            "TEXT",
            "IMAGE"
          ],
          "imageConfig": {
            "aspectRatio": "16:9",
            "imageSize": "2K"
          }
        }
      }'
    ```
  </Tab>
</Tabs>

## Request Parameters

<ParamField path="model" type="string" required>
  The Gemini native (image) model identifier, located in the URL path, such as `gemini-3.1-flash-image` or `gemini-3-pro-image`.
</ParamField>

<ParamField query="alt" type="string">
  Streaming response format. When `sse` is passed, the API returns streaming `GenerateContentResponse` data as Server-Sent Events.
</ParamField>

<ParamField body="contents" type="array" required>
  Native conversation or image content list.

  <Expandable title="contents subfields">
    <ParamField body="contents[].role" type="string">
      Role identifier, usually `"user"`.
    </ParamField>

    <ParamField body="contents[].parts" type="array" required>
      Content block list. Each element can be a text prompt (`{"text": "..."}`) or inline media data (`{"inlineData": {"mimeType": "...", "data": "<BASE64>"}}`).

      <Expandable title="parts subfields">
        <ParamField body="contents[].parts[].inlineData" type="object">
          Inline media data object.

          <Expandable title="inlineData subfields">
            <ParamField body="contents[].parts[].inlineData.mimeType" type="string">
              The MIME format of the media type, such as `"image/png"`, `"image/jpeg"`, `"image/webp"`, and so on.
            </ParamField>

            <ParamField body="contents[].parts[].inlineData.data" type="string">
              The Base64-encoded image data string (without a prefix).
            </ParamField>
          </Expandable>
        </ParamField>
      </Expandable>
    </ParamField>
  </Expandable>
</ParamField>

<ParamField body="generationConfig" type="object">
  Generation configuration parameters.

  <Expandable title="generationConfig subfields">
    <ParamField body="generationConfig.responseModalities" type="array">
      Response modalities. If this field is provided, it must include `"IMAGE"`; the common form is `["TEXT", "IMAGE"]`.
    </ParamField>

    <ParamField body="generationConfig.imageConfig" type="object">
      Image generation configuration.

      <Expandable title="imageConfig subfields">
        <ParamField body="generationConfig.imageConfig.aspectRatio" type="string">
          The output image aspect ratio. `gemini-2.5-flash-image` and `gemini-3-pro-image` support `"1:1"`, `"2:3"`, `"3:2"`, `"3:4"`, `"4:3"`, `"4:5"`, `"5:4"`, `"9:16"`, `"16:9"`, and `"21:9"`; the `gemini-3.1-flash-image` series additionally supports `"1:4"`, `"4:1"`, `"1:8"`, and `"8:1"`.
        </ParamField>

        <ParamField body="generationConfig.imageConfig.imageSize" type="string">
          The output resolution or size tier. `gemini-2.5-flash-image` does not use this field and returns fixed 1024px-class dimensions according to the aspect ratio; the `gemini-3.1-flash-image` series and `gemini-3-pro-image` support `"1K"`, `"2K"`, and `"4K"`.
        </ParamField>
      </Expandable>
    </ParamField>
  </Expandable>
</ParamField>

For more parameters and model differences, see [Image generation](/cn/api-reference/endpoint/image-generation).

## Response Format

The API returns an SSE stream. Each `data:` event contains a JSON `GenerateContentResponse` instance; images may appear in one or more chunks' `candidates` as one or more Base64-encoded `inlineData` entries. Clients should read SSE line by line and parse the JSON after each `data:` separately.

| Field                                              | Type    | Description                                                        |
| -------------------------------------------------- | ------- | ------------------------------------------------------------------ |
| `data`                                             | string  | SSE event data line, containing one JSON `GenerateContentResponse` |
| `candidates[].content.parts[].text`                | string  | Text fragment returned by the model                                |
| `candidates[].content.parts[].inlineData.mimeType` | string  | Image MIME type, such as `image/png`                               |
| `candidates[].content.parts[].inlineData.data`     | string  | Base64-encoded image data                                          |
| `candidates[].finishReason`                        | string  | Generation finish reason, such as `"STOP"`                         |
| `usageMetadata.promptTokenCount`                   | integer | Number of input tokens                                             |
| `usageMetadata.candidatesTokenCount`               | integer | Number of output tokens (image tokens)                             |
| `modelVersion`                                     | string  | The actual model version used                                      |
| `responseId`                                       | string  | Unique response identifier                                         |

<RequestExample>
  ```bash cURL theme={null}
  curl --request POST \
    --url 'https://api.mixroute.ai/v1/models/gemini-3.1-flash-image:streamGenerateContent?alt=sse' \
    --header 'Authorization: Bearer sk-xxxxxxxxxx' \
    --header 'Content-Type: application/json' \
    --no-buffer \
    --data '{
      "contents": [
        {
          "role": "user",
          "parts": [
            {
              "text": "A cute orange kitten sitting in a garden"
            }
          ]
        }
      ],
      "generationConfig": {
        "responseModalities": [
          "TEXT",
          "IMAGE"
        ],
        "imageConfig": {
          "aspectRatio": "16:9",
          "imageSize": "2K"
        }
      }
    }'
  ```

  ```python Python theme={null}
  import json
  import requests

  url = "https://api.mixroute.ai/v1/models/gemini-3.1-flash-image:streamGenerateContent?alt=sse"
  headers = {
      "Authorization": "Bearer sk-xxxxxxxxxx",
      "Content-Type": "application/json"
  }
  payload = {
      "contents": [
          {
              "role": "user",
              "parts": [
                  {
                      "text": "A cute orange kitten sitting in a garden"
                  }
              ]
          }
      ],
      "generationConfig": {
          "responseModalities": [
              "TEXT",
              "IMAGE"
          ],
          "imageConfig": {
              "aspectRatio": "16:9",
              "imageSize": "2K"
          }
      }
  }

  with requests.post(url, json=payload, headers=headers, stream=True) as response:
      response.raise_for_status()
      for line in response.iter_lines(decode_unicode=True):
          if not line or not line.startswith("data: "):
              continue
          chunk = json.loads(line.removeprefix("data: "))
          print(chunk)
  ```

  ```javascript JavaScript theme={null}
  const response = await fetch('https://api.mixroute.ai/v1/models/gemini-3.1-flash-image:streamGenerateContent?alt=sse', {
    method: 'POST',
    headers: {
      'Authorization': 'Bearer sk-xxxxxxxxxx',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      contents: [
        {
          role: "user",
          parts: [
            {
              text: "A cute orange kitten sitting in a garden"
            }
          ]
        }
      ],
      generationConfig: {
        responseModalities: [
          "TEXT",
          "IMAGE"
        ],
        imageConfig: {
          aspectRatio: "16:9",
          imageSize: "2K"
        }
      }
    })
  });

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

  while (true) {
    const { value, done } = await reader.read();
    if (done) break;
    buffer += decoder.decode(value, { stream: true });
    const lines = buffer.split("\n");
    buffer = lines.pop() ?? "";

    for (const line of lines) {
      if (!line.startsWith("data: ")) continue;
      const chunk = JSON.parse(line.slice("data: ".length));
      console.log(chunk);
    }
  }

  if (buffer.startsWith("data: ")) {
    const chunk = JSON.parse(buffer.slice("data: ".length));
    console.log(chunk);
  }
  ```

  ```php PHP theme={null}
  <?php
  $client = new GuzzleHttp\Client();
  $response = $client->post('https://api.mixroute.ai/v1/models/gemini-3.1-flash-image:streamGenerateContent?alt=sse', [
      'headers' => [
          'Authorization' => 'Bearer sk-xxxxxxxxxx',
          'Content-Type' => 'application/json',
      ],
      'stream' => true,
      'json' => [
          'contents' => [
              [
                  'role' => 'user',
                  'parts' => [
                      [
                          'text' => 'A cute orange kitten sitting in a garden'
                      ]
                  ]
              ]
          ],
          'generationConfig' => [
              'responseModalities' => [
                  'TEXT',
                  'IMAGE'
              ],
              'imageConfig' => [
                  'aspectRatio' => '16:9',
                  'imageSize' => '2K'
              ]
          ]
      ]
  ]);

  $body = $response->getBody();
  $buffer = '';
  while (!$body->eof()) {
      $buffer .= $body->read(8192);
      $lines = explode("\n", $buffer);
      $buffer = array_pop($lines);
      foreach ($lines as $line) {
          if (str_starts_with($line, 'data: ')) {
              print_r(json_decode(substr($line, 6), true));
          }
      }
  }

  if (str_starts_with($buffer, 'data: ')) {
      print_r(json_decode(substr($buffer, 6), true));
  }
  ```

  ```go Go theme={null}
  package main

  import (
      "bufio"
      "bytes"
      "encoding/json"
      "fmt"
      "net/http"
      "strings"
  )

  func main() {
      payload := map[string]interface{}{
          "contents": []interface{}{
              map[string]interface{}{
                  "role": "user",
                  "parts": []interface{}{
                      map[string]interface{}{
                          "text": "A cute orange kitten sitting in a garden",
                      },
                  },
              },
          },
          "generationConfig": map[string]interface{}{
              "responseModalities": []string{
                  "TEXT",
                  "IMAGE",
              },
              "imageConfig": map[string]interface{}{
                  "aspectRatio": "16:9",
                  "imageSize":   "2K",
              },
          },
      }
      body, _ := json.Marshal(payload)
      req, _ := http.NewRequest("POST", "https://api.mixroute.ai/v1/models/gemini-3.1-flash-image:streamGenerateContent?alt=sse", bytes.NewBuffer(body))
      req.Header.Set("Authorization", "Bearer sk-xxxxxxxxxx")
      req.Header.Set("Content-Type", "application/json")

      resp, _ := http.DefaultClient.Do(req)
      defer resp.Body.Close()

      reader := bufio.NewReader(resp.Body)
      for {
          line, err := reader.ReadString('\n')
          if len(line) > 0 {
              line = strings.TrimSuffix(line, "\n")
              line = strings.TrimSuffix(line, "\r")
          }
          if strings.HasPrefix(line, "data: ") {
              fmt.Println(strings.TrimPrefix(line, "data: "))
          }
          if err != nil {
              break
          }
      }
  }
  ```

  ```java Java theme={null}
  import java.net.http.*;
  import java.net.URI;

  HttpClient client = HttpClient.newHttpClient();
  String json = """
      {
          "contents": [
              {
                  "role": "user",
                  "parts": [
                      {
                          "text": "A cute orange kitten sitting in a garden"
                      }
                  ]
              }
          ],
          "generationConfig": {
              "responseModalities": [
                  "TEXT",
                  "IMAGE"
              ],
              "imageConfig": {
                  "aspectRatio": "16:9",
                  "imageSize": "2K"
              }
          }
      }
      """;
  HttpRequest request = HttpRequest.newBuilder()
      .uri(URI.create("https://api.mixroute.ai/v1/models/gemini-3.1-flash-image:streamGenerateContent?alt=sse"))
      .header("Authorization", "Bearer sk-xxxxxxxxxx")
      .header("Content-Type", "application/json")
      .POST(HttpRequest.BodyPublishers.ofString(json))
      .build();

  HttpResponse<java.util.stream.Stream<String>> response = client.send(
      request,
      HttpResponse.BodyHandlers.ofLines()
  );

  response.body()
      .filter(line -> line.startsWith("data: "))
      .forEach(line -> System.out.println(line.substring("data: ".length())));
  ```

  ```ruby Ruby theme={null}
  require 'net/http'
  require 'json'

  uri = URI('https://api.mixroute.ai/v1/models/gemini-3.1-flash-image:streamGenerateContent?alt=sse')
  http = Net::HTTP.new(uri.host, uri.port)
  http.use_ssl = true

  request = Net::HTTP::Post.new(uri)
  request['Authorization'] = 'Bearer sk-xxxxxxxxxx'
  request['Content-Type'] = 'application/json'
  request.body = {
    contents: [
      {
        role: 'user',
        parts: [
          {
            text: 'A cute orange kitten sitting in a garden'
          }
        ]
      }
    ],
    generationConfig: {
      responseModalities: [
        'TEXT',
        'IMAGE'
      ],
      imageConfig: {
        aspectRatio: '16:9',
        imageSize: '2K'
      }
    }
  }.to_json

  http.request(request) do |response|
    buffer = ''
    response.read_body do |chunk|
      buffer << chunk
      lines = buffer.split("\n")
      buffer = lines.pop || ''

      lines.each do |line|
        line = line.delete_suffix("\r")
        next unless line.start_with?('data: ')

        puts JSON.parse(line.delete_prefix('data: '))
      end
    end

    if buffer.start_with?('data: ')
      puts JSON.parse(buffer.delete_prefix('data: '))
    end
  end
  ```
</RequestExample>

<ResponseExample>
  ```text Response theme={null}
  data: {"candidates":[{"content":{"role":"model","parts":[{"text":"Generating the image."}]}}],"modelVersion":"gemini-3.1-flash-image","responseId":"stream-response-id"}

  data: {"candidates":[{"content":{"role":"model","parts":[{"inlineData":{"mimeType":"image/png","data":"iVBORw0KGgoAAAANSUhEUgAAAAQAAAAECAIA..."}}]},"finishReason":"STOP"}],"usageMetadata":{"promptTokenCount":13,"candidatesTokenCount":1680,"totalTokenCount":1693,"promptTokensDetails":[{"modality":"TEXT","tokenCount":13}],"candidatesTokensDetails":[{"modality":"IMAGE","tokenCount":1680}]},"modelVersion":"gemini-3.1-flash-image","responseId":"stream-response-id"}
  ```
</ResponseExample>
