> For clean Markdown of any page, append `.md` to the page URL. > For a complete documentation index, see https://docs.sarvam.ai/llms.txt. > For full documentation content in one file, see https://docs.sarvam.ai/llms-full.txt. > For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.sarvam.ai/_mcp/server. # Migrating Chat Completion from Gemini to Sarvam > Endpoint, parameter, and response-format differences between the Gemini API and Sarvam's Chat Completion API, with before/after code for both the OpenAI-compatible endpoint and each platform's native SDK. *Last updated: July 15, 2026* 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](/api/migrations/from-gemini/text-to-speech) and [Migrating Speech-to-Text](/api/migrations/from-gemini/speech-to-text) for those. > **Tip** > > **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 API | Sarvam | | ------------------- | ------------------------------------------- | --------------------------------- | | Base URL | `https://generativelanguage.googleapis.com` | `https://api.sarvam.ai` | | Auth header | `x-goog-api-key: ` | `api-subscription-key: ` | | Auth failure status | `401` | `403` | With that, replace the generation call: #### Before (Gemini) #### Python ```python from google import genai client = genai.Client(api_key="YOUR_GEMINI_API_KEY") interaction = client.interactions.create( model="gemini-3.5-flash", input="What's a good name for a coffee shop in Bengaluru?", system_instruction="You are a helpful assistant.", generation_config={ "temperature": 0.7, "max_output_tokens": 200, }, ) print(interaction.output_text) ``` #### JavaScript ```javascript import { GoogleGenAI } from "@google/genai"; const ai = new GoogleGenAI({ apiKey: "YOUR_GEMINI_API_KEY" }); const interaction = await ai.interactions.create({ model: "gemini-3.5-flash", input: "What's a good name for a coffee shop in Bengaluru?", system_instruction: "You are a helpful assistant.", generation_config: { temperature: 0.7, max_output_tokens: 200 }, }); console.log(interaction.output_text); ``` #### cURL ```bash curl -X POST "https://generativelanguage.googleapis.com/v1beta/interactions" \ -H "x-goog-api-key: YOUR_GEMINI_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gemini-3.5-flash", "input": "What'\''s a good name for a coffee shop in Bengaluru?", "system_instruction": "You are a helpful assistant." }' ``` #### After (Sarvam) #### Python ```python from sarvamai import SarvamAI client = SarvamAI(api_subscription_key="YOUR_SARVAM_API_KEY") response = client.chat.completions( model="sarvam-105b", messages=[ {"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": "What's a good name for a coffee shop in Bengaluru?"}, ], temperature=0.7, max_tokens=200, ) print(response.choices[0].message.content) ``` #### JavaScript ```javascript import { SarvamAIClient } from "sarvamai"; const client = new SarvamAIClient({ apiSubscriptionKey: "YOUR_SARVAM_API_KEY" }); const response = await client.chat.completions({ model: "sarvam-105b", messages: [ { role: "system", content: "You are a helpful assistant." }, { role: "user", content: "What's a good name for a coffee shop in Bengaluru?" }, ], temperature: 0.7, max_tokens: 200, }); console.log(response.choices[0].message.content); ``` #### cURL ```bash curl -X POST https://api.sarvam.ai/v1/chat/completions \ -H "api-subscription-key: YOUR_SARVAM_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "sarvam-105b", "messages": [ {"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": "What'\''s a good name for a coffee shop in Bengaluru?"} ] }' ``` 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: #### Before (Gemini) ```python from openai import OpenAI client = OpenAI( api_key="YOUR_GEMINI_API_KEY", base_url="https://generativelanguage.googleapis.com/v1beta/openai/", ) response = client.chat.completions.create( model="gemini-3.5-flash", messages=[ {"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": "Hello!"}, ], ) print(response.choices[0].message.content) ``` #### After (Sarvam) ```python from openai import OpenAI client = OpenAI( api_key="YOUR_SARVAM_API_KEY", base_url="https://api.sarvam.ai/v1", ) response = client.chat.completions.create( model="sarvam-105b", messages=[ {"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": "Hello!"}, ], ) print(response.choices[0].message.content) ``` > **Note** > > Sarvam accepts the same `Authorization: Bearer ` header the `openai` package sends by default, on every endpoint, not just Chat Completion. See [Authentication](/api-reference/authentication) for details. ## Endpoint map > **Tip** > > 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 API | Sarvam | | ---------------------- | ------------------------------------------------------------------ | ----------------------------------------------- | | Chat / text generation | `POST /v1beta/interactions` (or `/v1beta/openai/chat/completions`) | `POST /v1/chat/completions` | | Streaming | `POST /v1beta/interactions` with `stream: true` | `POST /v1/chat/completions` with `stream: true` | ## Parameter concept map | Gemini API | Sarvam | Note | | -------------------------------------------------------------------------------------------- | ---------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- | | `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 `messages` | Folded into the message list instead of a separate field | | `model` (`gemini-3.5-flash`, `gemini-3-pro`, etc.) | `model` (`sarvam-105b`) | A single flagship model, not a wider family of tiers | | `generation_config.temperature`/`top_p`/`max_output_tokens`/`stop_sequences` | `temperature`, `top_p`, `max_tokens`, `stop` | Same 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 call | Matches the widely-used OpenAI Chat Completions convention, so existing message-history logic carries over unchanged | | `interaction.output_text` | `response.choices[0].message.content` | One 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-105b` supports 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.** The model is 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: #### Before (Gemini) #### Python ```python from google import genai client = genai.Client(api_key="YOUR_GEMINI_API_KEY") def ask(prompt: str, system: str = "You are a helpful assistant.") -> str: interaction = client.interactions.create( model="gemini-3.5-flash", input=prompt, system_instruction=system, ) return interaction.output_text ``` #### JavaScript ```javascript import { GoogleGenAI } from "@google/genai"; const ai = new GoogleGenAI({ apiKey: "YOUR_GEMINI_API_KEY" }); async function ask(prompt, system = "You are a helpful assistant.") { const interaction = await ai.interactions.create({ model: "gemini-3.5-flash", input: prompt, system_instruction: system, }); return interaction.output_text; } ``` #### cURL ```bash ask() { local prompt="$1" system="${2:-You are a helpful assistant.}" curl -sS -X POST "https://generativelanguage.googleapis.com/v1beta/interactions" \ -H "x-goog-api-key: YOUR_GEMINI_API_KEY" \ -H "Content-Type: application/json" \ -d "{\"model\": \"gemini-3.5-flash\", \"input\": \"$prompt\", \"system_instruction\": \"$system\"}" } ``` #### After (Sarvam) #### Python ```python from sarvamai import SarvamAI from sarvamai.core.api_error import ApiError client = SarvamAI(api_subscription_key="YOUR_SARVAM_API_KEY") def ask(prompt: str, system: str = "You are a helpful assistant.") -> str: try: response = client.chat.completions( model="sarvam-105b", messages=[ {"role": "system", "content": system}, {"role": "user", "content": prompt}, ], ) except ApiError as e: if e.status_code == 403: raise RuntimeError("Invalid API key. Check your credentials.") from e elif e.status_code == 429: raise RuntimeError("Rate limit exceeded. Wait and retry.") from e elif e.status_code == 503: raise RuntimeError("Service overloaded. Retry with backoff.") from e raise return response.choices[0].message.content ``` #### JavaScript ```javascript import { SarvamAIClient, SarvamAIError } from "sarvamai"; const client = new SarvamAIClient({ apiSubscriptionKey: "YOUR_SARVAM_API_KEY" }); async function ask(prompt, system = "You are a helpful assistant.") { try { const response = await client.chat.completions({ model: "sarvam-105b", messages: [ { role: "system", content: system }, { role: "user", content: prompt }, ], }); return response.choices[0].message.content; } catch (e) { if (e instanceof SarvamAIError) { if (e.statusCode === 403) throw new Error("Invalid API key. Check your credentials."); if (e.statusCode === 429) throw new Error("Rate limit exceeded. Wait and retry."); if (e.statusCode === 503) throw new Error("Service overloaded. Retry with backoff."); } throw e; } } ``` #### cURL ```bash ask() { local prompt="$1" system="${2:-You are a helpful assistant.}" local body status body=$(curl -sS -w '\n%{http_code}' -X POST https://api.sarvam.ai/v1/chat/completions \ -H "api-subscription-key: YOUR_SARVAM_API_KEY" \ -H "Content-Type: application/json" \ -d "{\"model\": \"sarvam-105b\", \"messages\": [{\"role\": \"system\", \"content\": \"$system\"}, {\"role\": \"user\", \"content\": \"$prompt\"}]}") status="${body##*$'\n'}" case "$status" in 403) echo "Invalid API key. Check your credentials." >&2; return 1 ;; 429) echo "Rate limit exceeded. Wait and retry." >&2; return 1 ;; 503) echo "Service overloaded. Retry with backoff." >&2; return 1 ;; esac # Minimal single-value extraction; use a real JSON parser for production code. echo "${body%$'\n'*}" | grep -o '"content":"[^"]*"' | tail -1 | cut -d'"' -f4 } ``` ## Common mistakes #### Sending a system\_instruction field instead of a system message 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. #### Reading interaction.output\_text out of habit 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. #### Looking for a previous\_interaction\_id equivalent 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. #### Mixing native and OpenAI-compatible auth styles 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. #### Forgetting max\_tokens covers reasoning output too 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 * [Migration overview](/api/migrations/from-gemini/overview): base URL, auth, and SDK client changes * [Migrating Text-to-Speech](/api/migrations/from-gemini/text-to-speech) * [Migrating Speech-to-Text](/api/migrations/from-gemini/speech-to-text) * [Chat Completion Overview](/api/api-guides-tutorials/chat-completion/overview) * [Authentication](/api-reference/authentication) * [Errors & Troubleshooting](/api/getting-started/errors-troubleshooting) > Endpoint, parameter, and response-format differences between the Gemini API and Sarvam's Chat Completion API, with before/after code for both the OpenAI-compatible endpoint and each platform's native SDK.