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

EndpointInput → outputBilling
POST /audio/speechJSON text → audio bytesper character
POST /audio/transcriptionsaudio file → transcriptper audio minute
POST /audio/translationsaudio file → English transcriptper audio minute
POST /audio/speech-to-speechaudio file → re-voiced audioper audio minute
POST /audio/conversationJSON turns → one dialogue clipper character
POST /audio/voice-changeraudio file → re-voiced audioper audio minute
POST /audio/isolatenoisy audio → clean voiceper audio minute
GET /voices— → catalog + pricingfree

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 assembly
  • POST /voices — registering a new clone
  • POST /voices/{id}/calibrate
  • DELETE /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.

FieldTypeRequiredDefaultDescription
inputstringyes—Text to speak, 1–5,000 characters. Any Indian script or Latin.
languagestringyesHIN3-letter code, e.g. HIN, GUJ, TAM (list). TTS Indian English is IEN.
voicestringnoaditiAny id from GET /voices — a preset, or a cloned voice by its voice_id or live_voice_id.
modelstringnonaad-tts-v1naad-tts-v1 or the faster, cheaper naad-tts-v1-turbo.
speednumberno1.0Speaking rate, clamped 0.5–2.0. Lower is slower.
stabilitynumberno0.50–1. Higher = more consistent delivery.
stylenumberno0.350–1. Higher = more expressive.
temperaturenumberno0.70–2 sampling variety.
seedintegernorandomSet 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:

HeaderMeaning
Content-TypeUsually audio/wav; a live upstream may return audio/mpeg. Check it before saving.
X-Naad-Model / X-Naad-VoiceThe model and voice actually used.
X-Naad-Cost-PaiseInteger paise charged for this call.
X-Naad-Live1 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.wav

Speech 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.

FieldTypeRequiredDefaultDescription
filefileyes—The audio to transcribe (multipart part).
languagestringyes—3-letter code of the spoken language. No auto-detect. STT Indian English is ENG.
modelstringnonaad-stt-v1The transcription model.
response_formatstringnotexttext · json · verbose_json (segments + timestamps) · dialogue_json (speaker turns) · srt (subtitles).
diarizebooleannofalsetrue labels who is speaking (SP1, SP2…) in segmented output.
num_speakersintegerno—How many people are talking (2–20), when you know. Used only with diarize=true; accuracy improves noticeably when the count is given.
modestringnoautoauto · 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 fetch aborts if response headers take over 300 seconds, regardless of anyAbortSignal you pass. Raise it with an undici agent: new Agent({ headersTimeout: 1_800_000, bodyTimeout: 1_800_000 }) passed as dispatcher.
  • Python requests — pass timeout=1800; the default is no timeout, but most wrappers set a short one.
  • axios — set timeout explicitly (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:

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

bash
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 it

Translation 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.

FieldTypeRequiredDefaultDescription
filefileyes—The recording to re-voice.
modelstringnonaad-sts-v1Batch speech-to-speech model.
voicestringnoaditiTarget voice from GET /voices.
speednumberno1.00.5–2.0.
stabilitynumberno0.50–1.
stylenumberno0.350–1.

Response: re-voiced audio bytes, with the same X-Naad-* headers as TTS.

bash
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.wav

Multi-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.

FieldTypeRequiredDefaultDescription
languagestringyes—3-letter code for the whole dialogue (Sanskrit SAN is supported here).
segmentsarrayyes—Ordered turns — see below. 1–50 segments.
modelstringnonaad-tts-v1TTS model.
conversation_idstringnogeneratedYour own id; echoed back and in X-Conversation-Id.
temperaturenumbernomodel defaultSampling variety.
seedintegernorandomFor reproducible output.
turn_gap_snumberno0.45Silence between different speakers (0–5s).
same_speaker_gap_snumberno0.28Silence between consecutive turns of one speaker.
modestringnoautoasync forces an async job.

Each segment is { "voice_id": "…", "text": "…" }:

FieldRequiredDescription
voice_idyesA preset id, a conversation speaker alias (male_voice_1, male_voice_2, kanika_female_sa, manav_male_sa), or a cloned voice id.
textyesThe 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.wav

Voice 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.

FieldTypeRequiredDefaultDescription
filefileyes—The source clip.
modelstringnonaad-s2s-v1Voice-changer model.
voicestringnoaditiTarget voice.
bash
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.wav

Voice 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.

FieldTypeRequiredDefaultDescription
filefileyes—The noisy recording.
modelstringnonaad-isolate-v1Isolation model.
bash
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.wav

List 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.

bash
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).

StatusCodeMeaning
400invalid_jsonJSON body did not parse (TTS, conversation).
400missing_input / input_too_longTTS text absent, or over 5,000 characters.
400missing_fileA multipart endpoint received no file part.
400missing_language / invalid_languageLanguage absent, unknown, or currently disabled.
400invalid_voiceVoice disabled for your account (see GET /voices).
400invalid_segmentsConversation segments failed validation (count, length, missing field).
404model_not_foundUnknown model id, or wrong kind for this endpoint.
413payload_too_largeUpload above the endpoint's cap (25/150/200 MB) or JSON body over 256 KB.
502upstream_errorThe voice model failed — retry once, then see errors.