Migrating Chat Completion from Gemini to Sarvam

View as Markdown

This guide covers migrating Gemini text generation and chat calls to Sarvam. It doesn’t cover Gemini’s audio capabilities; see Migrating Text-to-Speech and Migrating Speech-to-Text for those.

In short: if your code already goes through the openai Python package pointed at Gemini’s OpenAI-compatible endpoint, this migration is a two-line change: swap the api_key and base_url, keep everything else. If you’re on the native google-genai SDK’s interactions.create(), messages replaces input + system_instruction, and the response reads back in the same choices[0].message.content shape either way.

Quick start

Base URL and auth header change the same way for every Gemini migration on the native SDK path, so they’re folded in here rather than in a separate page:

Gemini APISarvam
Base URLhttps://generativelanguage.googleapis.comhttps://api.sarvam.ai
Auth headerx-goog-api-key: <api_key>api-subscription-key: <api_key>
Auth failure status401403

With that, replace the generation call:

1from google import genai
2
3client = genai.Client(api_key="YOUR_GEMINI_API_KEY")
4
5interaction = client.interactions.create(
6 model="gemini-3.5-flash",
7 input="What's a good name for a coffee shop in Bengaluru?",
8 system_instruction="You are a helpful assistant.",
9 generation_config={
10 "temperature": 0.7,
11 "max_output_tokens": 200,
12 },
13)
14
15print(interaction.output_text)

If your code goes through the openai package instead, see the next section: it’s a separate, often simpler route for Chat Completion specifically.

If you’re using the OpenAI-compatible endpoint

Gemini exposes an OpenAI-compatible Chat Completions endpoint, and so does Sarvam. If your code already goes through the openai package, migrating is just a client config change:

1from openai import OpenAI
2
3client = OpenAI(
4 api_key="YOUR_GEMINI_API_KEY",
5 base_url="https://generativelanguage.googleapis.com/v1beta/openai/",
6)
7
8response = client.chat.completions.create(
9 model="gemini-3.5-flash",
10 messages=[
11 {"role": "system", "content": "You are a helpful assistant."},
12 {"role": "user", "content": "Hello!"},
13 ],
14)
15
16print(response.choices[0].message.content)

Sarvam accepts the same Authorization: Bearer <key> header the openai package sends by default, on every endpoint, not just Chat Completion. See Authentication for details.

Endpoint map

The tables below aren’t a checklist to apply field-by-field: Quick Start above already shows the whole call swapped over in one go. Use these tables as a reference for what changed and why, not as a sequence of edits to make yourself.

Gemini APISarvam
Chat / text generationPOST /v1beta/interactions (or /v1beta/openai/chat/completions)POST /v1/chat/completions
StreamingPOST /v1beta/interactions with stream: truePOST /v1/chat/completions with stream: true

Parameter concept map

Gemini APISarvamNote
input (a string, or a list of prior turns)messages (a list of {"role", "content"} objects)Same shape the OpenAI-compatible path uses
system_instruction (separate top-level field)System entry in messagesFolded into the message list instead of a separate field
model (gemini-3.5-flash, gemini-3-pro, etc.)model (sarvam-30b or sarvam-105b)Two models on cost/latency vs. quality, not a wider family of tiers
generation_config.temperature/top_p/max_output_tokens/stop_sequencestemperature, top_p, max_tokens, stopSame concepts and ranges; Sarvam’s are top-level call params, not nested under a config object
previous_interaction_id (chain to a stored prior interaction instead of resending history)Resend the full messages list each callMatches the widely-used OpenAI Chat Completions convention, so existing message-history logic carries over unchanged
interaction.output_textresponse.choices[0].message.contentOne direct string vs. the OpenAI Chat Completions shape

What’s different by design

  • One message list, not a split input/system_instruction. The system prompt is just another entry in messages, so the whole conversation, including instructions, lives in one place.
  • No interaction ID to track. Sarvam follows the stateless, resend-the-history convention used by OpenAI-compatible tooling broadly, rather than Gemini’s previous_interaction_id chaining; if your app already manages its own message history, it carries over as-is.
  • A response shape you may already know. response.choices[0].message.content matches the OpenAI Chat Completions shape exactly, since Sarvam’s endpoint is OpenAI-compatible; there’s no separate native shape to learn if you’re coming from the OpenAI-compatible Gemini path.
  • Hybrid thinking mode built into the model. sarvam-30b and sarvam-105b support both “think” and “non-think” modes via reasoning_effort, tuned for complex reasoning and coding without needing a separate reasoning-specific model.
  • Advanced Indic skills are Sarvam’s specific strength. Both models are post-trained on Indian languages and cultural context, with native script and romanized-language support, alongside strong English performance.

Full example

A small wrapper function, before and after, showing the complete set of changes together:

1from google import genai
2
3client = genai.Client(api_key="YOUR_GEMINI_API_KEY")
4
5def ask(prompt: str, system: str = "You are a helpful assistant.") -> str:
6 interaction = client.interactions.create(
7 model="gemini-3.5-flash",
8 input=prompt,
9 system_instruction=system,
10 )
11 return interaction.output_text

Common mistakes

Sarvam (and the OpenAI-compatible shape generally) expects the system prompt as a {"role": "system", ...} entry at the start of messages, not a separate top-level field. Fold it into the message list rather than looking for a system_instruction parameter.

Sarvam’s response doesn’t have an output_text shortcut. Read the reply from response.choices[0].message.content, matching the OpenAI Chat Completions shape.

There isn’t one, and that’s fine: resend the conversation so far as the messages list on every call, the same way OpenAI-compatible tooling generally works. There’s no separate interaction resource to reference or manage the lifecycle of.

If you’re using the openai package against Sarvam’s endpoint, pass the key as api_key on the OpenAI(...) client as usual; don’t also try to set the api-subscription-key header manually; the two aren’t meant to be combined on the same request.

When reasoning_effort is used, reasoning tokens count against the same max_tokens budget as the visible reply. A limit copied over from a non-reasoning Gemini setup may cut off responses; raise it if you turn reasoning on.

See also