Your quickstart
Get from nothing to a working transcription and a working synthesis in five minutes. You'll need an API key, a short-lived access token minted from it, and about three lines of code per direction.
1. Get an API key
- Sign in at accounts.shunyalabs.ai.
- Navigate to API Keys and click Create New Key.
- Copy the key immediately, it's shown once.
.env file or a secrets manager. Add .env to .gitignore. Rotate if leaked.Set the environment variable
export SHUNYALABS_API_KEY="sk-your-key-here"$env:SHUNYALABS_API_KEY = "sk-your-key-here"2. Generate an access token
The speech APIs accept only a short-lived access token, never the API key itself. Generate one and set it as ACCESS_TOKEN — every call below uses it.
From the console (recommended). In the console, click Generate token next to your API key. It is minted and copied to your clipboard — paste it straight into Postman or a request. This is the fastest way to start.
export ACCESS_TOKEN="eyJhbGciOiJSUzI1NiIs…paste-here"$env:ACCESS_TOKEN = "eyJhbGciOiJSUzI1NiIs…paste-here"Or mint it from your API key. For a production integration, exchange the key at the token endpoint and refresh the token as it nears expiry — the response carries expires_in. (A copied console token stops working once it lapses, so build production on this path, not the button.)
# Exchange the key -> {"token": "eyJ...", "expires_in": 900}
curl -X POST https://app.shunyalabs.ai/api/auth/token \
-H "api-key: $SHUNYALABS_API_KEY"
# Or capture it straight into the env var:
export ACCESS_TOKEN=$(curl -s -X POST https://app.shunyalabs.ai/api/auth/token \
-H "api-key: $SHUNYALABS_API_KEY" | jq -r .token)import os, requests
auth = requests.post(
"https://app.shunyalabs.ai/api/auth/token",
headers={"api-key": os.environ["SHUNYALABS_API_KEY"]},
)
auth.raise_for_status()
access_token = auth.json()["token"] # reuse until close to expiryconst auth = await fetch("https://app.shunyalabs.ai/api/auth/token", {
method: "POST",
headers: { "api-key": process.env.SHUNYALABS_API_KEY },
});
const { token: accessToken } = await auth.json(); // reuse until close to expiry3. Transcribe an audio file
Send audio to POST /v1/audio/transcriptions with your token. The response is a JSON object with the transcript and per-segment timestamps.
curl -X POST https://asrv2prod.shunyalabs.ai/v1/audio/transcriptions \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-F "file=@meeting.wav" \
-F "model=zero-indic"import os, requests
# 1. Exchange the API key for a short-lived access token. Do this once and reuse the
# token until it is close to expiry — the response carries expires_in.
auth = requests.post(
"https://app.shunyalabs.ai/api/auth/token",
headers={"api-key": os.environ["SHUNYALABS_API_KEY"]},
)
auth.raise_for_status()
access_token = auth.json()["token"]
# 2. Call the API with the token. The API key is never sent to the speech service.
with open("meeting.wav", "rb") as f:
r = requests.post(
"https://asrv2prod.shunyalabs.ai/v1/audio/transcriptions",
headers={"Authorization": f"Bearer {access_token}"},
files={"file": f},
data={"model": "zero-indic"},
)
r.raise_for_status()
print(r.json()["text"])import fs from "node:fs";
const form = new FormData();
form.append("file", new Blob([fs.readFileSync("meeting.wav")]), "meeting.wav");
form.append("model", "zero-indic");
// 1. Exchange the API key for a short-lived access token, once, on your server.
const auth = await fetch("https://app.shunyalabs.ai/api/auth/token", {
method: "POST",
headers: { "api-key": process.env.SHUNYALABS_API_KEY },
});
const { token: accessToken } = await auth.json();
// 2. Call the API with the token. The API key never reaches the speech service.
const r = await fetch("https://asrv2prod.shunyalabs.ai/v1/audio/transcriptions", {
method: "POST",
headers: { Authorization: `Bearer ${accessToken}` },
body: form,
});
const data = await r.json();
console.log(data.text);from openai import OpenAI
import os
client = OpenAI(
# The SDK sends api_key as a Bearer token, so pass the ACCESS_TOKEN from step 2 (not the raw
# key). Refresh it when it nears expiry.
api_key=os.environ["ACCESS_TOKEN"],
base_url="https://asrv2prod.shunyalabs.ai/v1",
)
r = client.audio.transcriptions.create(
model="zero-indic",
file=open("meeting.wav", "rb"),
)
print(r.text)Response
{
"success": true,
"request_id": "b3f1a2c4-...",
"text": "नमस्ते मोहम्मद जी, ये एक ज़रूरी कॉल है।",
"segments": [
{ "start": 0.51, "end": 5.70, "text": "नमस्ते मोहम्मद जी..." }
],
"detected_language": "Hindi",
"audio_duration": 5.7,
"inference_time_ms": 812.3
}4. Generate speech
Now send text to POST /v1/audio/speech. The response body is audio bytes in your requested format.
curl -X POST https://ttsv2.shunyalabs.ai/v1/audio/speech \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{"model":"zero-indic","input":"Hello, how are you today?","voice":"Varun"}' \
--output hello.mp3import os, requests
auth = requests.post(
"https://app.shunyalabs.ai/api/auth/token",
headers={"api-key": os.environ["SHUNYALABS_API_KEY"]},
)
auth.raise_for_status()
access_token = auth.json()["token"]
r = requests.post(
"https://ttsv2.shunyalabs.ai/v1/audio/speech",
headers={"Authorization": f"Bearer {access_token}"},
json={"model": "zero-indic", "input": "Hello, how are you today?", "voice": "Varun"},
timeout=120,
)
r.raise_for_status()
with open("hello.mp3", "wb") as f:
f.write(r.content)import asyncio
from shunyalabs import AsyncShunyaClient
from shunyalabs.tts import TTSConfig
async def main():
async with AsyncShunyaClient() as client:
result = await client.tts.synthesize(
"Hello, how are you today?",
config=TTSConfig(model="zero-indic", voice="Varun"),
)
result.save("hello.mp3")
asyncio.run(main())5. Stream in real time
For voice agents and IVR, both ASR and TTS support streaming over WebSocket, see ASR streaming and TTS streaming. You get partial transcripts as speech is happening, and synthesized audio as text is generated.