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

# Generate an image with Gemini

> Generate and edit images with Gemini image models.

Gemini image models do not use `/v1/images/*`. They generate images through the same `generateContent` endpoint as text models, and return the bytes as an inline part alongside any text the model produced.

For GPT image models, use [`POST /v1/images/generations`](/api-reference/image/generations) instead.

See [Nano Banana](/use-cases/nano-banana) for worked text-to-image and image-to-image examples.

<Info>
  Model IDs change over time. List the ones available to your account with [`GET /v1/models`](/api-reference/list-models).
</Info>

## Endpoint

| Mode | Path |
| - | - |
| Non-streaming | `POST /v1beta/models/{model}:generateContent` |
| Streaming | `POST /v1beta/models/{model}:streamGenerateContent` |

Authenticate with `x-goog-api-key`, as on every Gemini route. See [Gemini overview](/api-reference/gemini/overview).

<ParamField body="contents" type="array" required>
  Conversation turns. For text-to-image a single user turn with one text part is enough; for editing, add the source image as an inline part in the same turn.

  <Expandable title="contents[]">
    <ParamField body="role" type="string">
      `user` or `model`.
    </ParamField>

    <ParamField body="parts" type="array" required>
      Mixed text and inline image parts.

      <Expandable title="parts[]">
        <ParamField body="text" type="string">
          Prompt text.
        </ParamField>

        <ParamField body="inlineData" type="object">
          A source image, as `mimeType` (for example `image/png`) plus base64 `data`.
        </ParamField>
      </Expandable>
    </ParamField>
  </Expandable>
</ParamField>

<ParamField body="generationConfig" type="object">
  Standard Gemini generation settings. Image models also accept sizing controls here; the accepted aspect ratios and resolutions differ per model, so check the model's own card before relying on a value.
</ParamField>

<RequestExample>
  ```bash cURL theme={"dark"}
  curl https://api.pipellm.ai/v1beta/models/gemini-3-pro-image:generateContent \
    -H "x-goog-api-key: $PIPELLM_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "contents": [{
        "role": "user",
        "parts": [{"text": "A red bicycle leaning against a cafe window, morning light"}]
      }]
    }'
  ```

  ```python Python theme={"dark"}
  import os
  from google import genai

  client = genai.Client(
      api_key=os.environ["PIPELLM_API_KEY"],
      http_options={"base_url": "https://api.pipellm.ai"},
  )

  response = client.models.generate_content(
      model="gemini-3-pro-image",
      contents="A red bicycle leaning against a cafe window, morning light",
  )

  for part in response.parts:
      if part.inline_data is not None:
          part.as_image().save("bicycle.png")
  ```

  ```typescript TypeScript theme={"dark"}
  import fs from "node:fs";
  import { GoogleGenAI } from "@google/genai";

  const ai = new GoogleGenAI({
    apiKey: process.env.PIPELLM_API_KEY,
    httpOptions: { baseUrl: "https://api.pipellm.ai" },
  });

  const response = await ai.models.generateContent({
    model: "gemini-3-pro-image",
    contents: "A red bicycle leaning against a cafe window, morning light",
  });

  for (const part of response.candidates[0].content.parts) {
    if (part.inlineData) {
      fs.writeFileSync("bicycle.png", Buffer.from(part.inlineData.data, "base64"));
    }
  }
  ```
</RequestExample>

<ResponseExample>
  ```json theme={"dark"}
  {
    "candidates": [{
      "content": {
        "role": "model",
        "parts": [
          { "text": "Here is the bicycle scene you asked for." },
          {
            "inlineData": {
              "mimeType": "image/png",
              "data": "iVBORw0KGgoAAAANSUhEUgAA..."
            }
          }
        ]
      },
      "finishReason": "STOP"
    }],
    "usageMetadata": {
      "promptTokenCount": 12,
      "candidatesTokenCount": 1290,
      "totalTokenCount": 1302
    }
  }
  ```
</ResponseExample>

<ResponseField name="candidates" type="array">
  Generated responses.

  <Expandable title="candidates[]">
    <ResponseField name="content.parts" type="array">
      Interleaved output. A part carries either `text` or `inlineData`, never both. A single response can hold several of each, so iterate over all parts rather than reading `parts[0]`.
    </ResponseField>

    <ResponseField name="content.parts[].inlineData.mimeType" type="string">
      For example `image/png`.
    </ResponseField>

    <ResponseField name="content.parts[].inlineData.data" type="string">
      Base64-encoded image bytes.
    </ResponseField>

    <ResponseField name="finishReason" type="string">
      `STOP` on success. A safety stop returns no image part.
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="usageMetadata" type="object">
  Token counts. Generated images are billed as image output tokens, which dominate the cost — see [Pricing](/api-reference/model-pricing).
</ResponseField>

## Reading the response

Two habits save trouble:

* **Iterate over every part.** Ask for an illustrated explanation and you get text and image parts interleaved. Taking only the first part silently drops content.
* **Expect a text-only response sometimes.** If the prompt is refused on safety grounds the response still returns `200` with a text part and no `inlineData`. Check for the image part before decoding.

Images generated by Gemini carry a SynthID watermark.

## Errors

Gemini routes return the Gemini error shape, not the OpenAI one. See [Errors](/api-reference/errors) for the full mapping.

<AccordionGroup>
  <Accordion title="400 INVALID_ARGUMENT">
    Malformed `contents`, an unsupported `mimeType` on an inline part, or a sizing value the model does not accept.
  </Accordion>

  <Accordion title="401 UNAUTHENTICATED">
    Missing or wrong `x-goog-api-key`.
  </Accordion>

  <Accordion title="429 RESOURCE_EXHAUSTED">
    See [Rate Limits](/api-reference/rate-limits).
  </Accordion>
</AccordionGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.