Skip to content
Docs

AI Gateway Text to Speech

Generate spoken audio from text with ElevenLabs, Google, Microsoft, and OpenAI speech models. Use this for voiceovers, audio versions of written content, or spoken responses in your app.

Use this to turn text into spoken audio. For live, two-way voice, see Realtime; to transcribe recorded audio, see Speech to Text.

Text to speech is in beta and access is rolling out gradually. Speech models may not appear in the model catalog yet for your team.

ProviderModel ID
ElevenLabselevenlabs/eleven-flash-v2
ElevenLabselevenlabs/eleven-flash-v2.5
ElevenLabselevenlabs/eleven-multilingual-v2
ElevenLabselevenlabs/eleven-v3
ElevenLabselevenlabs/eleven-v3-conversational
ElevenLabselevenlabs/eleven-v4
ElevenLabselevenlabs/eleven-v4-turbo
ElevenLabselevenlabs/music-v2
ElevenLabselevenlabs/music-v2.5
ElevenLabselevenlabs/sound-effects-v2
Googlegoogle/gemini-3.8-flash-lite-tts
Googlegoogle/gemini-3.8-flash-tts
Microsoftmicrosoft/mai-voice-2
Microsoftmicrosoft/mai-voice-2-flash
Microsoftmicrosoft/mai-voice-2.1
Microsoftmicrosoft/mai-voice-2.1-flash
OpenAIopenai/tts-1
OpenAIopenai/tts-1-hd

For SDK options and result types, see AI SDK speech and Python speech.

Pass a speech model ID directly to generateSpeech. The AI SDK uses AI Gateway by default:

generate-speech.ts
import { generateSpeech } from 'ai';
import { writeFile } from 'node:fs/promises';
 
const result = await generateSpeech({
  model: 'openai/tts-1',
  text: 'Hello! Thanks for trying out AI Gateway.',
  voice: 'alloy',
  outputFormat: 'mp3',
});
 
await writeFile('greeting.mp3', result.audio.uint8Array);
 
console.log("Saved greeting.mp3");
generate-speech.py
import asyncio
import ai
import base64
from pathlib import Path
 
async def main():
    result = await ai.ops.generate_audio(
        ai.get_model('openai/tts-1'),
        'Hello! Thanks for trying out AI Gateway.',
        params=ai.ops.AudioParams(voice="alloy", output_format="mp3"),
    )
    audio = result.value[0]
    data = audio.data if isinstance(audio.data, bytes) else base64.b64decode(audio.data)
    Path("greeting.mp3").write_bytes(data)
    print("Saved greeting.mp3")
 
asyncio.run(main())

Speech support ships in the stable AI SDK releases. Install it with pnpm add ai.

The Python examples use the AI SDK for Python beta. These audio operations use dedicated AI Gateway endpoints. They are separate from Chat Completions, Messages, and Responses. See the Python SDK setup for installation requirements.

Pass an ElevenLabs model ID to generateSpeech. Set voice to an ElevenLabs voice ID, such as hpp4J3VqNfWAUOO0d1Us (Bella):

generate-elevenlabs-speech.ts
import { generateSpeech } from 'ai';
import { writeFile } from 'node:fs/promises';
 
const result = await generateSpeech({
  model: 'elevenlabs/eleven-flash-v2.5',
  text: 'Welcome to the audio edition.',
  voice: 'hpp4J3VqNfWAUOO0d1Us',
  outputFormat: 'mp3',
});
 
await writeFile('greeting.mp3', result.audio.uint8Array);

Use experimental_streamSpeech to receive audio chunks as the model generates them. text accepts a string or a stream of text deltas:

stream-elevenlabs-speech.ts
import { experimental_streamSpeech as streamSpeech } from 'ai';
import { writeFile } from 'node:fs/promises';
 
const result = streamSpeech({
  model: 'elevenlabs/eleven-flash-v2.5',
  text: 'Welcome to the audio edition.',
  voice: 'hpp4J3VqNfWAUOO0d1Us',
  outputFormat: { type: 'audio/mpeg' },
});
 
const chunks: Uint8Array[] = [];
for await (const chunk of result.audioStream) {
  chunks.push(chunk);
}
 
await writeFile('greeting.mp3', Buffer.concat(chunks));

Flash v2, Flash v2.5, and Multilingual v2 stream one voice per connection. To stream v3, v3 Conversational, v4, or v4 Turbo, set providerOptions.elevenlabs.dialogue.voices. For example, replace the streamSpeech call above with:

elevenlabs-dialogue.ts
const result = streamSpeech({
  model: 'elevenlabs/eleven-v4',
  text: [
    { type: 'text', text: 'Welcome to the audio edition. ', voice: 'hpp4J3VqNfWAUOO0d1Us' },
    { type: 'text', text: 'Thanks for listening.', voice: 'CwhRBWXzGAHq8TQ4Fs17' },
  ],
  outputFormat: { type: 'audio/mpeg' },
  providerOptions: {
    elevenlabs: {
      dialogue: { voices: ['hpp4J3VqNfWAUOO0d1Us', 'CwhRBWXzGAHq8TQ4Fs17'] },
    },
  },
});

Each text part's voice must appear in dialogue.voices. v3 Conversational accepts exactly one voice. For more options, see AI SDK speech.

Eleven v4 and v3 read audio tags in square brackets as delivery directions rather than words to say. Tags can set:

  • Emotion, such as [whispers] or [excited]
  • Reactions, such as [laughs] or [sighs]
  • Sound cues, such as [footsteps]

For example, replace the generateSpeech call in the first ElevenLabs example with:

elevenlabs-audio-tags.ts
const result = await generateSpeech({
  model: 'elevenlabs/eleven-v4',
  text: '[whispers] The deploy is live. [laughs] On a Friday.',
  voice: 'hpp4J3VqNfWAUOO0d1Us',
});

Use elevenlabs/music-v2.5 or elevenlabs/music-v2 with generateSpeech to turn a description into music. Use AI SDK v7. Omit voice; these models do not support streaming:

generate-elevenlabs-music.ts
import { generateSpeech } from 'ai';
import { writeFile } from 'node:fs/promises';
 
const result = await generateSpeech({
  model: 'elevenlabs/music-v2.5',
  text: 'An upbeat synthwave intro with driving bass.',
  providerOptions: {
    elevenlabs: { musicLengthMs: 5000 },
  },
});
 
await writeFile('audio.mp3', result.audio.uint8Array);

To generate sound effects, replace the generateSpeech call above with:

generate-elevenlabs-sound-effects.ts
const result = await generateSpeech({
  model: 'elevenlabs/sound-effects-v2',
  text: 'Glass shattering on a concrete floor.',
  providerOptions: {
    elevenlabs: { durationSeconds: 2 },
  },
});

Music also accepts forceInstrumental to leave out vocals, and compositionPlan to set the style and length of each section in place of the text prompt. Sound effects also accept loop for a clip that repeats without a gap, and promptInfluence, from 0 to 1, for how closely the result follows the description. For every option, see the AI SDK ElevenLabs provider.

Use either google/gemini-3.8-flash-lite-tts or google/gemini-3.8-flash-tts with generateSpeech. Both support single-voice narration and two-speaker dialogue.

Choose a prebuilt voice such as Kore, Puck, Zephyr, or Charon. Voice names are case-sensitive, and the default is Kore. Set instructions to control delivery without changing the spoken text:

generate-google-speech.ts
import { generateSpeech } from 'ai';
import { writeFile } from 'node:fs/promises';
 
const result = await generateSpeech({
  model: 'google/gemini-3.8-flash-lite-tts',
  text: 'Welcome to the audio edition.',
  voice: 'Kore',
  instructions: 'Warm, relaxed, and speaking slowly',
  outputFormat: 'wav',
});
 
await writeFile('greeting.wav', result.audio.uint8Array);
 
console.log('Saved greeting.wav');
generate-google-speech.js
import { generateSpeech } from 'ai';
import { writeFile } from 'node:fs/promises';
 
const result = await generateSpeech({
  model: 'google/gemini-3.8-flash-lite-tts',
  text: 'Welcome to the audio edition.',
  voice: 'Kore',
  instructions: 'Warm, relaxed, and speaking slowly',
  outputFormat: 'wav',
});
 
await writeFile('greeting.wav', result.audio.uint8Array);
 
console.log('Saved greeting.wav');

Google returns WAV audio by default. Save result.audio.uint8Array directly as it already includes the WAV header. Gemini 3.8 also supports pcm, mulaw, and alaw through outputFormat.

For two-speaker dialogue, configure voices and structured turns under providerOptions.google. Set text to an empty string because turns supplies the transcript. Each turn's speechMetadata.speaker must match a configured speaker:

generate-google-dialogue.ts
import { generateSpeech } from 'ai';
import { writeFile } from 'node:fs/promises';
 
const result = await generateSpeech({
  model: 'google/gemini-3.8-flash-tts',
  text: '',
  outputFormat: 'wav',
  providerOptions: {
    google: {
      multiSpeakerVoiceConfig: {
        speakerVoiceConfigs: [
          {
            speaker: 'Host',
            voiceConfig: { prebuiltVoiceConfig: { voiceName: 'Kore' } },
          },
          {
            speaker: 'Guest',
            voiceConfig: { prebuiltVoiceConfig: { voiceName: 'Puck' } },
          },
        ],
      },
      turns: [
        {
          text: 'What are we exploring today?',
          speechMetadata: { speaker: 'Host', style: 'curious' },
        },
        {
          text: 'How to turn text into spoken audio.',
          speechMetadata: { speaker: 'Guest', style: 'cheerful' },
        },
      ],
    },
  },
});
 
await writeFile('dialogue.wav', result.audio.uint8Array);
 
console.log('Saved dialogue.wav');
generate-google-dialogue.js
import { generateSpeech } from 'ai';
import { writeFile } from 'node:fs/promises';
 
const result = await generateSpeech({
  model: 'google/gemini-3.8-flash-tts',
  text: '',
  outputFormat: 'wav',
  providerOptions: {
    google: {
      multiSpeakerVoiceConfig: {
        speakerVoiceConfigs: [
          {
            speaker: 'Host',
            voiceConfig: { prebuiltVoiceConfig: { voiceName: 'Kore' } },
          },
          {
            speaker: 'Guest',
            voiceConfig: { prebuiltVoiceConfig: { voiceName: 'Puck' } },
          },
        ],
      },
      turns: [
        {
          text: 'What are we exploring today?',
          speechMetadata: { speaker: 'Host', style: 'curious' },
        },
        {
          text: 'How to turn text into spoken audio.',
          speechMetadata: { speaker: 'Guest', style: 'cheerful' },
        },
      ],
    },
  },
});
 
await writeFile('dialogue.wav', result.audio.uint8Array);
 
console.log('Saved dialogue.wav');

A turn's speechMetadata.style overrides instructions. Turns without a style inherit instructions. For more options, see the AI SDK Google speech documentation.

Use microsoft/mai-voice-2.1 or microsoft/mai-voice-2 for high-fidelity, long-form narration, and microsoft/mai-voice-2.1-flash or microsoft/mai-voice-2-flash for low-latency voice agents. All four models share the same voices and options.

Choose a prebuilt voice such as en-US-Harper, de-DE-Mia, or es-MX-Valeria. Each voice name includes its locale, which sets the spoken language, and the default is en-US-Harper. Set providerOptions.azure.style to control delivery, and styleDegree to set its intensity:

generate-microsoft-speech.ts
import { generateSpeech } from 'ai';
import { writeFile } from 'node:fs/promises';
 
const result = await generateSpeech({
  model: 'microsoft/mai-voice-2-flash',
  text: 'Thanks for calling! Your order shipped this morning.',
  voice: 'en-US-Ethan',
  outputFormat: 'mp3',
  providerOptions: {
    azure: { style: 'excited', styleDegree: 1.5 },
  },
});
 
await writeFile('order-update.mp3', result.audio.uint8Array);
 
console.log('Saved order-update.mp3');
generate-microsoft-speech.js
import { generateSpeech } from 'ai';
import { writeFile } from 'node:fs/promises';
 
const result = await generateSpeech({
  model: 'microsoft/mai-voice-2-flash',
  text: 'Thanks for calling! Your order shipped this morning.',
  voice: 'en-US-Ethan',
  outputFormat: 'mp3',
  providerOptions: {
    azure: { style: 'excited', styleDegree: 1.5 },
  },
});
 
await writeFile('order-update.mp3', result.audio.uint8Array);
 
console.log('Saved order-update.mp3');

Microsoft returns MP3 audio by default. MAI-Voice also supports wav, pcm, and opus through outputFormat. When you omit voice, language picks a default voice for that language, such as de-DE-Mia for de. For more options, see the AI SDK Azure speech documentation.

OptionDescription
textThe text to convert to speech. Required.
voiceThe voice to use, such as alloy. Available voices depend on the model.
outputFormatThe audio format, such as mp3 or wav.
instructionsDirections for how the model should speak, such as tone or pacing.
speedPlayback speed. Defaults to 1.
languageThe language of the input text.

Support for each option varies by model. Unsupported options are reported in warnings on the result instead of failing the request.

You can also call the speech endpoint directly. Send a POST request with the model in the ai-model-id header. The response contains the audio as a base64-encoded string:

generate-speech-rest.ts
import { writeFile } from 'node:fs/promises';
 
const response = await fetch(
  'https://ai-gateway.vercel.sh/v4/ai/speech-model',
  {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${process.env.AI_GATEWAY_API_KEY}`,
      'ai-gateway-protocol-version': '0.0.1',
      'ai-speech-model-specification-version': '4',
      'ai-model-id': 'openai/tts-1',
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      text: 'Hello! Thanks for trying out AI Gateway.',
      voice: 'alloy',
      outputFormat: 'mp3',
    }),
  },
);
 
const result = await response.json();
await writeFile('greeting.mp3', Buffer.from(result.audio, 'base64'));
 
console.log("Saved greeting.mp3");
request.py
import json
import os
import urllib.request
 
request = urllib.request.Request(
    'https://ai-gateway.vercel.sh/v4/ai/speech-model',
    data=json.dumps({'text': 'Hello! Thanks for trying out AI Gateway.', 'voice': 'alloy', 'outputFormat': 'mp3'}).encode(),
    headers={'Authorization': "Bearer " + os.environ["AI_GATEWAY_API_KEY"], 'ai-gateway-protocol-version': '0.0.1', 'ai-speech-model-specification-version': '4', 'ai-model-id': 'openai/tts-1', 'Content-Type': 'application/json'},
)
with urllib.request.urlopen(request) as response:
    print(json.load(response))
generate-speech.sh
set -euo pipefail
 
curl --fail-with-body -X POST https://ai-gateway.vercel.sh/v4/ai/speech-model \
  -H "Authorization: Bearer $AI_GATEWAY_API_KEY" \
  -H "ai-gateway-protocol-version: 0.0.1" \
  -H "ai-speech-model-specification-version: 4" \
  -H "ai-model-id: openai/tts-1" \
  -H "Content-Type: application/json" \
  -d '{
    "text": "Hello! Thanks for trying out AI Gateway.",
    "voice": "alloy",
    "outputFormat": "mp3"
  }' | jq -r '.audio' | base64 -d > greeting.mp3
 
if [ -s greeting.mp3 ]; then echo "Saved greeting.mp3"; else exit 1; fi

The response is a JSON object with the base64-encoded audio:

response.json
{
  "audio": "SUQzBAAAAAAA...",
  "warnings": []
}
  • Audio returns base64-encoded in a JSON response. Streaming audio output is not supported.
  • Google speech models don't support speed or language. Set pacing through instructions since the model detects the language from the text.
  • Microsoft speech models don't support instructions. Set the delivery with providerOptions.azure.style instead.
  • Microsoft speech models reject unknown voices and unsupported styles.
Last updated September 8, 2026

Was this helpful?

supported.