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

# Use Gemini 2.5 Flash Image with Comfy Router

> Call vertexai/gemini-2.5-flash-image through Comfy Router: endpoint, request shape and the response Router returns.

API Reference for `vertexai/gemini-2.5-flash-image`, served by Comfy Router from Google.

## Quick start

Create a key at [platform.comfy.org/profile/api-keys](https://platform.comfy.org/profile/api-keys) and export it as `COMFY_API_KEY`. The Python and TypeScript snippets use the Comfy SDKs (`pip install comfy-sdk`, `npm install @comfyorg/sdk`); the cURL snippet is the same call over raw HTTP.

**Model ID:** `vertexai/gemini-2.5-flash-image`

**Endpoint:** `POST https://api.comfy.org/v2/models/vertexai/gemini-2.5-flash-image`

<CodeGroup>
  ```python Python theme={null}
  from comfy_sdk import Comfy

  # Reads COMFY_API_KEY from the environment. Each call sends a fresh
  # Idempotency-Key and waits up to 10 minutes for the finished result.
  with Comfy() as client:
      result = client.models.run(
          "vertexai/gemini-2.5-flash-image",
          {
              # Request fields are the provider's own — see Input below.
          },
      )

  print(result)
  ```

  ```typescript TypeScript theme={null}
  import { comfy } from "@comfyorg/sdk";

  // Reads COMFY_API_KEY from the environment. Each call sends a fresh
  // Idempotency-Key and waits up to 10 minutes for the finished result.
  const { data } = await comfy.models.run("vertexai/gemini-2.5-flash-image", {
    // Request fields are the provider's own — see Input below.
  });

  console.log(data);
  ```

  ```bash cURL theme={null}
  # Request fields are the provider's own — see Input below.
  curl https://api.comfy.org/v2/models/vertexai/gemini-2.5-flash-image \
    -H "X-API-Key: $COMFY_API_KEY" \
    -H "Idempotency-Key: $(uuidgen)" \
    -H "Content-Type: application/json" \
    -d '{}'
  ```
</CodeGroup>

## Schema

### Input

<Note>
  Router has not published an authored input schema for this model yet: `GET /v2/models/vertexai/gemini-2.5-flash-image/openapi.json` returns an open object with `x-comfy-input-schema-authored: false`. Router forwards the body to Google unchanged, so [Google's own API reference](https://cloud.google.com/vertex-ai/generative-ai/docs/model-reference/inference) is authoritative for the request fields, and nothing is validated server side.
</Note>

### Output

<ResponseField name="candidates" type="object[]" />

<ResponseField name="candidates[].citationMetadata" type="object" />

<ResponseField name="candidates[].citationMetadata.citations" type="object[]" />

<ResponseField name="candidates[].citationMetadata.citations[].authors" type="string[]" />

<ResponseField name="candidates[].citationMetadata.citations[].endIndex" type="integer" />

<ResponseField name="candidates[].citationMetadata.citations[].license" type="string" />

<ResponseField name="candidates[].citationMetadata.citations[].publicationDate" type="string (date)">
  Format: `date`
</ResponseField>

<ResponseField name="candidates[].citationMetadata.citations[].startIndex" type="integer" />

<ResponseField name="candidates[].citationMetadata.citations[].title" type="string" />

<ResponseField name="candidates[].citationMetadata.citations[].uri" type="string" />

<ResponseField name="candidates[].content" type="object">
  The content of the current conversation with the model. For single-turn queries, this is a single instance. For multi-turn queries, this is a repeated field that contains conversation history and the latest request.
</ResponseField>

<ResponseField name="candidates[].content.parts" type="object[]" required />

<ResponseField name="candidates[].content.parts[].fileData" type="object">
  URI based data.
</ResponseField>

<ResponseField name="candidates[].content.parts[].fileData.fileUri" type="string">
  URI
</ResponseField>

<ResponseField name="candidates[].content.parts[].fileData.mimeType" type="string">
  The media type of the file specified in the data or fileUri fields. Acceptable values include the following. For gemini-2.0-flash-lite and gemini-2.0-flash, the maximum length of an audio file is 8.4 hours and the maximum length of a video file (without audio) is one hour. For more information, see Gemini audio and video requirements. Text files must be UTF-8 encoded. The contents of the text file count toward the token limit. There is no limit on image resolution.

  Possible values: `application/pdf`, `audio/mpeg`, `audio/mp3`, `audio/wav`, `image/png`, `image/jpeg`, `image/webp`, `text/plain`, `video/mov`, `video/mpeg`, `video/mp4`, `video/mpg`, `video/avi`, `video/wmv`, `video/mpegps`, `video/flv`
</ResponseField>

<ResponseField name="candidates[].content.parts[].inlineData" type="object">
  Inline data in raw bytes. For gemini-2.0-flash-lite and gemini-2.0-flash, you can specify up to 3000 images by using inlineData.
</ResponseField>

<ResponseField name="candidates[].content.parts[].inlineData.data" type="string (byte)">
  The base64 encoding of the image, PDF, or video to include inline in the prompt. When including media inline, you must also specify the media type (mimeType) of the data. Size limit: 20MB

  Format: `byte`
</ResponseField>

<ResponseField name="candidates[].content.parts[].inlineData.mimeType" type="string">
  The media type of the file specified in the data or fileUri fields. Acceptable values include the following. For gemini-2.0-flash-lite and gemini-2.0-flash, the maximum length of an audio file is 8.4 hours and the maximum length of a video file (without audio) is one hour. For more information, see Gemini audio and video requirements. Text files must be UTF-8 encoded. The contents of the text file count toward the token limit. There is no limit on image resolution.

  Possible values: `application/pdf`, `audio/mpeg`, `audio/mp3`, `audio/wav`, `image/png`, `image/jpeg`, `image/webp`, `text/plain`, `video/mov`, `video/mpeg`, `video/mp4`, `video/mpg`, `video/avi`, `video/wmv`, `video/mpegps`, `video/flv`
</ResponseField>

<ResponseField name="candidates[].content.parts[].text" type="string">
  A text prompt or code snippet.
</ResponseField>

<ResponseField name="candidates[].content.parts[].thought" type="boolean">
  Indicates this part is a thinking/reasoning step from the model.
</ResponseField>

<ResponseField name="candidates[].content.role" type="string" required>
  Possible values: `user`, `model`
</ResponseField>

<ResponseField name="candidates[].finishReason" type="string" />

<ResponseField name="candidates[].safetyRatings" type="object[]" />

<ResponseField name="candidates[].safetyRatings[].category" type="string">
  Possible values: `HARM_CATEGORY_SEXUALLY_EXPLICIT`, `HARM_CATEGORY_HATE_SPEECH`, `HARM_CATEGORY_HARASSMENT`, `HARM_CATEGORY_DANGEROUS_CONTENT`
</ResponseField>

<ResponseField name="candidates[].safetyRatings[].probability" type="string">
  The probability that the content violates the specified safety category

  Possible values: `NEGLIGIBLE`, `LOW`, `MEDIUM`, `HIGH`, `UNKNOWN`
</ResponseField>

<ResponseField name="createTime" type="string">
  Timestamp when the response was created.
</ResponseField>

<ResponseField name="modelVersion" type="string">
  The model version used to generate the response.
</ResponseField>

<ResponseField name="promptFeedback" type="object" />

<ResponseField name="promptFeedback.blockReason" type="string" />

<ResponseField name="promptFeedback.blockReasonMessage" type="string" />

<ResponseField name="promptFeedback.safetyRatings" type="object[]" />

<ResponseField name="promptFeedback.safetyRatings[].category" type="string">
  Possible values: `HARM_CATEGORY_SEXUALLY_EXPLICIT`, `HARM_CATEGORY_HATE_SPEECH`, `HARM_CATEGORY_HARASSMENT`, `HARM_CATEGORY_DANGEROUS_CONTENT`
</ResponseField>

<ResponseField name="promptFeedback.safetyRatings[].probability" type="string">
  The probability that the content violates the specified safety category

  Possible values: `NEGLIGIBLE`, `LOW`, `MEDIUM`, `HIGH`, `UNKNOWN`
</ResponseField>

<ResponseField name="responseId" type="string">
  Unique identifier for the response.
</ResponseField>

<ResponseField name="usageMetadata" type="object" />

<ResponseField name="usageMetadata.cachedContentTokenCount" type="integer">
  Output only. Number of tokens in the cached part in the input (the cached content).
</ResponseField>

<ResponseField name="usageMetadata.candidatesTokenCount" type="integer">
  Number of tokens in the response(s).
</ResponseField>

<ResponseField name="usageMetadata.candidatesTokensDetails" type="object[]">
  Breakdown of candidate tokens by modality.
</ResponseField>

<ResponseField name="usageMetadata.candidatesTokensDetails[].modality" type="string">
  Type of input or output content modality.

  Possible values: `MODALITY_UNSPECIFIED`, `TEXT`, `IMAGE`, `VIDEO`, `AUDIO`, `DOCUMENT`
</ResponseField>

<ResponseField name="usageMetadata.candidatesTokensDetails[].tokenCount" type="integer">
  Number of tokens for the given modality.
</ResponseField>

<ResponseField name="usageMetadata.promptTokenCount" type="integer">
  Number of tokens in the request. When cachedContent is set, this is still the total effective prompt size meaning this includes the number of tokens in the cached content.
</ResponseField>

<ResponseField name="usageMetadata.promptTokensDetails" type="object[]">
  Breakdown of prompt tokens by modality.
</ResponseField>

<ResponseField name="usageMetadata.promptTokensDetails[].modality" type="string">
  Type of input or output content modality.

  Possible values: `MODALITY_UNSPECIFIED`, `TEXT`, `IMAGE`, `VIDEO`, `AUDIO`, `DOCUMENT`
</ResponseField>

<ResponseField name="usageMetadata.promptTokensDetails[].tokenCount" type="integer">
  Number of tokens for the given modality.
</ResponseField>

<ResponseField name="usageMetadata.thoughtsTokenCount" type="integer">
  Number of tokens present in thoughts output.
</ResponseField>

<ResponseField name="usageMetadata.toolUsePromptTokenCount" type="integer">
  Number of tokens present in tool-use prompt(s).
</ResponseField>

<ResponseField name="usageMetadata.totalTokenCount" type="integer">
  Total number of tokens (prompt + candidates).
</ResponseField>

<ResponseField name="usageMetadata.trafficType" type="string">
  Traffic type used for the request (e.g., PROVISIONED\_THROUGHPUT).
</ResponseField>

## Examples

### Output

```json theme={null}
{
  "candidates": [
    {
      "content": {
        "parts": [
          {
            "inlineData": {
              "data": "PGJhc2U2ND4=",
              "mimeType": "image/png"
            }
          }
        ],
        "role": "model"
      },
      "finishReason": "STOP"
    }
  ],
  "modelVersion": "gemini-2.5-flash-image",
  "responseId": "7c6b5a49-3827-1605-f4e3-d2c1b0a99887",
  "usageMetadata": {
    "candidatesTokenCount": 1290,
    "promptTokenCount": 11,
    "totalTokenCount": 1301
  }
}
```

## Before you ship

The snippets above are the shortest working call. Three things are the same for every model and are documented once on the [Comfy Router headers](/development/comfy-router/headers) page: send an `Idempotency-Key` on every paid call and reuse it when you retry, expect the connection to be held up to Router's 10 minute deadline, and keep `X-Comfy-Request-Id` from every response. The SDKs do all three for you; the cURL tab does none of them. On failure, `X-Comfy-Error-Type` names the bucket, and a `422` means the body failed the model's schema and was never billed.

<CardGroup cols={3}>
  <Card title="Headers" icon="list" href="/development/comfy-router/headers">
    Authentication, idempotency, request IDs, error buckets, retry pacing, spend limits.
  </Card>

  <Card title="Quick Start" icon="rocket" href="/development/comfy-router/quickstart">
    Typed error handling in Python and TypeScript, reading the 422, walking the catalog.
  </Card>

  <Card title="Limitations" icon="triangle-exclamation" href="/development/comfy-router/limitations">
    What Router does not do today, and what to use instead.
  </Card>
</CardGroup>
