---
title: Gemini
description: Call Gemini models through the native Gemini API with the Google Gen AI SDKs or the Gemini CLI. Covers generateContent, streaming with alt=sse, countTokens and thinking.
---

Lovely Router serves the Gemini API's `generateContent`, `streamGenerateContent` and `countTokens` methods under `/v1beta/models/{model}`. Point the Google Gen AI SDK at `https://lovelyrouter.com` and pass your Lovely Router key as the API key. Request bodies pass through unchanged, so [Google's API reference](https://ai.google.dev/api/generate-content) applies to every field.

## Set up the SDK

The SDKs send the key in `x-goog-api-key`. A `?key=` query parameter also works.

```bash title="cURL"
curl "https://lovelyrouter.com/v1beta/models/gemini-3.1-pro:generateContent" \
  -H "x-goog-api-key: $LOVELY_API_KEY" \
  -H "content-type: application/json" \
  -d '{
    "contents": [{"role": "user", "parts": [{"text": "Explain DNS in two sentences."}]}]
  }'
```

```python title="Python"
import os
from google import genai
from google.genai import types

client = genai.Client(
    api_key=os.environ["LOVELY_API_KEY"],
    http_options=types.HttpOptions(base_url="https://lovelyrouter.com"),
)
response = client.models.generate_content(
    model="gemini-3.1-pro",
    contents="Explain DNS in two sentences.",
)
print(response.text)
```

```js title="Node.js"
import { GoogleGenAI } from '@google/genai';

const ai = new GoogleGenAI({
  apiKey: process.env.LOVELY_API_KEY,
  httpOptions: { baseUrl: 'https://lovelyrouter.com' },
});
const response = await ai.models.generateContent({
  model: 'gemini-3.1-pro',
  contents: 'Explain DNS in two sentences.',
});
console.log(response.text);
```

Install the SDK with `pip install google-genai` or `npm install @google/genai`. Model ids follow [`GET /v1/models`](/docs/models/pricing); Google's spellings such as `gemini-3.1-pro-preview` resolve to the same model. `GET /v1beta/models` lists every model available in this format, in Gemini's own response shape.

## Stream responses

Call `:streamGenerateContent?alt=sse` to get server-sent events. Each event is a partial `GenerateContentResponse`; the last one carries the final `usageMetadata`. The SDKs add `alt=sse` for you.

```bash title="cURL"
curl -N "https://lovelyrouter.com/v1beta/models/gemini-3-flash:streamGenerateContent?alt=sse" \
  -H "x-goog-api-key: $LOVELY_API_KEY" \
  -H "content-type: application/json" \
  -d '{"contents": [{"role": "user", "parts": [{"text": "Count to ten, slowly."}]}]}'
```

```python title="Python"
for chunk in client.models.generate_content_stream(
    model="gemini-3-flash",
    contents="Count to ten, slowly.",
):
    print(chunk.text or "", end="", flush=True)
```

```js title="Node.js"
const stream = await ai.models.generateContentStream({
  model: 'gemini-3-flash',
  contents: 'Count to ten, slowly.',
});
for await (const chunk of stream) {
  process.stdout.write(chunk.text ?? '');
}
```

Without `alt=sse`, the method streams one JSON array. That works too, but SSE is easier to parse and lets the gateway send a clean error event if the stream stalls. See [Streaming](/docs/models/streaming).

## Set a system instruction and thinking

`systemInstruction`, `generationConfig`, `safetySettings` and `tools` (function declarations) pass through as Google documents them. Gemini 3 models think by default; adjust it with `thinkingConfig` as described in [Google's thinking guide](https://ai.google.dev/gemini-api/docs/thinking).

```python title="Python"
response = client.models.generate_content(
    model="gemini-3.1-pro",
    contents="Plan a three-day trip to Kyoto.",
    config=types.GenerateContentConfig(
        system_instruction="You are a concise travel planner.",
        max_output_tokens=4096,
    ),
)
print(response.text)
print(response.usage_metadata)
```

```js title="Node.js"
const response = await ai.models.generateContent({
  model: 'gemini-3.1-pro',
  contents: 'Plan a three-day trip to Kyoto.',
  config: {
    systemInstruction: 'You are a concise travel planner.',
    maxOutputTokens: 4096,
  },
});
console.log(response.text);
console.log(response.usageMetadata);
```

Thinking tokens are reported in `usageMetadata.thoughtsTokenCount` and billed at the output rate, together with `candidatesTokenCount`. Thought summaries come back as parts with `"thought": true` only when the request sets `generationConfig.thinkingConfig.includeThoughts: true`, as in Google's API; without it the answer holds no thought parts.

## Count tokens

`:countTokens` takes the same `contents` as a generate call and returns `totalTokens`. It is free and works with a zero balance. The count is approximate: use it to check size against the context window, not to predict the exact charge.

```bash title="cURL"
curl "https://lovelyrouter.com/v1beta/models/gemini-3.1-pro:countTokens" \
  -H "x-goog-api-key: $LOVELY_API_KEY" \
  -H "content-type: application/json" \
  -d '{"contents": [{"role": "user", "parts": [{"text": "Hello, Gemini"}]}]}'
```

```python title="Python"
result = client.models.count_tokens(model="gemini-3.1-pro", contents="Hello, Gemini")
print(result.total_tokens)
```

```js title="Node.js"
const result = await ai.models.countTokens({ model: 'gemini-3.1-pro', contents: 'Hello, Gemini' });
console.log(result.totalTokens);
```

## Use the Gemini CLI

The Gemini CLI reads its endpoint and key from the environment:

```bash
export GOOGLE_GEMINI_BASE_URL=https://lovelyrouter.com
export GEMINI_API_KEY="$LOVELY_API_KEY"
gemini -m gemini-3.1-pro
```

[Use with your tools](/docs/integrations) covers other clients.

## Caching

Gemini caches repeated prompt prefixes on its own (implicit caching). Cached tokens appear in `usageMetadata.cachedContentTokenCount` and are billed at the cache-read rate. The gateway keeps each conversation on the same channel so the cache stays warm; see [Prompt caching](/docs/models/caching#gemini).

## What is not supported

These Gemini API endpoints return `404`: `cachedContents` (explicit context caching), file uploads, `embedContent` and `batchEmbedContents`, batch mode, and the Live API.

## Headers

| Header | Notes |
| --- | --- |
| `x-goog-api-key` | Your Lovely Router key. `Authorization: Bearer` and `?key=` also work. |
| `x-lovely-group` | Optional. Pins the request to a [channel group](/docs/models/channel-groups) when your key is not bound to one. |
| `x-session-id` | Optional. Groups requests into one conversation for [cache-aware routing](/docs/models/caching#send-an-explicit-session-id). |

Errors use Gemini's shape, `{"error": {"code": 429, "message": "...", "status": "RESOURCE_EXHAUSTED"}}`; see [Errors](/docs/api/errors).
