Skip to content

Nano Banana Image Guide ​

Use Nano Banana image models through the Gemini-compatible API. This page covers text-to-image, image-to-image and request parameters. Aspect ratios, reference-image limits and resolution support can differ by model; unconfirmed capabilities are not guaranteed across the family.

  • Base URL — https://api.llmnex.com
  • Hong Kong acceleration — https://hk.llmnex.com
  • Protocol — Gemini generateContent
  • Available models — determined by the model list for your key
  • Resolution — confirm per model; output dimensions are not guaranteed to be identical across models

Overview ​

Use the Gemini-compatible request format with https://api.llmnex.com and an API key issued by this service. Configure output modalities and supported parameters as described below. For Hong Kong acceleration, use https://hk.llmnex.com with the same paths and authentication. https://www.llmnex.com is the console, not an API endpoint.

ItemValue
Base URLhttps://api.llmnex.com
Hong Kong accelerationhttps://hk.llmnex.com
Image endpointPOST /v1beta/models/{model}:generateContent
Streaming endpointPOST /v1beta/models/{model}:streamGenerateContent
Response formatIMAGE examples below use base64 in inlineData; determine the image format from the response mimeType
Images per requestExamples handle one image; do not rely on candidateCount for multiple images
Usage statisticsNon-streaming responses include usageMetadata (token counts); see Response Structure

A model name identifies the target and billing mode, not all of its capabilities. In particular, only the name gemini-nano-banana-2.1 and backend Nano Banana 2.1 are confirmed; ask the service provider about other capabilities.

Authentication ​

Use either authentication method below. With an official Google SDK, configure the base URL, API key and output modality according to that SDK. This guide provides HTTP examples; changing only the URL is not guaranteed to work with every SDK version.

http
Authorization: Bearer YOUR_API_KEY
http
x-goog-api-key: YOUR_API_KEY

Keep your key private

Keys look like sk-xxxxxxxx. Make requests from your server; do not embed keys in frontend code or client apps.

Quick Start ​

Image generation requires explicit IMAGE output

The backend supports TEXT mode for fast initial text responses. To generate images, generationConfig.responseModalities must explicitly include "IMAGE". Use "responseModalities": ["IMAGE"]. If omitted or set to TEXT only, Pro models may generate text instead of images.

bash
curl -X POST "https://api.llmnex.com/v1beta/models/gemini-3-pro-image-c:generateContent" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  --max-time 300 \
  -d '{
    "contents": [
      { "parts": [ { "text": "a red ceramic teapot on a wooden table, soft daylight" } ] }
    ],
    "generationConfig": { "responseModalities": ["IMAGE"] }
  }'
bash
set -o pipefail
curl -fsS -X POST "https://api.llmnex.com/v1beta/models/gemini-3-pro-image-c:generateContent" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" --max-time 300 \
  -d '{"contents":[{"parts":[{"text":"a red ceramic teapot"}]}],"generationConfig":{"responseModalities":["IMAGE"]}}' \
  | python3 -c "import sys,json,base64;
d=json.load(sys.stdin);
candidates=d.get('candidates') or [];
parts=(candidates[0].get('content',{}).get('parts') or []) if candidates else [];
b=next((p.get('inlineData',{}).get('data') for p in parts if p.get('inlineData',{}).get('data')),None);
if not b: sys.exit('No image found in response; check responseModalities, model support and response content');
image=base64.b64decode(b,validate=True);
open('out.png','wb').write(image);
print('saved out.png')"

Models & Resolution ​

The model suffix distinguishes billing modes, not fixed resolutions. Models with -c use per-request billing; models without -c use token-based billing. List available models first. Prices and actual charges are determined by the console and your account bill.

Model familyBackendNotes
gemini-3-pro-imageNano Banana ProPro family; quality and latency vary by request, with no absolute ranking
gemini-3.1-flash-imageNano Banana 2Flash family; validate quality and latency for your workload
gemini-nano-banana-2.1Nano Banana 2.1New Nano Banana model; consult model-specific capability information

Naming examples (actual availability depends on the list for your key):

Model nameBilling
gemini-3-pro-image-cPer-request
gemini-3-pro-imageToken-based

Old fixed-resolution-suffix models have been retired. Update the full model name in your requests; do not switch automatically without confirming the billing mode.

The -c Suffix: Choose Resolution in the Request ​

Models ending in -c do not lock the resolution. Set it using generationConfig.imageConfig.imageSize. The default is 1K when omitted.

json
{
  "contents": [ { "parts": [ { "text": "a plain grey cube" } ] } ],
  "generationConfig": {
    "responseModalities": ["IMAGE"],
    "imageConfig": { "imageSize": "4K" }
  }
}

The -c examples on this page use 1K, 2K or 4K for imageSize. Do not pass unlisted values such as 512 or rely on automatic fallback for invalid values. Confirm the supported range for your model first.

Models without -c

Models without -c use token-based billing. Confirm resolution-parameter support in the specific model's documentation. Do not reuse fixed-resolution rules from retired models.

About 4K

4K is a resolution tier, not a fixed width-and-height guarantee. Read the actual dimensions of the returned image. Do not hardcode 4096 × 4096 or assume identical dimension rules across models.

Request Body ​

The request follows the official Gemini format. In addition to contents, image requests must explicitly include "IMAGE" in generationConfig.responseModalities. A prompt asking for an image is not sufficient.

FieldTypeDescription
contents[].parts[].textstringPrompt; multiple text parts are joined with spaces
contents[].parts[].inlineDataobjectEmbedded reference image; see Image-to-Image. Confirm image-count limits per model
generationConfig.responseModalitiesstring[]Must explicitly include "IMAGE" for images; use ["IMAGE"]. The backend supports TEXT, so omission or TEXT-only requests may make Pro return text
generationConfig.imageConfig.aspectRatiostringAspect ratio, such as "16:9"; default 1:1
generationConfig.imageConfig.imageSizestring1K / 2K / 4K for -c models; confirm parameter support for models without -c
json
{
  "contents": [
    { "parts": [ { "text": "a lighthouse on a cliff at dawn, cinematic" } ] }
  ],
  "generationConfig": {
    "responseModalities": ["IMAGE"],
    "imageConfig": { "aspectRatio": "16:9" }
  }
}

Aspect Ratios ​

Historical documentation lists 1:1, 16:9, 9:16, 4:3, 3:4, 3:2, 2:3, 5:4, 4:5, 21:9, 1:4, 4:1, 1:8 and 8:1. This is not a universal support list for current models. Confirm the target model's ratios before use.

The following are historical 1K examples with no model attribution. They are not output guarantees for current models; use the dimensions of the actual returned image.

aspectRatioHistorical 1K dimensionsTypical use
1:11024 × 1024Avatars, product images
16:91376 × 768Landscape covers, video opening frames
9:16768 × 1376Vertical short videos, phone wallpapers
4:31200 × 896Traditional photography
21:91584 × 672Ultrawide banners
4:5928 × 1152Portrait social images

Note

Do not extrapolate fixed 2K / 4K dimensions from these examples or rely on automatic fallback for unsupported ratios. Correct an unsupported parameter instead of assuming the service will ignore it.

Confirm extreme aspect ratios separately

Historical documentation marks 1:4, 4:1, 1:8 and 8:1 as unsupported by the gemini-3-pro-image family. Avoid these four ratios for Pro until reconfirmed. Ask the service provider about support and pixel-dimension rules for other models, including Nano Banana 2.1. Do not inherit scaling or error rules from old examples.

Image-to-Image ​

Add inlineData to parts for image-to-image requests. Describe the desired changes in text and supply reference images for content or style.

json
{
  "contents": [
    {
      "parts": [
        { "text": "Turn this photo into a watercolor painting, preserving the composition" },
        {
          "inlineData": {
            "mimeType": "image/png",
            "data": "iVBORw0KGgoAAAANSUhEUg..."
          }
        }
      ]
    }
  ],
  "generationConfig": { "responseModalities": ["IMAGE"] }
}
ConstraintValue
Reference-image countExamples use one; confirm multi-image support and limits per model
FormatsPNG / JPEG / WebP, declared by mimeType
EncodingRaw base64, without a data:image/png;base64, prefix
Output resolutionSet through imageSize for -c models, independently of input dimensions

Parameters without Effect ​

These compatibility limits come from historical documentation and have not been revalidated for every model. Do not rely on these parameters. Acceptance and identical results are not guaranteed across models.

ParameterRecorded behavior
generationConfig.candidateCountExamples handle one image; do not rely on this parameter for multiple images
generationConfig.seedAccepted, but reproducibility across requests is not guaranteed; the same seed and prompt may produce different images
googleSearch in toolsNo web access and no groundingMetadata in the response
generationConfig.thinkingConfigNo effect and no thinking-token count in the response
contents[].parts[].fileData (reference-image URLs)Not read; embed reference images using inlineData instead

Streaming (Not Recommended) ​

Prefer non-streaming :generateContent

The following describes historical IMAGE streaming behavior, not rechecked for every model. It does not cover fast initial TEXT responses or guarantee the same behavior for new models.

Images are generated as a whole, without intermediate images to stream. The endpoint waits for the complete image, then sends the final result in one SSE chunk. Its content matches the generateContent response body, except that usageMetadata is absent.

You incur SSE parsing costs without the usual streaming benefits:

  • No earlier image preview: generation takes as long as non-streaming.
  • No connection keepalive: the server sends no bytes during generation, so intermediary idle timeouts are not prevented.
  • All base64 data occupies one data: line, historically often 7–11 MB. Some SSE clients and reverse proxies limit line length, increasing truncation risk.
  • Errors are not SSE: they are returned as JSON error bodies, requiring handling of both formats.
  • No usageMetadata: use non-streaming if you need usage statistics.

Allow sufficient timeouts in the client and intermediaries. Increasing only the client timeout does not prevent proxy or gateway timeouts. See Timeouts & Latency below.

The endpoint remains for compatibility with existing code. Change the endpoint to :streamGenerateContent to receive SSE with lines beginning with data: .

bash
curl -N -X POST "https://api.llmnex.com/v1beta/models/gemini-3-pro-image-c:streamGenerateContent" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" --max-time 300 \
  -d '{"contents":[{"parts":[{"text":"a plain grey cube"}]}],"generationConfig":{"responseModalities":["IMAGE"]}}'

Response Structure ​

This is an example of a successful non-streaming image response from gemini-3-pro-image-c with explicit responseModalities: ["IMAGE"]. It does not guarantee the structure of TEXT or error responses.

json
{
  "candidates": [
    {
      "content": {
        "role": "model",
        "parts": [
          {
            "inlineData": {
              "mimeType": "image/png",
              "data": "iVBORw0KGgoAAAANSUhEUgAABAAA..."
            }
          }
        ]
      },
      "finishReason": "STOP",
      "index": 0
    }
  ],
  "responseId": "ee2bcd924c5d",
  "upstreamJobId": "d427bc37-0f6c-458b-93c9-d40a0b5412f7",
  "usageMetadata": {
    "promptTokenCount": 4,
    "promptTokensDetails": [ { "modality": "TEXT", "tokenCount": 4 } ],
    "candidatesTokenCount": 1120,
    "candidatesTokensDetails": [ { "modality": "IMAGE", "tokenCount": 1120 } ],
    "totalTokenCount": 1124
  }
}

For successful image generation, search candidates[0].content.parts for inlineData.data; do not hardcode parts[0]. Check the HTTP status first, then check candidates and image data. Report an error when no image is present instead of decoding or writing a file.

FieldTypeDescription
candidates[]arrayExamples use the first candidate; check it exists
candidates[].content.rolestringmodel in this example
candidates[].content.parts[].inlineData.mimeTypestringimage/png in this example; use the actual response value
candidates[].content.parts[].inlineData.datastringBase64 image data; check it exists before decoding
candidates[].finishReasonstringSTOP for normal image completion
candidates[].indexinteger0 in this example
responseIdstringService request ID, matching the X-Oneapi-Request-Id response header
upstreamJobIdstringUpstream job ID; include it with responseId in issue reports
usageMetadataobjectToken usage for this request; see below

This example shows only an IMAGE result. The backend supports TEXT mode. If the output modality is omitted or TEXT only, Pro models may return text in parts[].text; not every successful response contains an image. Optional fields such as promptFeedback, modelVersion and safetyRatings depend on the actual response and are not required for image extraction.

usageMetadata ​

usageMetadata reports token usage in the official structure: promptTokenCount (prompt and reference images), candidatesTokenCount (generated images), totalTokenCount, and per-modality breakdowns in *Details. Usage fields and calculations may differ by model; do not estimate actual charges from a fixed token count per image.

Three notes

  • Historical IMAGE streaming requests (:streamGenerateContent) omit usageMetadata. This has not been confirmed for every model or TEXT mode. For usage statistics, prefer non-streaming and check the actual returned fields.
  • Historical documentation labels promptTokenCount as an estimate. Accuracy and calculation rules have not been confirmed for every model; treat it as a reference.
  • usageMetadata reports usage, not the billing mode. Models with -c use per-request billing; models without -c use token-based billing. Your account bill determines actual charges.

4K responses can be large

Historical 4K base64 response samples were about 25–28 MB. Actual size varies by model and image content. Check HTTP client and intermediary response-body limits, and allow time for transfer. The latency table below covers server generation time, not download. For 4K on a slow connection, use a 600-second timeout.

Timeouts & Latency ​

Generation is significantly faster than before. These typical times are references, not latency guarantees.

A 300-second client timeout is still recommended to allow for peak-time queues, retries and image transfer. Typical latency is not a maximum; shorter timeouts may interrupt requests.

TierTypical server generation time
1KAbout 15–25 seconds
2KAbout 15–35 seconds
4KAbout 20–45 seconds

Image transfer is additional; response sizes vary by model and image content. Upstream load may increase latency at peak times. The service already retries internally; avoid aggressive client retries. Correct parameter, authentication, permission and moderation issues first. See below for temporary-failure retry guidance.

Error Handling ​

HTTPMeaningAction
400Invalid body, such as missing or empty contentsCheck the JSON structure
401Invalid or expired keyCheck the Authorization header
429Request limitedBack off for 2–5 seconds and retry once; success is not guaranteed; avoid retry loops
451Content blocked by safety checksTerminal; do not retry unchanged. Modify the prompt or reference image
500Temporary upstream unavailability or internal gateway errorRetry once after a short delay; contact us if it persists
503model_not_found — incorrect name or no permission for the modelSee below
503Other messages, such as upstream session errorTemporary upstream connection failure; retry once after backoff

Keep the X-Oneapi-Request-Id response header and include it in reports when present. Never disclose your API key.

json
{
  "error": {
    "message": "image_unsafe: The generated images appear to be unsafe.",
    "type": "invalid_request_error"
  }
}

Do not automatically retry 451

451 indicates a content-safety block. Change the prompt or reference image rather than automatically repeating the request. Repeated requests are not guaranteed to receive the same moderation outcome, and this does not determine whether you are charged.

503 No available channel for model ...

Check spelling, retirement status and your key's permissions first. Use GET /v1/models to confirm the available name instead of automatically retrying. Contact the service provider if the model is missing. If listed but still failing, include the request ID for investigation; this message alone does not rule out a service-side issue.

Complete Examples ​

The Python examples require requests; the Node.js example requires a runtime with built-in fetch. These examples save a PNG response as out.png. If the actual mimeType differs, use the matching extension. A successful write overwrites any file with that name in the current directory; back it up first.

python
import base64, requests

API_KEY = "YOUR_API_KEY"
BASE    = "https://api.llmnex.com"
MODEL   = "gemini-3-pro-image-c"

resp = requests.post(
    f"{BASE}/v1beta/models/{MODEL}:generateContent",
    headers={"Authorization": f"Bearer {API_KEY}"},
    json={
        "contents": [{"parts": [{"text": "a lighthouse on a cliff at dawn"}]}],
        "generationConfig": {"responseModalities": ["IMAGE"], "imageConfig": {"aspectRatio": "16:9", "imageSize": "2K"}},
    },
    timeout=300,          # Allow time for queues, retries and transfer
)
resp.raise_for_status()

candidates = resp.json().get("candidates") or []
parts = (candidates[0].get("content", {}).get("parts") or []) if candidates else []
b64 = next((p.get("inlineData", {}).get("data") for p in parts if p.get("inlineData", {}).get("data")), None)
if not b64:
    raise RuntimeError("No image found in response; check responseModalities, model support and response content")
image = base64.b64decode(b64, validate=True)
with open("out.png", "wb") as f:
    f.write(image)
print("saved out.png")
javascript
import { writeFile } from "node:fs/promises";

const API_KEY = "YOUR_API_KEY";
const MODEL   = "gemini-3-pro-image-c";

const res = await fetch(
  `https://api.llmnex.com/v1beta/models/${MODEL}:generateContent`,
  {
    method: "POST",
    headers: {
      "Authorization": `Bearer ${API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      contents: [{ parts: [{ text: "a lighthouse on a cliff at dawn" }] }],
      generationConfig: { responseModalities: ["IMAGE"], imageConfig: { aspectRatio: "16:9", imageSize: "2K" } },
    }),
    signal: AbortSignal.timeout(300_000),   // 5 minutes
  }
);
if (!res.ok) throw new Error(`HTTP ${res.status}: ${await res.text()}`);

const payload = await res.json();
const parts = payload.candidates?.[0]?.content?.parts ?? [];
const b64 = parts.find((p) => p.inlineData?.data)?.inlineData.data;
if (!b64) throw new Error("No image found in response; check responseModalities, model support and response content");
await writeFile("out.png", Buffer.from(b64, "base64"));
console.log("saved out.png");
python
import base64, requests

src = base64.b64encode(open("input.png", "rb").read()).decode()

resp = requests.post(
    "https://api.llmnex.com/v1beta/models/gemini-3-pro-image-c:generateContent",
    headers={"Authorization": "Bearer YOUR_API_KEY"},
    json={"contents": [{"parts": [
        {"text": "Turn this photo into a watercolor painting, preserving the composition"},
        {"inlineData": {"mimeType": "image/png", "data": src}},
    ]}], "generationConfig": {"responseModalities": ["IMAGE"]}},
    timeout=300,
)
resp.raise_for_status()
candidates = resp.json().get("candidates") or []
parts = (candidates[0].get("content", {}).get("parts") or []) if candidates else []
b64 = next((p.get("inlineData", {}).get("data") for p in parts if p.get("inlineData", {}).get("data")), None)
if not b64:
    raise RuntimeError("No image found in response; check responseModalities, model support and response content")
image = base64.b64decode(b64, validate=True)
with open("out.png", "wb") as f:
    f.write(image)

LLMNEX · Model Integration Guide