Skip to main content
Lesson 8 of 14 · Voxtral TTS: Voice Synthesis and Cloning

Basic Speech Generation

Lesson 8 of 14 Updated November 3, 2026 Developer
On this page
  1. Your First Voice Generation
  2. What Each Parameter Controls
  3. Scaling Up: An Audiobook Chapter
  4. When Generation Fails
  5. Key Takeaways

Course updated November 3, 2026 for Voxtral TTS 3, based on the Migrate from Voxtral 2 guide and Mistral’s Text to Speech pages: voxtral-mini-tts-2603, deprecated October 31, 2026 and retired April 30, 2027, is replaced in examples by voxtral-mini-tts-3-0. Predefined Voxtral 2 voices (gb_jane_neutral…) remain accepted; Voxtral 3 adds [emotion=…] and [pace=…] tags in text, the expressiveness setting, custom pronunciation (phoneme_hints), and 35 languages. Mistral recommends re-measuring latency after migration. Sources: Migrate from Voxtral 2 · Speech generation.

Your First Voice Generation

You now know how to create a voice and manage it via the Voices API. The final step is to do what all this work was for: produce audio. Mistral’s Speech API boils down to a single call, client.audio.speech.complete(), which takes text and a voice ID and returns audio encoded in base64. This lesson covers basic generation with a saved voice; on-the-fly cloning and streaming come next.

The code below is all you need to turn a sentence into an MP3 file on your disk. Pay close attention to the last step: the response doesn’t contain raw audio bytes you can use directly, but a base64 string you must decode before writing the file. This is the classic first-time mistake—saving the string as-is causes audio players to refuse to open it.

import base64
from pathlib import Path
from mistralai.client import Mistral

client = Mistral(api_key="your-api-key")

# Generate speech with a saved voice
response = client.audio.speech.complete(
    model="voxtral-mini-tts-3-0",
    input="Hello and welcome to this course on Voxtral TTS.",
    voice_id="your-voice-id",
    response_format="mp3",
)

# Decode base64 and save to file
audio_bytes = base64.b64decode(response.audio_data)
Path("output.mp3").write_bytes(audio_bytes)
print(f"Audio generated: {len(audio_bytes)} bytes")

What Each Parameter Controls

The model parameter specifies the synthesis model. As of today, voxtral-mini-tts-3-0 is the only one available, but always specify it explicitly: when other models appear, your code will continue producing exactly the same voice it does today.

input takes the text to speak, and it’s by far the parameter requiring the most attention. Voxtral 2 documentation advised staying under 300 words per request; Voxtral TTS 3 documentation no longer sets a limit, but single-paragraph requests remain easier to control and replay. Avoid emojis, which the model can’t interpret, as well as complex formatting—Markdown, HTML, special characters—which ends up read aloud verbatim. Finally, spell out numbers and abbreviations: “twenty-three” rather than “23”, “Limited Liability Company” rather than “LLC”. A voice agent that stumbles over numbers while announcing an amount loses credibility in one sentence.

voice_id identifies the voice used. You get it three ways: by creating a voice with client.audio.voices.create() (see lesson 5), by listing your existing voices with client.audio.voices.list(), or by reusing the ID of a Mistral professional voice. response_format, finally, sets the output format among mp3, wav, pcm, flac, and opus. This choice affects both file size and latency, and lesson 12 is entirely devoted to it.

Scaling Up: An Audiobook Chapter

For slightly longer text, splitting remains best practice. The principle is simple: prepare a list of paragraphs, generate each one separately, then accumulate the decoded bytes in a single buffer before writing the final file.

import base64
from pathlib import Path
from mistralai.client import Mistral

client = Mistral(api_key="your-api-key")
voice_id = "your-voice-id"

# Text split into paragraphs
paragraphs = [
    "First paragraph of the chapter. The narrator introduces the context.",
    "Second paragraph. The action begins to develop.",
    "Third paragraph. Suspense builds gradually.",
]

full_audio = b""

for i, text in enumerate(paragraphs):
    print(f"Generating segment {i + 1}/{len(paragraphs)}...")
    response = client.audio.speech.complete(
        model="voxtral-mini-tts-3-0",
        input=text,
        voice_id=voice_id,
        response_format="wav",
    )
    full_audio += base64.b64decode(response.audio_data)

Path("chapter-01.wav").write_bytes(full_audio)
print("Audio chapter generated successfully!")

This script works because it produces WAV, an uncompressed format whose segments concatenate end-to-end without special processing. Try the same thing with MP3 or Opus and the result will be unusable: these compressed formats carry headers and frame structures that raw concatenation breaks. Instead, use a library like pydub, which decodes each segment before merging them cleanly.

When Generation Fails

In production, you must protect the call. A voice_id deleted yesterday by a colleague, text that quietly grows longer because a field was concatenated, an API quota exhausted at month-end, a misspelled output format: each of these situations raises an exception, and without safeguards your batch processing stops midway through the corpus.

try:
    response = client.audio.speech.complete(
        model="voxtral-mini-tts-3-0",
        input="Text to convert to speech.",
        voice_id=voice_id,
        response_format="mp3",
    )
    audio_bytes = base64.b64decode(response.audio_data)
    Path("output.mp3").write_bytes(audio_bytes)
except Exception as e:
    print(f"Error during generation: {e}")

Take time to test this error path before going to production: deliberately generate with a bogus voice_id and verify that your application signals it clearly rather than producing an empty file.

Key Takeaways

  • Generation uses client.audio.speech.complete() with model voxtral-mini-tts-3-0
  • Split long texts into paragraphs and avoid emojis and formatting
  • Spell out numbers and abbreviations for correct pronunciation
  • Returned audio is base64—decode it before saving
  • For long texts, split into segments and concatenate results