Gemini
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 applies to every field.
Set up the SDK#
The SDKs send the key in x-goog-api-key. A ?key= query parameter also works.
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."}]}]
}'Install the SDK with pip install google-genai or npm install @google/genai. Model ids follow GET /v1/models; 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.
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."}]}]}'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.
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.
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)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.
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"}]}]}'Use the Gemini CLI#
The Gemini CLI reads its endpoint and key from the environment:
export GOOGLE_GEMINI_BASE_URL=https://lovelyrouter.com
export GEMINI_API_KEY="$LOVELY_API_KEY"
gemini -m gemini-3.1-proUse with your tools 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.
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 when your key is not bound to one. |
x-session-id | Optional. Groups requests into one conversation for cache-aware routing. |
Errors use Gemini's shape, {"error": {"code": 429, "message": "...", "status": "RESOURCE_EXHAUSTED"}}; see Errors.