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

Not available right now

The catalogue is naad-tts-v2, naad-stt-v2 and naad-sts-v2 — see Models. The endpoints below exist in the URL space but are not part of it, and do not do their job today. They are listed so you can recognise the behaviour rather than debug it:

EndpointWhat happens
POST /audio/conversationRetired. Returns 502 (upstream 410 Gone). For multi-speaker audio call POST /audio/speech once per turn and join the clips yourself.
POST /audio/translationsReturns 200, but the text is the source language, untranslated — only the language field is relabelled en. Use /audio/transcriptions and translate the text yourself.
POST /audio/speech-to-speechReturns 502 (upstream 413) for every upload, a 1-second clip included. Batch re-voicing is not part of the catalogue — for a live voice swap, use the realtime socket.
POST /embeddingsReturns 503 model_not_configured — not activated on this deployment.

Three voice-clone management endpoints return 503 with a JSON body explaining exactly this, so you can surface the message directly:

  • 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 — Naad TTS v2 is live

Set model to naad-tts-v2 to use v2 with your existing API key and POST /v1/audio/speech endpoint. The request fields and audio response format stay the same. Omitting model still selects the previous-generation engine for compatibility, so set it explicitly on every request. The /v1 in the URL is the API version, not the speech model version.

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.
languagestringnoHIN3-letter code, e.g. HIN, GUJ, TAM (list). Indian English is ENG. There is no auto-detection — an unknown code falls back to Hindi rather than erroring.
voicestringnoaditiAny id from GET /voices — a preset, or a cloned voice by its voice_id or live_voice_id.
modelstringnoprevious generationSend naad-tts-v2. Omitting model still falls back to the previous-generation engine, so set it explicitly on every request.

Those four fields are the whole request. speed, stability, style, temperature and seed are still accepted — older integrations keep working — but the v2 engine does not act on them: the same text at speed: 0.5 and speed: 2.0 returns audio of identical length, and two calls with the same seed return different audio. Treat every call as a fresh, natural performance. To start playing before the clip is finished, see streaming below.

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-Generation-IdId for this synthesis. Quote it when reporting a bad clip — it is how we find the 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-v2",
    "input": "नमस्ते! आज मैं आपकी क्या मदद कर सकती हूँ?",
    "voice": "aditi",
    "language": "HIN"
  }' \
  --output hello.wav

Streaming

Send stream: true and the audio is sent while it is still being generated, so playback can start almost immediately. Measured against this endpoint on the same sentence: first byte at 0.53 s streaming against 1.80 s buffered, for byte-identical audio. The response is Transfer-Encoding: chunked with no Content-Length.

FieldTypeDefaultDescription
streambooleanfalsetrue streams the audio as it is produced.
stream_formatstringaudioaudio sends raw bytes. sse sends Server-Sent Events: speech.audio.delta with base64 audio, then speech.audio.done with audio_seconds.
response_formatstringwavStreaming only. wav sends a 44-byte header whose size fields are 0xFFFFFFFF (the length is not known yet) followed by PCM; pcm sends raw 16-bit little-endian PCM at 24 kHz mono.

Write it to a file or a player as it arrives — with curl that means -N. Because the WAV header carries placeholder sizes, a player that insists on a correct length may refuse the file; if you are saving it for later, rewrite the two size fields once the stream ends, or request pcm and wrap it yourself. Billing is unchanged (per character) and X-Naad-Cost-Paise is on the response as usual. Streamed calls are not kept in your generation history, since we do not hold a copy of the audio.

bash
# -N keeps curl from buffering, so bytes land as they arrive
curl -N https://platform.oogam.ai/v1/audio/speech \
  -H "Authorization: Bearer $OOGAM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "naad-tts-v2",
    "input": "नमस्ते! आपका ऑर्डर कल पहुँच जाएगा।",
    "language": "HIN",
    "voice": "aditi",
    "stream": true
  }' \
  --output speech.wav

# Server-Sent Events instead of raw bytes
curl -N https://platform.oogam.ai/v1/audio/speech \
  -H "Authorization: Bearer $OOGAM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"naad-tts-v2","input":"Streaming check.","language":"HIN",
       "voice":"aditi","stream":true,"stream_format":"sse"}'

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-v2The transcription model.
response_formatstringnotexttext · json · verbose_json (segments + timestamps) · srt (subtitles) · dialogue_json (the same segments as HH:MM:SS.mmm strings, wrapped in an array — no speaker labels).

diarize, num_speakers and mode are still accepted so existing calls keep working, but none of them changes the result today: no response carries a speaker field, and every request is answered synchronously whatever mode says. Speaker separation is not available on this endpoint — if you need it, record each party on its own channel and transcribe them separately.

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 — 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.
curl https://platform.oogam.ai/v1/audio/transcriptions \
  -H "Authorization: Bearer $OOGAM_API_KEY" \
  -F "model=naad-stt-v2" \
  -F "language=HIN" \
  -F "response_format=verbose_json" \
  -F "file=@call-recording.wav"

verbose_json response

With response_format=verbose_json the transcript is split into segments. language comes back lower-case(hin), and timestampsEstimated: true means the segment times are evenly divided across the recording, not forced alignment — fine for reading along, not for frame-accurate subtitles:

json
{
  "text": "नमस्ते, मैं राज बोल रहा हूँ। आपका ऑर्डर कल पहुँच जाएगा।",
  "language": "hin",
  "duration": 6.4,
  "model": "naad-stt-v2",
  "segments": [
    { "id": 0, "start": 0.0, "end": 3.2,
      "text": "नमस्ते, मैं राज बोल रहा हूँ।" },
    { "id": 1, "start": 3.2, "end": 6.4,
      "text": "आपका ऑर्डर कल पहुँच जाएगा।" }
  ],
  "timestampsEstimated": true
}

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).
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).
404model_not_foundUnknown model id, or wrong kind for this endpoint.
413payload_too_largeTranscription upload above 200 MB, or a JSON body over 256 KB.
502upstream_errorThe voice model failed — retry once, then see errors.