Shunya Labs DocsShunya Labs Docs
🌐 International
🇺🇸 English
🇯🇵 Japanese
🇨🇳 Chinese (Simplified)
🇹🇼 Chinese (Traditional)
🇸🇦 Arabic
🇩🇪 German
🇫🇷 French
🇪🇸 Spanish
🇧🇷 Portuguese
🇷🇺 Russian
🇰🇷 Korean
🇹🇷 Turkish
🇻🇳 Vietnamese
🇮🇩 Indonesian
🇮🇳 Hindi Belt
हिन्दी — Hindi
भोजपुरी — Bhojpuri
मैथिली — Maithili
राजस्थानी — Rajasthani
🇮🇳 South India
தமிழ் — Tamil
తెలుగు — Telugu
ಕನ್ನಡ — Kannada
മലയാളം — Malayalam
🇮🇳 West India
मराठी — Marathi
ગુજરાતી — Gujarati
कोंकणी — Konkani
🇮🇳 East India
বাংলা — Bengali
ଓଡ଼ିଆ — Odia
অসমীয়া — Assamese
🇮🇳 North-East India
মেইতেই — Meitei
नेपाली — Nepali
🇮🇳 North India
ਪੰਜਾਬੀ — Punjabi
اردو — Urdu
کٲشُر — Kashmiri
डोगरी — Dogri
سنڌي — Sindhi

ASR API reference

Every endpoint under asrv2prod.shunyalabs.ai, their request fields, response shapes, and error codes.

First: get an access token

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_TOKEN

2. 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):

FieldTypeDefaultDescription
file / urlfile / stringThe audio to transcribe — a multipart file upload, or a public url to fetch. One is required.
modelstringzero-indicWhich model to run: zero-indic (Indian languages + English), zero-med (medical vocabulary), zero-codeswitch (mixed-language speech). See Models.
language_codestringautoISO 639-1 code (hi, en) or full English name (Hindi). auto detects the language.
response_formatstringjsonjson returns { text }. verbose_json adds segments, timings, and any features you enabled.
enable_diarizationbooleanfalseLabel each segment with a speaker (SPEAKER_00…) and return speaker_turns.
num_speakersintegerSpeaker count hint for diarization when you know it.
output_scriptstringTransliterate the transcript into another script, e.g. Latin.
boost_phrasesstringDomain terms to bias recognition toward, separated by || or newlines (names, products, acronyms).
boost_weightfloatHow strongly to bias toward boost_phrases.
enable_intent_detectionbooleanfalseDetect the call intent (optional intent_choices constrains the labels). Returned under nlp_analysis.intent.
enable_sentiment_analysisbooleanfalseOverall sentiment, under nlp_analysis.sentiment.
enable_summarizationbooleanfalseSummary of the transcript (summary_max_length caps the length), under nlp_analysis.summary.
enable_keyterm_normalizationbooleanfalseKey terms (optional keyterm_keywords glossary), under nlp_analysis.keyterms.
enable_emotion_diarizationbooleanfalseAdd per-segment emotion (segments[].emotion), a whole-clip emotion, and an emotion_summary.
enable_speaker_identificationbooleanfalseMap diarized speakers to registered profiles (use with num_speakers). See Speaker APIs.
medicalbooleanfalseCorrect medical terminology in the transcript.
enable_profanity_hashingbooleanfalseMask profanity in the transcript.
codeswitchbooleanfalseRestore code-switched (mixed-language) spans to their original script.
lidbooleanfalseRun 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:

  1. Open wss://asrv2prod.shunyalabs.ai/v1/realtime
  2. Send a JSON init frame carrying your access token, plus model, language, sample_rate, dtype (see Streaming for the exact fields)
  3. Stream binary audio frames
  4. Send "END" / {"type":"end"} / empty binary to finalize
  5. Receive readyspeech_startpartial* → speech_endfinal_segment → ... → end_of_transcript done

4. GET /health

Unauthenticated. Use for deployment smoke tests.

Request:

curl https://asrv2prod.shunyalabs.ai/health

Response:

{ "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.

Reference clip requirements
5-15 seconds, speaker alone, no background music, no overlapping voices, 16 kHz or higher sample rate.

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

StatusMeaning
200Success, audio or JSON body returned.
400Bad request, missing or malformed fields. Response body: {"detail": "..."}.
401Unauthorized — access token invalid, expired, or missing. Mint a new one and retry.
422Synthesis / transcription error (invalid text or config).
429Rate limit exceeded. Back off and retry.
500Internal server error, unexpected server-side failure.
503Service unavailable, an internal dependency is temporarily down.
504Gateway 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

LimitValue
Max file size500 MB
Max audio duration per file4 hours
Concurrent requests (default tier)16
HTTP request timeoutUse at least 120 s for long audio
WebSocket inactivity timeout300 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.

ASR API reference | Shunya Labs Docs