---
title: "Gemini protocol"
description: "Models on pirouter that accept the Gemini protocol work with the Google GenAI SDK or REST, with request bodies written in Google's own format. Unlike calling Google directly, the key must go in the Authorization header; sending only x-goog-api-key returns 401."
canonical: "https://pirouter.ai/docs/api/gemini"
last-updated: "2026-10-17"
lang: "en"
source: "content/docs/en/api/gemini.mdx"
---

# Gemini protocol

Models on pirouter that accept the Gemini protocol work with the Google GenAI SDK or REST, with request bodies written in Google's own format. Unlike calling Google directly, the key must go in the Authorization header; sending only x-goog-api-key returns 401.

The Google GenAI SDK and Google's REST format work with pirouter after three changes: set the base URL to `https://api.pirouter.ai`, put your pirouter API key in the `Authorization: Bearer` header, and write the model ID in the path as `vendor/model`. Request bodies follow Google's own format.

## Call it over REST

The Gemini protocol has two endpoints:

| Endpoint                                            | Use                             |
| --------------------------------------------------- | ------------------------------- |
| `POST /v1beta/models/{model}:generateContent`       | Returns the whole reply at once |
| `POST /v1beta/models/{model}:streamGenerateContent` | Streams the reply               |

The model ID goes in the path as the full `vendor/model`, with the slash kept as is, for example `/v1beta/models/google/gemini-4-argon:generateContent`. pirouter does not add a vendor prefix for you.

cURL:

```bash
curl "https://api.pirouter.ai/v1beta/models/google/gemini-4-argon:generateContent" \
  -H "Authorization: Bearer $PIROUTER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "contents": [{ "parts": [{ "text": "Hello!" }] }]
  }'
```

Python:

```python
import os
from google import genai
from google.genai import types

client = genai.Client(
    api_key=os.environ["PIROUTER_API_KEY"],
    http_options=types.HttpOptions(
        base_url="https://api.pirouter.ai",
        headers={"Authorization": f"Bearer {os.environ['PIROUTER_API_KEY']}"},
    ),
)

response = client.models.generate_content(
    model="google/gemini-4-argon",
    contents="Hello!",
)
print(response.text)
```

TypeScript:

```ts
import { GoogleGenAI } from '@google/genai'

const ai = new GoogleGenAI({
  apiKey: process.env.PIROUTER_API_KEY,
  httpOptions: {
    baseUrl: 'https://api.pirouter.ai',
    headers: { Authorization: `Bearer ${process.env.PIROUTER_API_KEY}` },
  },
})

const response = await ai.models.generateContent({
  model: 'google/gemini-4-argon',
  contents: 'Hello!',
})
console.log(response.text)
```

On success it returns a JSON response with the reply in `candidates[0].content.parts[0].text`. The request shows up in the console's **Logs**.

To stream, replace `generateContent` with `streamGenerateContent`. The body stays the same and needs no `stream` field:

```bash
curl -N "https://api.pirouter.ai/v1beta/models/google/gemini-4-argon:streamGenerateContent" \
  -H "Authorization: Bearer $PIROUTER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "contents": [{ "parts": [{ "text": "Hello!" }] }]
  }'
```

The stream is a series of `data: ` events, each with part of the reply in `candidates`. The last event carries `finishReason` and `usageMetadata`, and no separate end marker follows. The response is this event stream with or without `?alt=sse`.

The body is forwarded to the provider as is, apart from routing fields such as `provider`, which are removed.

## Use the Google GenAI SDK

Change two things when you create the client:

1. Set the base URL to `https://api.pirouter.ai`. Do not set an API version; the SDK adds `/v1beta` by default.
2. Add an `Authorization: Bearer` header under `headers` in `http_options` (`httpOptions` in TypeScript). Pass the SDK's `api_key` as usual.

The Python and TypeScript tabs above are written this way.

## Sending only x-goog-api-key returns 401

pirouter reads neither `x-goog-api-key` nor the `?key=` query parameter, and removes both before forwarding. A request that follows Google's REST example and sends only `x-goog-api-key`:

```bash
curl "https://api.pirouter.ai/v1beta/models/google/gemini-4-argon:generateContent" \
  -H "x-goog-api-key: $PIROUTER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "contents": [{ "parts": [{ "text": "Hello!" }] }]
  }'
```

returns `401`:

```json
{
  "error": {
    "message": "You didn't provide an API key. Provide it in an Authorization header using Bearer auth (Authorization: Bearer YOUR_KEY).",
    "type": "invalid_request_error",
    "code": "invalid_api_key"
  }
}
```

Add `Authorization: Bearer` and the request goes through; the `x-goog-api-key` the SDK sends by default can stay. Which headers the gateway reads is in [Send the key with a request](https://pirouter.ai/docs/authentication.md#send-the-key-with-a-request).

## Which models you can use

The Google Gemini family accepts the Gemini protocol, including several image generation models. To check one model, look for `gemini` in the `protocols` of `GET /v1/models/{vendor}/{model}/endpoints`, or see each model's protocols in the [model list](https://pirouter.ai/models).

The Gemini protocol has no separate image endpoint: image models are called with `generateContent` too, and the generated image comes back in the reply's `inlineData`. See [Generate and edit images](https://pirouter.ai/docs/guides/images.md).

## How pirouter differs from Google

| Area                           | What pirouter does                                                                                                                                                                                               |
| ------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Authentication                 | `x-goog-api-key` and `?key=` are not read; see [Sending only x-goog-api-key returns 401](https://pirouter.ai/docs/api/gemini.md#sending-only-x-goog-api-key-returns-401)                                         |
| `modelVersion` in the response | Rewritten to the pirouter model ID (`vendor/model`). The name the provider reported is in the `X-Pi-Upstream-Model` response header                                                                              |
| The provider's request ID      | Some providers return no request ID, and then the response has no `X-Pi-Upstream-Request-Id`                                                                                                                     |
| Error body                     | pirouter's own errors are `{"error": {"message", "type", "code"}}`, with `code` a string error code; see [Errors](https://pirouter.ai/docs/api/errors.md). Errors the provider returns are passed on as they are |
| Failover                       | When a provider fails before sending its first byte, pirouter moves to the next provider; see [Failover](https://pirouter.ai/docs/guides/routing.md#failover)                                                    |

## Not available today

pirouter does not serve these Gemini methods today:

- `countTokens`. A call to `POST /v1beta/models/{model}:countTokens` returns `400` `invalid_request` with the `message` `model is required`, which means pirouter does not serve the method; see [`invalid_request`](https://pirouter.ai/docs/api/errors.md#invalid_request).
- `embedContent` and the other methods.

## Next steps

- [Generate content](https://pirouter.ai/docs/api/generate-content.md): Every parameter and response field of the generateContent endpoint.
- [Authentication & API keys](https://pirouter.ai/docs/authentication.md): Which header carries the key, and how to create and revoke keys.
