POST/v1/voices/design

VoiceDesign API

Create any voice by describing it in natural language. Generate speech with custom voices — no presets, no limitations.

murmr's Key Differentiator

VoiceDesign lets you describe any voice in natural language — "A warm, professional female voice, calm and confident" — and generate speech with that voice instantly. Try different descriptions in the Playground before coding.

Endpoints

POST/v1/voices/design

Sync binary audio — returns complete audio file with response_format support (mp3 default)

POST/v1/voices/design/stream

SSE streaming — low-latency PCM audio chunks

Two modes

/v1/voices/design returns 200 with binary audio (mp3 default). /v1/voices/design/stream returns Server-Sent Events with PCM audio chunks for low-latency playback. Both accept the same JSON request body.

Request Parameters

Send a JSON body with the following parameters:

ParameterTypeDescription
textrequiredstringThe text to synthesize. Maximum 4,096 characters.
voice_descriptionrequiredstringNatural language description of the voice (e.g., "A warm, friendly male voice"). Maximum 500 characters.
languagestringOutput language: English, Spanish, Portuguese, German, French, Italian, Chinese, Japanese, Korean, Russian, or "Auto" (detect from text)
inputstringAlias for text (OpenAI API compatibility). If both text and input are provided, text takes precedence.

Sync Request (Binary Audio)

/v1/voices/design returns a complete audio file. Default format is mp3. Use this when you want the full file without handling SSE chunks.

curl -X POST "https://api.murmr.dev/v1/voices/design" \
  -H "Authorization: Bearer $MURMR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "text": "Bienvenue dans notre podcast. Explorons ensemble le futur de l\'IA.",
    "voice_description": "A warm, articulate French female narrator, mid-30s, smooth and professional",
    "language": "French"
  }' --output french_narrator.mp3

Streaming Request (SSE)

/v1/voices/design/stream returns audio chunks via Server-Sent Events for low-latency playback (~450ms to first chunk).

curl -X POST "https://api.murmr.dev/v1/voices/design/stream" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "text": "Welcome to our podcast. Today we explore the future of AI.",
    "voice_description": "A warm, professional female voice, 35 years old, calm and confident",
    "language": "English"
  }'

Complete WAV (SDK)

The SDK's design() method streams internally, collects all chunks, and returns a complete WAV buffer. Use this when you want the full audio file without handling chunks manually.

import { MurmrClient } from '@murmr/sdk';
import { writeFileSync } from 'node:fs';

const client = new MurmrClient({
  apiKey: process.env.MURMR_API_KEY!,
});

const wav = await client.voices.design({
  input: 'The quick brown fox jumps over the lazy dog.',
  voice_description: 'A deep, resonant male voice with a slow, deliberate pace',
  language: 'English',
});

writeFileSync('voicedesign.wav', wav);

SSE Event Format

JSON
// Audio chunk
data: {
  "chunk": "<base64 PCM int16>",
  "chunk_index": 0,
  "sample_rate": 24000,
  "format": "pcm_s16le",
  "mode": "voicedesign",
  "first_chunk_latency_ms": 450
}

// Completion
data: {
  "done": true,
  "total_chunks": 5,
  "total_time_ms": 2500,
  "first_chunk_latency_ms": 450,
  "sample_rate": 24000
}

See SSE Streaming for complete integration guide including Web Audio API playback and PCM-to-WAV conversion.

Voice Description Best Practices

Comprehensive Guide Available

For detailed guidance with examples directly from Qwen3-TTS documentation, see our Crafting Voices Guide.

Effective Descriptions

  • "A wise elderly wizard with a deep, mystical voice. Speaks slowly and deliberately with gravitas."
  • "Professional male CEO voice, confident and authoritative, measured pace"
  • "Warm grandmother voice, gentle and soothing, perfect for bedtime stories"
  • "Excited teenage girl, high-pitched voice with lots of energy"

Avoid

  • Celebrity references — "like Morgan Freeman"
  • Accent or nationality requests — "British accent", "Parisian accent" (use the language parameter instead)
  • Contradictory traits — "high-pitched deep voice"
  • Overly long descriptions (>500 chars)

Save Voice for Reuse

Each VoiceDesign call generates a unique voice. To reuse the same voice consistently, save it with voices.save(). The saved voice ID can then be used with /v1/audio/speech.

const inputText = 'This is the reference audio for my saved voice.';

const wav = await client.voices.design({
  input: inputText,
  voice_description: 'A confident male tech presenter, mid-30s, clear and energetic',
});

const saved = await client.voices.save({
  name: 'Tech Presenter',
  description: 'Confident male, mid-30s, clear and energetic, for product demos',
  audio: wav,
  ref_text: inputText,
  language: 'English',
});

console.log(`Saved as ${saved.id} — use this ID in future requests`);

Saved voice limits by plan: Free (3), Starter (10), Pro (25), Realtime (50), Scale (100)

Error Responses

400

Bad Request

Missing text or voice_description, text too long (>4096), description too long (>500)

401

Unauthorized

Missing or invalid API key

429

Quota Exceeded

Character quota or voice design quota exceeded for this billing period

See Error Reference for all error codes.

Error Handling Example

import { MurmrClient, MurmrError } from '@murmr/sdk';

const client = new MurmrClient({ apiKey: process.env.MURMR_API_KEY! });

try {
  const wav = await client.voices.design({
    input: "Bienvenue dans notre podcast.",
    voice_description: "A warm, articulate female narrator, mid-30s, smooth and professional",
    language: "French",
  });
  writeFileSync("output.wav", wav);
} catch (error) {
  if (error instanceof MurmrError) {
    switch (error.status) {
      case 400:
        console.error("Invalid request:", error.message);
        // Check text length, description length, missing fields
        break;
      case 401:
        console.error("Check your MURMR_API_KEY");
        break;
      case 429:
        console.error("Quota exceeded — upgrade plan or wait for reset");
        break;
      default:
        console.error(`API error ${error.status}: ${error.message}`);
    }
  } else {
    console.error("Network or unexpected error:", error);
  }
}

See Also