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

# Google Gemini models

> Connect Google Gemini to ModuleX with your own API key, browse the available Gemini models, and understand how BYOK usage is billed.

export const MediaEmbed = ({id, type = 'screenshot', caption = '', ext, ratio = '16 / 9'}) => {
  const isVideo = type === 'video' || type === 'app_video';
  const resolvedExt = ext || (isVideo ? 'mp4' : type === 'screenshot' ? 'webp' : 'svg');
  const src = 'https://media.modulex.dev/' + id + '.' + resolvedExt;
  const [status, setStatus] = useState('loading');
  const [isDev, setIsDev] = useState(false);
  const [inView, setInView] = useState(false);
  const boxRef = useRef(null);
  useEffect(() => {
    if (typeof window === 'undefined') return;
    const h = window.location.hostname;
    setIsDev(h === 'localhost' || h === '127.0.0.1' || h.endsWith('.mintlify.app'));
  }, []);
  useEffect(() => {
    if (inView) return;
    if (typeof IntersectionObserver === 'undefined') {
      setInView(true);
      return;
    }
    const el = boxRef.current;
    if (!el) return;
    const io = new IntersectionObserver(entries => {
      if (entries.some(e => e.isIntersecting)) {
        setInView(true);
        io.disconnect();
      }
    }, {
      rootMargin: '300px'
    });
    io.observe(el);
    return () => io.disconnect();
  }, [inView]);
  if (status === 'missing') {
    if (!isDev) return null;
    return <div style={{
      display: 'flex',
      flexDirection: 'column',
      justifyContent: 'center',
      gap: '0.4rem',
      padding: '1rem 1.25rem',
      margin: '1.25rem 0',
      width: '100%',
      aspectRatio: ratio,
      boxSizing: 'border-box',
      border: '1px dashed rgba(128,128,128,0.45)',
      borderRadius: '0.75rem',
      background: 'rgba(128,128,128,0.06)',
      color: 'currentColor',
      fontSize: '0.85rem',
      lineHeight: 1.45
    }}>
        <div style={{
      display: 'flex',
      alignItems: 'center',
      gap: '0.5rem',
      opacity: 0.75
    }}>
          <span aria-hidden="true">🎬</span>
          <code style={{
      fontSize: '0.75rem'
    }}>{id}</code>
          <span style={{
      fontSize: '0.65rem',
      textTransform: 'uppercase',
      letterSpacing: '0.04em',
      padding: '0.1rem 0.4rem',
      borderRadius: '0.4rem',
      background: 'rgba(128,128,128,0.18)'
    }}>
            {type}
          </span>
        </div>
        <div style={{
      opacity: 0.9
    }}>{caption || 'Media not uploaded yet.'}</div>
        <div style={{
      fontSize: '0.7rem',
      opacity: 0.5
    }}>
          Upload to R2 as <code>{id}.{resolvedExt}</code> — preview only, hidden in production.
        </div>
      </div>;
  }
  const mediaStyle = {
    display: status === 'loaded' ? 'block' : 'none',
    width: '100%',
    height: 'auto',
    borderRadius: '0.75rem'
  };
  const media = isVideo ? <video src={inView ? src : undefined} autoPlay loop muted playsInline preload="metadata" onLoadedData={() => setStatus('loaded')} onError={() => setStatus('missing')} style={mediaStyle} /> : <img src={inView ? src : undefined} alt={caption} onLoad={() => setStatus('loaded')} onError={() => setStatus('missing')} style={mediaStyle} />;
  return <figure style={{
    margin: '1.25rem 0'
  }}>
      <div ref={boxRef} style={status === 'loaded' ? {
    width: '100%'
  } : {
    width: '100%',
    aspectRatio: ratio,
    borderRadius: '0.75rem',
    background: 'rgba(128,128,128,0.06)'
  }}>
        {media}
      </div>
      {status === 'loaded' && caption ? <figcaption style={{
    marginTop: '0.5rem',
    textAlign: 'center',
    fontSize: '0.85rem',
    opacity: 0.7
  }}>
          {caption}
        </figcaption> : null}
    </figure>;
};

Google Gemini is a [BYOK (bring-your-own-key)](/reference/glossary) LLM provider in ModuleX. You connect your own Google AI API key, then use Gemini models in the [LLM node](/workflow-builder/nodes/llm), the [Agent node](/workflow-builder/nodes/agent), [chat](/platform/chat/model-selection), and the [Assistant](/assistant/models-and-settings). Because Gemini is BYOK, Google bills you directly for token usage and ModuleX adds no credit charge on top — see [BYOK billing](#byok-billing) below.

For the full list of provider types and how managed versus BYOK selection works, see [LLM providers](/integrations/llm-providers/overview). If you would rather have ModuleX provision and meter the model for you in credits, use [ModuleX-managed models](/integrations/llm-providers/modulexai) instead.

<MediaEmbed id="MX-MEDIA-4080" type="screenshot" caption={"The Google Gemini connection card in the ModuleX integrations/credentials UI."} />

## What you need

<Steps>
  <Step title="A Google AI API key">
    Create a key in [Google AI Studio](https://aistudio.google.com/app/apikey): click **Create API Key**, select (or create) a Google Cloud project, then copy the key. Google AI keys start with `AIza` (for example `AIzaSy…`). ModuleX validates the key against the pattern `^AIza[0-9A-Za-z\-_]{35}$` before saving it.
  </Step>

  <Step title="A ModuleX organization and an owner/admin role">
    Credentials are scoped to an [organization](/concepts/organizations-roles). Connecting and managing integrations requires the **owner** or **admin** role — the `member` role is retired. See [roles & permissions](/security/roles-permissions).
  </Step>

  <Step title="A ModuleX API key (for programmatic setup)">
    To connect Gemini over the API or an SDK, you need a ModuleX API key (`mx_live_…`) and your organization id. See [authentication](/api-reference/authentication).
  </Step>
</Steps>

## Connect Gemini

You connect Gemini by storing your Google AI API key as a [credential](/integrations/managing-credentials) for the `gemini` integration. You can do this in the app or programmatically.

### In the app

1. Open the [integrations catalog](/integrations/catalog) and select **Gemini**, or open **Settings → Credentials**.
2. Choose **API Key Authentication** and paste your Google AI key.
3. Optionally set a custom **Base URL** (defaults to `https://generativelanguage.googleapis.com/v1beta`) — only needed for Google AI-compatible gateways.
4. Save. ModuleX validates the key with a minimal request before storing it (see [Test a key before saving](#test-a-key-before-saving)).

Gemini exposes a single authentication scheme — `api_key`. Its fields are:

<ParamField path="api_key" type="string" required>
  Your Google AI API key. Stored encrypted and never returned in plaintext. Marked `sensitive`. Pattern `^AIza[0-9A-Za-z\-_]{35}$`; sample format `AIzaSyXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX`.
</ParamField>

<ParamField path="base_url" type="string" default="https://generativelanguage.googleapis.com/v1beta">
  Optional custom API base URL, only needed when pointing at a Google AI-compatible API. Not required and not sensitive. Must match `^https?://.*`.
</ParamField>

### Over the API or an SDK

Create the credential with `POST /credentials`. ModuleX detects the credential type from the body: an `auth_data.api_key` value is stored as an `api_key` credential. Set `make_default: true` to make this the default Gemini credential for the organization.

Every request authenticates with `Authorization: Bearer mx_live_…` plus the `X-Organization-ID` header (see [authentication](/api-reference/authentication)).

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://api.modulex.dev/credentials \
    -H "Authorization: Bearer mx_live_xxxxxxxxxxxxxxxxxxxxxxxx" \
    -H "X-Organization-ID: 6f1c2d3e-4a5b-6c7d-8e9f-0a1b2c3d4e5f" \
    -H "Content-Type: application/json" \
    -d '{
          "integration_name": "gemini",
          "display_name": "Gemini production key",
          "make_default": true,
          "auth_data": { "api_key": "AIzaSy..." }
        }'
  ```

  ```python Python theme={null}
  import asyncio
  from modulex import Modulex

  async def main():
      async with Modulex(
          api_key="mx_live_xxxxxxxxxxxxxxxxxxxxxxxx",   # or MODULEX_API_KEY
          organization_id="6f1c2d3e-4a5b-6c7d-8e9f-0a1b2c3d4e5f",
      ) as client:
          cred = await client.credentials.create(
              integration_name="gemini",
              display_name="Gemini production key",
              make_default=True,
              auth_data={"api_key": "AIzaSy..."},
          )
          print(cred.id)

  asyncio.run(main())
  ```

  ```javascript JavaScript theme={null}
  import { Modulex } from "modulex-js";

  const client = new Modulex({
    apiKey: "mx_live_xxxxxxxxxxxxxxxxxxxxxxxx",         // or process.env.MODULEX_API_KEY
    organizationId: "6f1c2d3e-4a5b-6c7d-8e9f-0a1b2c3d4e5f",
  });

  const cred = await client.credentials.create({
    integration_name: "gemini",
    display_name: "Gemini production key",
    make_default: true,
    auth_data: { api_key: "AIzaSy..." },
  });
  console.log(cred.id);
  ```
</CodeGroup>

<ResponseField name="id" type="string">
  The new credential's identifier. A `201` response returns the credential metadata; the secret is never echoed back.
</ResponseField>

<Note>
  Creating, listing, and managing credentials and the integration catalog requires the **owner** or **admin** role in the organization. A caller without that role receives a `403`. The integration-catalog read routes (for example `GET /integrations/llm-providers/gemini`) are also owner/admin-gated.
</Note>

### Test a key before saving

Use `POST /credentials/test-temporary` to validate a Gemini key without storing it. ModuleX runs the provider's test call — a 10-token `POST` to `https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash-lite:generateContent` with the header `x-goog-api-key` set to your key — and reports success when the response status is `200` and includes a `candidates` field.

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://api.modulex.dev/credentials/test-temporary \
    -H "Authorization: Bearer mx_live_xxxxxxxxxxxxxxxxxxxxxxxx" \
    -H "X-Organization-ID: 6f1c2d3e-4a5b-6c7d-8e9f-0a1b2c3d4e5f" \
    -H "Content-Type: application/json" \
    -d '{
          "integration_name": "gemini",
          "auth_type": "api_key",
          "auth_data": { "api_key": "AIzaSy..." }
        }'
  ```

  ```python Python theme={null}
  result = await client.credentials.test_temporary(
      integration_name="gemini",
      auth_type="api_key",
      auth_data={"api_key": "AIzaSy..."},
  )
  print(result.is_valid, result.status_code)
  ```

  ```javascript JavaScript theme={null}
  const result = await client.credentials.testTemporary({
    integration_name: "gemini",
    auth_type: "api_key",
    auth_data: { api_key: "AIzaSy..." },
  });
  console.log(result.is_valid, result.status_code);
  ```
</CodeGroup>

<ResponseField name="is_valid" type="boolean">
  `true` when the test call succeeds.
</ResponseField>

<ResponseField name="status_code" type="integer">
  The HTTP status returned by Google's test endpoint (`200` on success).
</ResponseField>

<ResponseField name="test_method" type="string">
  How the credential was validated. For Gemini this is `api_call`.
</ResponseField>

<ResponseField name="cost_level" type="string">
  The cost class of the test call. For Gemini this is `minimal` (a 10-token request).
</ResponseField>

## Available models

ModuleX serves Gemini models from the catalog at `GET /integrations/llm-providers/gemini`. The catalog is read-only metadata used by the credential UI and the builder — it is not the execution path. The models below are the ones ModuleX currently advertises for Gemini. Every Gemini model supports vision (image input) and a 1,048,576-token context window.

| Model id                | Display name          | Max input tokens | Max output tokens | Vision | Input \$/1M | Output \$/1M |
| ----------------------- | --------------------- | ---------------- | ----------------- | ------ | ----------- | ------------ |
| `gemini-2.5-pro`        | Gemini 2.5 Pro        | 1,048,576        | 65,536            | Yes    | `$2.50`     | `$15.00`     |
| `gemini-2.5-flash`      | Gemini 2.5 Flash      | 1,048,576        | 65,536            | Yes    | `$0.30`     | `$2.50`      |
| `gemini-2.5-flash-lite` | Gemini 2.5 Flash Lite | 1,048,576        | 65,536            | Yes    | `$0.10`     | `$0.40`      |

<Note>
  Prices in the table are **Google's per-token rates** as recorded in the ModuleX catalog, in USD per 1,000,000 tokens. Because Gemini is BYOK, these are the rates Google charges you directly — they are not ModuleX credits. See [BYOK billing](#byok-billing).
</Note>

Each model id is the catalog id you select in the app and in node configuration. ModuleX maps the catalog id to the underlying Gemini wire model and request settings:

<Expandable title="Gemini 2.5 Pro — full catalog fields">
  <ResponseField name="id" type="string">`gemini-2.5-pro` — the catalog id you select.</ResponseField>
  <ResponseField name="display_name" type="string">`Gemini 2.5 Pro`</ResponseField>
  <ResponseField name="provider" type="string">`Gemini`</ResponseField>
  <ResponseField name="provider_id" type="string">`gemini` — the routing provider slug.</ResponseField>
  <ResponseField name="max_input_tokens" type="integer">`1048576`</ResponseField>
  <ResponseField name="max_output_tokens" type="integer">`65536`</ResponseField>
  <ResponseField name="supports_vision" type="boolean">`true`</ResponseField>
  <ResponseField name="intelligence" type="integer">`5` (quality score, 1–5).</ResponseField>
  <ResponseField name="speed" type="integer">`2` (latency score, 1–5; higher is faster).</ResponseField>
  <ResponseField name="data_freshness" type="string">`2025-01-01` (knowledge cutoff).</ResponseField>
  <ResponseField name="input_usd_per_1m_tokens" type="number">`2.5`</ResponseField>
  <ResponseField name="output_usd_per_1m_tokens" type="number">`15.0`</ResponseField>

  <ResponseField name="wire model" type="string">
    ModuleX sends the wire model `gemini-2.5-pro` (the `body_extra.model` value). No extra headers are set (`headers_extra` is empty).
  </ResponseField>
</Expandable>

<Expandable title="Gemini 2.5 Flash — full catalog fields">
  <ResponseField name="id" type="string">`gemini-2.5-flash`</ResponseField>
  <ResponseField name="display_name" type="string">`Gemini 2.5 Flash`</ResponseField>
  <ResponseField name="provider_id" type="string">`gemini`</ResponseField>
  <ResponseField name="max_input_tokens" type="integer">`1048576`</ResponseField>
  <ResponseField name="max_output_tokens" type="integer">`65536`</ResponseField>
  <ResponseField name="supports_vision" type="boolean">`true`</ResponseField>
  <ResponseField name="intelligence" type="integer">`3`</ResponseField>
  <ResponseField name="speed" type="integer">`3`</ResponseField>
  <ResponseField name="data_freshness" type="string">`2025-01-01`</ResponseField>
  <ResponseField name="input_usd_per_1m_tokens" type="number">`0.3`</ResponseField>
  <ResponseField name="output_usd_per_1m_tokens" type="number">`2.5`</ResponseField>

  <ResponseField name="wire model" type="string">
    Wire model `gemini-2.5-flash` (the `body_extra.model` value); no extra headers.
  </ResponseField>
</Expandable>

<Expandable title="Gemini 2.5 Flash Lite — full catalog fields">
  <ResponseField name="id" type="string">`gemini-2.5-flash-lite`</ResponseField>
  <ResponseField name="display_name" type="string">`Gemini 2.5 Flash Lite`</ResponseField>
  <ResponseField name="provider_id" type="string">`gemini`</ResponseField>
  <ResponseField name="max_input_tokens" type="integer">`1048576`</ResponseField>
  <ResponseField name="max_output_tokens" type="integer">`65536`</ResponseField>
  <ResponseField name="supports_vision" type="boolean">`true`</ResponseField>
  <ResponseField name="intelligence" type="integer">`2`</ResponseField>
  <ResponseField name="speed" type="integer">`4`</ResponseField>
  <ResponseField name="data_freshness" type="string">`2025-01-01`</ResponseField>
  <ResponseField name="input_usd_per_1m_tokens" type="number">`0.1`</ResponseField>
  <ResponseField name="output_usd_per_1m_tokens" type="number">`0.4`</ResponseField>

  <ResponseField name="wire model" type="string">
    Wire model `gemini-2.5-flash-lite` (the `body_extra.model` value); no extra headers.
  </ResponseField>
</Expandable>

<Note>
  The catalog `max_output_tokens` is the model's hard ceiling. Separately, ModuleX applies a per-scenario output cap that is **lower** than the model ceiling and is **not** exposed through any catalog API response — for every Gemini model it is `8000` for simple chat, `10000` for chat with knowledge, `4000` for an LLM node, `12000` for an Agent node, `32000` for the main Composer agent, and `4000` for a Composer subagent. These caps are internal defaults and may change.
</Note>

<Note>
  The model list is served from the ModuleX catalog and may change as Google releases or deprecates models. Always read `GET /integrations/llm-providers/gemini` for the current set rather than hard-coding ids. Catalog reads are cached for up to 10 minutes.
</Note>

### Read the model catalog

<CodeGroup>
  ```bash cURL theme={null}
  curl https://api.modulex.dev/integrations/llm-providers/gemini \
    -H "Authorization: Bearer mx_live_xxxxxxxxxxxxxxxxxxxxxxxx" \
    -H "X-Organization-ID: 6f1c2d3e-4a5b-6c7d-8e9f-0a1b2c3d4e5f"
  ```

  ```python Python theme={null}
  detail = await client.integrations.llm_provider_detail("gemini")
  for model in detail.models:
      print(model["id"], model["display_name"])
  ```

  ```javascript JavaScript theme={null}
  const detail = await client.integrations.llmProvider("gemini");
  for (const model of detail.models) {
    console.log(model.id, model.display_name);
  }
  ```
</CodeGroup>

The response is an integration-detail object whose `models` array contains the entries shown above, plus an `auth_schemas` array describing the `api_key` scheme. A truncated example:

```json Catalog detail (truncated) theme={null}
{
  "name": "gemini",
  "display_name": "Gemini",
  "description": "Google's Gemini models for advanced reasoning, multimodal tasks, and long-context processing",
  "integration_type": "llm_provider",
  "version": "1.0.0",
  "categories": ["AI & LLM Providers", "ai", "chat", "reasoning", "multimodal"],
  "auth_schemas": [
    {
      "auth_type": "api_key",
      "display_name": "API Key Authentication",
      "description": "Connect using Google AI API Key"
    }
  ],
  "models": [
    {
      "id": "gemini-2.5-pro",
      "display_name": "Gemini 2.5 Pro",
      "provider_id": "gemini",
      "max_input_tokens": 1048576,
      "max_output_tokens": 65536,
      "supports_vision": true,
      "input_usd_per_1m_tokens": 2.5,
      "output_usd_per_1m_tokens": 15.0
    }
  ]
}
```

## Use Gemini in ModuleX

Once Gemini is connected, select a Gemini model id wherever a model can be chosen:

<CardGroup cols={2}>
  <Card title="LLM node" icon="message-square" href="/workflow-builder/nodes/llm">
    Call Gemini with prompts, variables, and structured output inside a workflow.
  </Card>

  <Card title="Agent node" icon="bot" href="/workflow-builder/nodes/agent">
    Run an autonomous step where Gemini can call tools and loop.
  </Card>

  <Card title="Chat" icon="messages-square" href="/platform/chat/model-selection">
    Pick Gemini as the model for a chat conversation.
  </Card>

  <Card title="Assistant" icon="sparkles" href="/assistant/models-and-settings">
    Configure the Assistant to reason and act with a Gemini model.
  </Card>
</CardGroup>

If your organization holds more than one Gemini credential, ModuleX uses the credential marked default for the `gemini` integration. Set a different default with `POST /credentials/{credential_id}/set-default`. See [managing credentials](/integrations/managing-credentials).

## BYOK billing

Gemini is **bring-your-own-key**, which changes how usage is billed compared with [ModuleX-managed models](/integrations/llm-providers/modulexai).

<CardGroup cols={2}>
  <Card title="BYOK (Gemini)" icon="key">
    Token usage is billed by **Google** against your own API key. ModuleX adds **no credit charge and no markup** for the model call itself. BYOK usage is recorded for analytics only — it does not consume ModuleX [credits](/billing/credits).
  </Card>

  <Card title="Managed (modulexai)" icon="gauge">
    ModuleX provisions the model and bills it in **credits** through the [usage gate](/billing/usage-gating), applying a system margin. Use this when you do not want to manage a provider account.
  </Card>
</CardGroup>

What this means in practice:

* **No ModuleX credit cost for tokens.** A Gemini call made with your Google AI key is not metered in credits. Google invoices you directly at its own rates (the per-million-token prices in the [model table](#available-models)).
* **Other ModuleX usage still applies.** Running a workflow or a turn through the Assistant can still consume the flat per-run credit and may pass through the billing gate even when the model itself is BYOK. See [credits & metering](/billing/credits) and [usage gating & limits](/billing/usage-gating).
* **Rate limits and quotas are Google's.** Token-rate limits, monthly spend caps, and model availability for a BYOK key are governed by your Google AI account, not by ModuleX.

<Note>
  BYOK is available on all plans — there is no separate ModuleX entitlement that gates connecting your own Gemini key. The pricing matrix on the marketing site lists BYOK for every tier; the only ModuleX-side billing that applies to a BYOK call is the non-model usage described above. **TBD:** confirm with the billing owner that no per-call ModuleX surcharge applies to BYOK model calls before treating "no markup" as a guarantee.
</Note>

## Errors

Catalog and credential operations return ModuleX's standard error shapes. See [errors & status codes](/api-reference/errors) for the full taxonomy.

| Status | When it happens                                                                                                                                       | Shape               |
| ------ | ----------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------- |
| `400`  | Invalid `auth_data`/`auth_type` on credential create, or asking a typed endpoint for the wrong integration type (for example `gemini` is not a tool). | `{detail: string}`  |
| `401`  | Missing or invalid `Authorization` / missing `X-Organization-ID`.                                                                                     | `{detail: string}`  |
| `403`  | Caller is not an owner/admin in the organization.                                                                                                     | `{detail: string}`  |
| `404`  | Unknown provider, for example `GET /integrations/llm-providers/<unknown>` returns `LLM provider not found`.                                           | `{detail: string}`  |
| `422`  | Query/parameter validation error.                                                                                                                     | `{detail: [ ... ]}` |
| `500`  | Unhandled server error.                                                                                                                               | `{detail: string}`  |

<Warning>
  A bad Gemini key does not surface as a ModuleX `401`. Connection validation happens against Google: `POST /credentials/test-temporary` (or saving with validation) returns `is_valid: false` with the status Google returned (for example `400` from `generativelanguage.googleapis.com` for an invalid key). The ModuleX request itself still succeeds.
</Warning>

## Related

<CardGroup cols={2}>
  <Card title="LLM providers" icon="layers" href="/integrations/llm-providers/overview">
    All providers, and how managed vs BYOK selection works.
  </Card>

  <Card title="ModuleX-managed models" icon="gauge" href="/integrations/llm-providers/modulexai">
    The credit-billed managed alternative to BYOK.
  </Card>

  <Card title="Authentication & credentials" icon="lock" href="/integrations/authentication">
    How integrations authenticate and the auth schema variants.
  </Card>

  <Card title="Managing credentials" icon="key-round" href="/integrations/managing-credentials">
    Create, default, rotate, and scope credentials.
  </Card>
</CardGroup>
