Short answer: You can call Sarvam's Indian-language text-to-speech and speech-to-text through the same OpenAI-style audio endpoints AICredits exposes: POST /v1/audio/speech with sarvam/bulbul-v2 or -v3, and POST /v1/audio/transcriptions with sarvam/saarika-v2. Below are the request shapes from the docs, a small Python client and a cost estimator you can adapt.
Disclosure: the aicredits.in links are affiliate links and I use the service. Everything here is taken from the AICredits docs as read on 16 Oct 2026. The code is untested, not run against the live API, and a couple of doc pages disagree with each other, which I flag where it matters.
If you build a voice feature for Indian users, general-purpose speech models are the default pick and the first thing to disappoint. Sarvam is an India-first provider, and the useful part for a developer is that AICredits routes to it with no separate Sarvam account: same sk- key, same INR wallet. If you want the model background first, read the Sarvam AI developer guide. For a cost comparison against Whisper and others from the catalog, see the speech API cost post.
What do you need before you start?
- An AICredits key. Minimum top-up is ₹50, credits last a year, and you can set an INR budget on the key. Setup is in the first-call walkthrough.
pip install openai requests- Base URL
https://api.aicredits.in/v1.
Audio requests count toward your key's requests-per-minute limit, and a wallet that runs out returns 402 (per the audio docs). Set a small budget cap on any key used for experiments.
How do you do Hindi text-to-speech with Sarvam?
The documented request is a JSON POST. This is the shape from the audio page:
curl https://api.aicredits.in/v1/audio/speech \
-H "Authorization: Bearer sk-your-key-here" \
-H "Content-Type: application/json" \
-d '{
"model": "sarvam/bulbul-v2",
"input": "नमस्ते, आप कैसे हैं?",
"voice": "anushka"
}' \
--output speech.wav
Sarvam voices are called speakers and carry Indian names. The voice field takes either a native speaker such as anushka, abhilash or priya, or an OpenAI alias that AICredits maps for you:
| OpenAI alias | Sarvam speaker |
|---|---|
alloy | anushka |
echo | abhilash |
fable | arya |
onyx | karun |
nova | priya |
shimmer | shreya |
The same call through the OpenAI SDK, with the language code passed as an extra body field as the LLM docs show:
from pathlib import Path
from openai import OpenAI
# Untested: adapted from the AICredits docs, not run against the live API.
client = OpenAI(
api_key="sk-your-aicredits-key",
base_url="https://api.aicredits.in/v1",
)
response = client.audio.speech.create(
model="sarvam/bulbul-v2",
voice="anushka",
input="नमस्ते, आप कैसे हैं?",
extra_body={"language": "hi-IN"},
)
Path("speech.wav").write_bytes(response.content)
Notes from the docs worth keeping in mind:
- The response is raw audio bytes. For Sarvam the docs say WAV, so save it as
.wav, not.mp3. - The shared TTS parameter table says
inputhas a maximum of 4096 characters and returns a 400 above that. It is written for the OpenAI models, and I did not find a separate Sarvam limit, so chunk long text on sentence boundaries either way. bulbul-v2andbulbul-v3are both routed. The audio page's intro mentions both, but its model table and the API reference list only v2, while the longer LLM docs add the v3 price. Try both on your key and keep the one that works.
How do you transcribe Hindi audio with Saarika?
Here the two doc pages disagree, so I will show both shapes and you can test which your key accepts.
Shape A, from the audio page: JSON with base64 audio.
import base64
import requests
# Untested: request shape copied from the AICredits audio docs.
API_KEY = "sk-your-aicredits-key"
def transcribe_json(path: str, language: str = "hi-IN") -> str:
with open(path, "rb") as f:
audio_b64 = base64.b64encode(f.read()).decode("ascii")
resp = requests.post(
"https://api.aicredits.in/v1/audio/transcriptions",
headers={"Authorization": f"Bearer {API_KEY}"},
json={
"model": "sarvam/saarika-v2",
"input_audio": {"data": audio_b64, "format": "wav"},
"language": language,
},
timeout=120,
)
resp.raise_for_status()
return resp.json()["text"]
The format field accepts mp3, mp4, mpeg, mpga, m4a, wav and webm. I am assuming the JSON response carries a text field because the OpenAI-style examples print .text; check the actual body on your first call.
Shape B, from the LLM docs: the standard OpenAI multipart form, which the SDK builds for you.
from openai import OpenAI
# Untested: adapted from the AICredits docs.
client = OpenAI(api_key="sk-your-aicredits-key", base_url="https://api.aicredits.in/v1")
with open("speech.wav", "rb") as f:
transcript = client.audio.transcriptions.create(
model="sarvam/saarika-v2.5",
file=("speech.wav", f, "audio/wav"),
language="hi-IN",
)
print(transcript.text)
That page lists saarika-v2.5 as the current model and says saarika-v2 is a legacy ID that routes to v2.5. If shape A fails, try shape B, and the other way round. Report the working combination in your own README so the next person does not repeat the experiment.
Which language codes can you use?
Sarvam models take BCP-47 codes. If you omit the code, the audio docs say it defaults to hi-IN.
| Code | Language | Code | Language |
|---|---|---|---|
hi-IN | Hindi | mr-IN | Marathi |
en-IN | English (India) | od-IN | Odia |
bn-IN | Bengali | pa-IN | Punjabi |
gu-IN | Gujarati | raj-IN | Rajasthani |
kn-IN | Kannada | ta-IN | Tamil |
ml-IN | Malayalam | te-IN | Telugu |
The longer LLM docs list all twelve. The audio page lists ten, without od-IN and raj-IN. Treat those two as "documented in one place" until you have tested them.
Always pass the code explicitly. A Tamil sentence sent without ta-IN falls back to Hindi settings, and you will blame the model for what is a missing parameter.
What does Sarvam speech cost?
Per AICredits docs on 16 Oct 2026, Sarvam is priced in INR directly, with no forex conversion:
| Service | Model | Documented price |
|---|---|---|
| TTS | Bulbul v2 | ₹15 per 10,000 characters |
| TTS | Bulbul v3 | ₹30 per 10,000 characters |
| STT | Saarika | ₹30 to ₹45 per hour, billed per second |
The STT range is stated as a range and I do not know what decides where in it a request lands. Use ₹45 for a conservative budget.
OpenAI's Whisper on the same gateway is billed in USD per minute ($0.006 per minute in the docs) and then converted: provider USD times live USD/INR times 1.05 forex buffer times 1.05 platform fee. That formula is why Sarvam rows have no 1.05 multipliers and Whisper rows do.
This estimator turns a project description into rupees. Rates are arguments, not constants, so update them when the docs change:
from dataclasses import dataclass
# Untested helper. Prices are the AICredits docs values dated 16 Oct 2026.
@dataclass
class Rates:
tts_inr_per_10k_chars: float = 15.0 # bulbul-v2; use 30.0 for v3
stt_inr_per_hour: float = 45.0 # top of the documented 30-45 range
whisper_usd_per_min: float = 0.006
usd_inr: float = 90.0 # ILLUSTRATIVE; use the live rate
forex_buffer: float = 1.05
platform_fee: float = 1.05
def tts_cost(chars: int, r: Rates) -> float:
return chars / 10_000 * r.tts_inr_per_10k_chars
def stt_cost(seconds: float, r: Rates) -> float:
return seconds / 3600 * r.stt_inr_per_hour
def whisper_cost(seconds: float, r: Rates) -> float:
usd = seconds / 60 * r.whisper_usd_per_min
return usd * r.usd_inr * r.forex_buffer * r.platform_fee
def estimate(calls_per_day: int, chars_per_reply: int, audio_sec_per_call: float,
days: int = 30, r: Rates = Rates()) -> dict:
tts = tts_cost(chars_per_reply, r) * calls_per_day * days
stt = stt_cost(audio_sec_per_call, r) * calls_per_day * days
whisper = whisper_cost(audio_sec_per_call, r) * calls_per_day * days
return {"tts_inr": round(tts, 2), "stt_sarvam_inr": round(stt, 2),
"stt_whisper_inr": round(whisper, 2)}
# Worked example with made-up traffic: 200 calls a day, 600-character replies,
# 40 seconds of caller audio per call.
print(estimate(200, 600, 40))
Working that example by hand with the stated inputs: TTS is 600 / 10,000 x 15 = ₹0.90 per reply, so 200 x 30 x 0.90 = ₹5,400 a month. Sarvam STT at ₹45 per hour is 40 / 3,600 x 45 = ₹0.50 per call, so 200 x 30 x 0.50 = ₹3,000. The Whisper line depends on the exchange rate you plug in. The traffic and the ₹90 rate are invented to show the arithmetic, not measured.
Plug in your own numbers, then compare against the free AICredits cost calculator for text-model costs if the same app also calls a chat model between the two audio steps.
Which model for what?
| Situation | Pick | Why |
|---|---|---|
| Hindi or regional-language voice bot | Sarvam Saarika and Bulbul | Built for Indian languages and accents, per the docs |
| English-only transcription, cost-sensitive | Whisper | Billed by the minute and not language-limited (99 languages per the docs) |
| Need a fast prototype in a voice you can swap later | OpenAI aliases mapped to Sarvam | Existing OpenAI TTS code keeps working |
| Phone-call agent end to end | Sarvam STT, a chat model, Sarvam TTS | See the voice agent architecture post |
Do not pick on the documented price alone. Test a dozen real recordings in your language with background noise, since the docs say nothing about accuracy and I have no WER numbers to give you. If your calls get recorded, read the consent and AI disclosure basics too.
Common errors
From the audio docs: 400 for a missing model, input or voice, or input over the length limit; 402 for an empty wallet; 429 when you exceed the RPM limit; 502 when all upstream providers fail, which the docs say to retry with exponential backoff. The retry and fallback patterns post has a wrapper you can reuse. Mixing model types also fails: a TTS model on the transcription endpoint, or the reverse, returns an error.
Next step
Create a key with a ₹100 budget at AICredits, run the TTS call, feed the WAV back to STT and compare the text with your input. That round trip is the cheapest test of your whole setup. The AICredits review covers the rest of the gateway.



