ASR API reference
Every endpoint under asrv2prod.shunyalabs.ai, their request fields, response shapes, and error codes.
The examples below send Authorization: Bearer $ACCESS_TOKEN. The speech APIs accept only a short-lived access token — never your API key directly.
From the console (recommended). Open the console, click Generate token next to your API key, and copy it — then set it:
export ACCESS_TOKEN="eyJhbGciOiJSUzI1NiIs…paste-here"Or mint it from your API key — the path for production, where your app refreshes the token as it nears expiry (the response carries expires_in):
export ACCESS_TOKEN=$(curl -s -X POST https://app.shunyalabs.ai/api/auth/token \
-H "api-key: $SHUNYALABS_API_KEY" | jq -r .token)1. Authentication
All endpoints except /health require a bearer token in the Authorization header.
Authorization: Bearer $ACCESS_TOKEN2. POST /v1/audio/transcriptions
Batch transcription of an audio file or URL. Returns the full transcript, per-segment timestamps, and any intelligence results you enabled.
Required fields: file (or url) and model. Content-Type: multipart/form-data. See Configuration for every parameter.
Request:
curl -X POST https://asrv2prod.shunyalabs.ai/v1/audio/transcriptions \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-F "file=@call.wav" \
-F "model=zero-indic" \
-F "language_code=hi" \
-F "response_format=verbose_json"Request parameters (all optional except the audio and model):
| Field | Type | Default | Description |
|---|---|---|---|
file / url | file / string | — | The audio to transcribe — a multipart file upload, or a public url to fetch. One is required. |
model | string | zero-indic | Which model to run: zero-indic (Indian languages + English), zero-med (medical vocabulary), zero-codeswitch (mixed-language speech). See Models. |
language_code | string | auto | ISO 639-1 code (hi, en) or full English name (Hindi). auto detects the language. |
response_format | string | json | json returns { text }. verbose_json adds segments, timings, and any features you enabled. |
enable_diarization | boolean | false | Label each segment with a speaker (SPEAKER_00…) and return speaker_turns. |
num_speakers | integer | — | Speaker count hint for diarization when you know it. |
output_script | string | — | Transliterate the transcript into another script, e.g. Latin. |
boost_phrases | string | — | Domain terms to bias recognition toward, separated by || or newlines (names, products, acronyms). |
boost_weight | float | — | How strongly to bias toward boost_phrases. |
enable_intent_detection | boolean | false | Detect the call intent (optional intent_choices constrains the labels). Returned under nlp_analysis.intent. |
enable_sentiment_analysis | boolean | false | Overall sentiment, under nlp_analysis.sentiment. |
enable_summarization | boolean | false | Summary of the transcript (summary_max_length caps the length), under nlp_analysis.summary. |
enable_keyterm_normalization | boolean | false | Key terms (optional keyterm_keywords glossary), under nlp_analysis.keyterms. |
enable_emotion_diarization | boolean | false | Add per-segment emotion (segments[].emotion), a whole-clip emotion, and an emotion_summary. |
enable_speaker_identification | boolean | false | Map diarized speakers to registered profiles (use with num_speakers). See Speaker APIs. |
medical | boolean | false | Correct medical terminology in the transcript. |
enable_profanity_hashing | boolean | false | Mask profanity in the transcript. |
codeswitch | boolean | false | Restore code-switched (mixed-language) spans to their original script. |
lid | boolean | false | Run explicit language identification. |
Response (verbose_json):
{
"success": true,
"request_id": "b3f1a2c4...",
"text": "नमस्ते मोहम्मद जी, ये एक ज़रूरी कॉल है।",
"detected_language": "hi",
"detected_language_name": "Hindi",
"segments": [
{
"start": 0.51, "end": 5.70,
"text": "नमस्ते मोहम्मद जी...",
"speaker": "SPEAKER_00",
"confidence": 0.97,
"emotion": "neu",
"emotion_confidence": 0.68
}
],
"words": [
{ "word": "नमस्ते", "start": 0.51, "end": 0.94, "confidence": 0.99 }
],
"speakers": ["SPEAKER_00"],
"speaker_turns": [
{ "start": 0.51, "end": 5.70, "speaker": "SPEAKER_00" }
],
"audio_duration": 5.7,
"inference_time_ms": 812.3,
"emotion": { "label": "neu", "score": 0.68 },
"emotion_summary": {
"dominant_emotion": "neu",
"emotion_distribution": { "neu": 100.0 },
"avg_confidence": 0.68
},
"nlp_analysis": {
"intent": "Booking confirmation request",
"sentiment": "neutral",
"summary": "Caller greets Mohammad and flags an urgent call.",
"keyterms": ["ज़रूरी कॉल"]
}
}detected_language is an ISO code; detected_language_name is the same value spelled out. The nlp_analysis, emotion/emotion_summary, and speakers/speaker_turns blocks appear only when you enable the matching parameter, and per-segment emotion/emotion_confidenceappear only with enable_emotion_diarization=true.
Response (json: minimal, OpenAI-compatible):
{ "text": "नमस्ते मोहम्मद जी, ये एक ज़रूरी कॉल है।" }3. WebSocket /ws
Full protocol documented in Streaming. Summary of the lifecycle:
- Open
wss://asrv2prod.shunyalabs.ai/v1/realtime - Send a JSON init frame carrying your access token, plus
model,language,sample_rate,dtype(see Streaming for the exact fields) - Stream binary audio frames
- Send
"END"/{"type":"end"}/ empty binary to finalize - Receive
ready→speech_start→partial* →speech_end→final_segment→ ... →end_of_transcript→done
4. GET /health
Unauthenticated. Use for deployment smoke tests.
Request:
curl https://asrv2prod.shunyalabs.ai/healthResponse:
{ "ok": true }Returns ok: true when the service is healthy. No token required — use it for liveness and deployment smoke tests.
5. GET /languages
Returns the full supported language list with ISO codes and script mappings.
Request:
curl https://asrv2prod.shunyalabs.ai/languages \
-H "Authorization: Bearer $ACCESS_TOKEN"6. Speaker APIs
Diarization produces SPEAKER_00-style labels. To map those to actual names, register voice profiles using these four endpoints.
6.1 POST /v1/speakers/register
Request:
curl -X POST https://asrv2prod.shunyalabs.ai/v1/speakers/register \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-F "name=Priya" \
-F "file=@priya_sample.wav" \
-F "project=support_team"Response:
{ "success": true, "speaker": "Priya", "message": "Registered successfully" }6.2 DELETE /v1/speakers/delete
Request:
curl -X DELETE https://asrv2prod.shunyalabs.ai/v1/speakers/delete \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-F "name=Priya" \
-F "project=support_team"Response:
{ "success": true }7. HTTP error codes
| Status | Meaning |
|---|---|
| 200 | Success, audio or JSON body returned. |
| 400 | Bad request, missing or malformed fields. Response body: {"detail": "..."}. |
| 401 | Unauthorized — access token invalid, expired, or missing. Mint a new one and retry. |
| 422 | Synthesis / transcription error (invalid text or config). |
| 429 | Rate limit exceeded. Back off and retry. |
| 500 | Internal server error, unexpected server-side failure. |
| 503 | Service unavailable, an internal dependency is temporarily down. |
| 504 | Gateway timeout, request exceeded the processing window. |
8. Retry patterns
Safe to retry: 429, 500, 502, 503, 504. Not safe: 400, 401, 422: fix the request first.
Exponential backoff (Python)
import time, requests
def transcribe_with_retry(file_path, retries=3):
for attempt in range(retries):
with open(file_path, "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"},
timeout=120,
)
if r.status_code == 200:
return r.json()
if r.status_code in (429, 500, 502, 503, 504):
time.sleep(2 ** attempt) # 1s, 2s, 4s
continue
r.raise_for_status()
raise RuntimeError("Max retries exceeded")WebSocket reconnection
async def stream_with_reconnect(max_attempts=3):
for attempt in range(max_attempts):
try:
async with websockets.connect("wss://asrv2prod.shunyalabs.ai/v1/realtime") as ws:
await ws.send(json.dumps({...}))
async for msg in ws:
yield json.loads(msg)
return
except websockets.ConnectionClosed:
await asyncio.sleep(2 ** attempt)
raise RuntimeError("Max reconnects exceeded")9. Rate limits
| Limit | Value |
|---|---|
| Max file size | 500 MB |
| Max audio duration per file | 4 hours |
| Concurrent requests (default tier) | 16 |
| HTTP request timeout | Use at least 120 s for long audio |
| WebSocket inactivity timeout | 300 s (configurable up to 3600 s) |
10. Request IDs
Every response includes request_id (and an X-Request-Id header). Log it, Shunya support uses it to trace issues. On WebSocket, the session_id in the ready event is the equivalent.