Audio & Voice API
One REST API for Indian-language voice. Every endpoint below shares the same base URL, authentication and error envelope — learn it once and every call follows the same shape. For live, two-way conversation over a WebSocket, see Realtime Speech to Speech instead.
Base URL https://platform.oogam.ai/v1 · authenticate with Authorization: Bearer sk-setu-… (Authentication) · errors use one JSON envelope · every request is metered in rupees from your project wallet, and the exact charge for each call comes back in the X-Naad-Cost-Paise response header.
Endpoints at a glance
| Endpoint | Input → output | Billing |
|---|---|---|
POST /audio/speech | JSON text → audio bytes | per character |
POST /audio/transcriptions | audio file → transcript | per audio minute |
POST /audio/translations | audio file → English transcript | per audio minute |
POST /audio/speech-to-speech | audio file → re-voiced audio | per audio minute |
POST /audio/conversation | JSON turns → one dialogue clip | per character |
POST /audio/voice-changer | audio file → re-voiced audio | per audio minute |
POST /audio/isolate | noisy audio → clean voice | per audio minute |
GET /voices | — → catalog + pricing | free |
All audio endpoints need the naad product enabled on your API key. Language codes are 3-letter (full list) and the models never auto-detect — always send the spoken language.
Temporarily unavailable
Batch speech now synthesises through the realtime voice workers rather than a second copy of the model, and four endpoints have not been ported to that path yet. Each returns 503 with a JSON body explaining exactly this, so you can surface the message directly:
POST /audio/conversation— multi-speaker assemblyPOST /voices— registering a new clonePOST /voices/{id}/calibrateDELETE /voices/{id}
Existing clones are not affected. Synthesising with an already-registered clone still works and GET /voices still lists them — only registering, calibrating and deleting are blocked. POST /audio/speech, GET /voices and GET /voices/{id} are unchanged.
Text to Speech
POST /audio/speech — JSON body in, audio bytes out, always synchronous. Billed per character.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
input | string | yes | — | Text to speak, 1–5,000 characters. Any Indian script or Latin. |
language | string | yes | HIN | 3-letter code, e.g. HIN, GUJ, TAM (list). TTS Indian English is IEN. |
voice | string | no | aditi | Any id from GET /voices — a preset, or a cloned voice by its voice_id or live_voice_id. |
model | string | no | naad-tts-v1 | naad-tts-v1 or the faster, cheaper naad-tts-v1-turbo. |
speed | number | no | 1.0 | Speaking rate, clamped 0.5–2.0. Lower is slower. |
stability | number | no | 0.5 | 0–1. Higher = more consistent delivery. |
style | number | no | 0.35 | 0–1. Higher = more expressive. |
temperature | number | no | 0.7 | 0–2 sampling variety. |
seed | integer | no | random | Set for reproducible audio; omit for natural variation call to call. |
Response: raw audio bytes — save the body straight to a file, there is no JSON wrapper on success. Useful response headers:
| Header | Meaning |
|---|---|
Content-Type | Usually audio/wav; a live upstream may return audio/mpeg. Check it before saving. |
X-Naad-Model / X-Naad-Voice | The model and voice actually used. |
X-Naad-Cost-Paise | Integer paise charged for this call. |
X-Naad-Live | 1 real model, 0 simulated. |
curl https://platform.oogam.ai/v1/audio/speech \
-H "Authorization: Bearer $OOGAM_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "naad-tts-v1",
"input": "नमस्ते! आज मैं आपकी क्या मदद कर सकती हूँ?",
"voice": "aditi",
"language": "HIN"
}' \
--output hello.wavSpeech to Text
POST /audio/transcriptions — multipart form in, transcript out. Billed per audio minute. Uploads up to 200 MB; formats: wav, mp3, m4a, aac, flac, ogg, webm. Mislabeled files are detected from their bytes and corrected automatically.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
file | file | yes | — | The audio to transcribe (multipart part). |
language | string | yes | — | 3-letter code of the spoken language. No auto-detect. STT Indian English is ENG. |
model | string | no | naad-stt-v1 | The transcription model. |
response_format | string | no | text | text · json · verbose_json (segments + timestamps) · dialogue_json (speaker turns) · srt (subtitles). |
diarize | boolean | no | false | true labels who is speaking (SP1, SP2…) in segmented output. |
num_speakers | integer | no | — | How many people are talking (2–20), when you know. Used only with diarize=true; accuracy improves noticeably when the count is given. |
mode | string | no | auto | auto · sync · async. Async job mode is temporarily unavailable — every request currently returns the transcript directly, whatever this is set to. |
Response timing
Every request returns the transcript directly with 200; the connection is held until it is ready. On an idle engine transcription runs at roughly 0.6× realtime including diarization — a 10-minute recording in about 6 minutes. Capacity is shared, though, and busy periods stretch that a long way: we have measured the same 10-minute file at 6 minutes idle and 21 minutes under load, and an identical 4-second clip anywhere between 8 seconds and 2 minutes depending on what else was running. Size your timeout for the busy case — the server allows up to 90 minutes per request.
Recordings up to 60 minutes or 200 MB are handled in a single request — split anything longer. Concurrent requests are accepted and do run alongside each other, but they compete for the same capacity, so expect latency to rise with load rather than stay flat.
Accepted formats: WAV, MP3, M4A, MP4, MOV, FLAC, OGG, WebM and AAC. Mono or stereo, any common sample rate — 8 kHz telephony recordings through 48 kHz all decode.
Watch your HTTP client's own timeout. Several default to giving up long before a long transcription finishes, and the failure looks like a network error rather than a timeout:
- Node.js
fetchaborts if response headers take over 300 seconds, regardless of anyAbortSignalyou pass. Raise it with an undici agent:new Agent({ headersTimeout: 1_800_000, bodyTimeout: 1_800_000 })passed asdispatcher. - Python
requests— passtimeout=1800; the default is no timeout, but most wrappers set a short one. - axios — set
timeoutexplicitly (0 for none); nginx and other proxies in front of your own service may cap it independently.
Async job mode (202 + polling) is temporarily switched off while an upstream issue with job callbacks is resolved. Code that already checks for 202 is safe to leave in place — it simply will not fire.
curl https://platform.oogam.ai/v1/audio/transcriptions \
-H "Authorization: Bearer $OOGAM_API_KEY" \
-F "model=naad-stt-v1" \
-F "language=HIN" \
-F "response_format=verbose_json" \
-F "diarize=true" \
-F "file=@call-recording.wav"verbose_json response
With response_format=verbose_json (and diarize=true) each segment carries timings and a speaker label:
{
"text": "नमस्ते, मैं राज बोल रहा हूँ। आपका ऑर्डर कल पहुँच जाएगा।",
"language": "HIN",
"duration": 6.4,
"segments": [
{ "id": 0, "start": 0.0, "end": 2.1, "speaker": "SP1",
"text": "नमस्ते, मैं राज बोल रहा हूँ।" },
{ "id": 1, "start": 2.4, "end": 6.4, "speaker": "SP1",
"text": "आपका ऑर्डर कल पहुँच जाएगा।" }
]
}Polling an async job
Kept for reference — async jobs are temporarily disabled, so this endpoint is not needed today.
curl https://platform.oogam.ai/v1/audio/transcriptions-status/JOB_ID \
-H "Authorization: Bearer $OOGAM_API_KEY"
# 202 {"status":"processing"} → keep polling (every few seconds)
# 200 <transcript> → done, same shape as the sync reply
# 200 {"status":"failed","error":"…"} → the model could not process itTranslation to English
POST /audio/translations — identical parameters to transcription, but the transcript always comes back in English no matter the spoken language. Same response timing and per-minute billing as transcription.
Speech to Speech (batch)
POST /audio/speech-to-speech — re-voice a whole recording in one call: send audio, get audio back in the target voice. Multipart form, synchronous, billed per source minute (₹0.38 / min). Uploads up to 25 MB. For live, turn-taking conversation use the realtime WebSocket instead.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
file | file | yes | — | The recording to re-voice. |
model | string | no | naad-sts-v1 | Batch speech-to-speech model. |
voice | string | no | aditi | Target voice from GET /voices. |
speed | number | no | 1.0 | 0.5–2.0. |
stability | number | no | 0.5 | 0–1. |
style | number | no | 0.35 | 0–1. |
Response: re-voiced audio bytes, with the same X-Naad-* headers as TTS.
curl https://platform.oogam.ai/v1/audio/speech-to-speech \
-H "Authorization: Bearer $OOGAM_API_KEY" \
-F "model=naad-sts-v1" \
-F "voice=aditi" \
-F "speed=1.0" \
-F "file=@original.wav" \
--output revoiced.wavMulti-speaker conversation
Currently unavailable — this endpoint returns 503. Multi-speaker assembly has not been ported to the realtime voice workers yet. Single-speaker POST /audio/speech is unaffected.
POST /audio/conversation — turn a scripted dialogue into one continuous clip with distinct voices per speaker. JSON in, audio out; billed per character across all turns.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
language | string | yes | — | 3-letter code for the whole dialogue (Sanskrit SAN is supported here). |
segments | array | yes | — | Ordered turns — see below. 1–50 segments. |
model | string | no | naad-tts-v1 | TTS model. |
conversation_id | string | no | generated | Your own id; echoed back and in X-Conversation-Id. |
temperature | number | no | model default | Sampling variety. |
seed | integer | no | random | For reproducible output. |
turn_gap_s | number | no | 0.45 | Silence between different speakers (0–5s). |
same_speaker_gap_s | number | no | 0.28 | Silence between consecutive turns of one speaker. |
mode | string | no | auto | async forces an async job. |
Each segment is { "voice_id": "…", "text": "…" }:
| Field | Required | Description |
|---|---|---|
voice_id | yes | A preset id, a conversation speaker alias (male_voice_1, male_voice_2, kanika_female_sa, manav_male_sa), or a cloned voice id. |
text | yes | The line to speak — up to 2,000 characters. |
Limits: at most 50 segments, 2,000 characters per segment, 12,000 characters total, request body 256 KB.
Response: one audio clip (bytes) with headers X-Conversation-Id and X-Segments. Longer dialogues (4+ turns) may return an async job instead — poll it the same way as STT:
curl https://platform.oogam.ai/v1/audio/conversation \
-H "Authorization: Bearer $OOGAM_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"language": "HIN",
"model": "naad-tts-v1",
"segments": [
{ "voice_id": "aditi", "text": "नमस्ते! Naad में आपका स्वागत है।" },
{ "voice_id": "male_voice_1", "text": "धन्यवाद! यह आवाज़ बहुत साफ़ है।" },
{ "voice_id": "aditi", "text": "बिलकुल — और पूरी तरह भारतीय।" }
]
}' \
--output dialogue.wavVoice changer
POST /audio/voice-changer — swap the voice of a clip while keeping the words and timing. Multipart form, synchronous, billed per source minute (₹0.45 / min), uploads up to 25 MB.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
file | file | yes | — | The source clip. |
model | string | no | naad-s2s-v1 | Voice-changer model. |
voice | string | no | aditi | Target voice. |
curl https://platform.oogam.ai/v1/audio/voice-changer \
-H "Authorization: Bearer $OOGAM_API_KEY" \
-F "model=naad-s2s-v1" \
-F "voice=kavya" \
-F "file=@memo.wav" \
--output changed.wavVoice isolation
POST /audio/isolate — strip background noise and music, leaving clean speech (ideal to prep field recordings before STT). Multipart form, synchronous, billed per minute (₹0.23 / min). Uploads up to 150 MB.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
file | file | yes | — | The noisy recording. |
model | string | no | naad-isolate-v1 | Isolation model. |
curl https://platform.oogam.ai/v1/audio/isolate \
-H "Authorization: Bearer $OOGAM_API_KEY" \
-F "model=naad-isolate-v1" \
-F "file=@noisy-field-recording.wav" \
--output clean.wavList voices
GET /voices returns every voice your key may use and the model catalogue with live pricing. It is the source of truth for the voice field above — see the full Voices reference for every response field, including the available flag and how a clone's live_voice_id maps onto the realtime WebSocket.
curl https://platform.oogam.ai/v1/voices \
-H "Authorization: Bearer $OOGAM_API_KEY"Endpoint errors
These are specific to the audio endpoints; auth, rate-limit and wallet errors are shared across the whole API (see the full error reference).
| Status | Code | Meaning |
|---|---|---|
| 400 | invalid_json | JSON body did not parse (TTS, conversation). |
| 400 | missing_input / input_too_long | TTS text absent, or over 5,000 characters. |
| 400 | missing_file | A multipart endpoint received no file part. |
| 400 | missing_language / invalid_language | Language absent, unknown, or currently disabled. |
| 400 | invalid_voice | Voice disabled for your account (see GET /voices). |
| 400 | invalid_segments | Conversation segments failed validation (count, length, missing field). |
| 404 | model_not_found | Unknown model id, or wrong kind for this endpoint. |
| 413 | payload_too_large | Upload above the endpoint's cap (25/150/200 MB) or JSON body over 256 KB. |
| 502 | upstream_error | The voice model failed — retry once, then see errors. |