> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://developers.deepgram.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://developers.deepgram.com/_mcp/server.

# Getting Started

This guide will walk you through how to turn streaming text into speech with Deepgram's text-to-speech Websocket API.

> **Info**
>
> Before you start, you'll need to follow the steps in the [Make Your First API Request](/guides/fundamentals/make-your-first-api-request) guide to obtain a Deepgram API key, and configure your environment if you are choosing to use a Deepgram SDK.

## Text-to-Speech Implementations

Deepgram has several SDKs that can make the API easier to use. Follow these steps to use the SDK of your choice to make a Deepgram TTS request.

### Add Dependencies

**`JavaScript`**

```shell JavaScript
# Install the SDK
npm install @deepgram/sdk

# Add the dependencies
npm install dotenv
```

**`Python`**

```shell Python
# Install the SDK
pip install deepgram-sdk

# Install dependencies required by the examples
pip install python-dotenv
```

**`Go`**

```shell Go
# Install the SDK
go get github.com/deepgram/deepgram-go-sdk

# Importing the Deepgram Go SDK should pull in all dependencies required
```

**`C#`**

```shell C#
# Install the SDK
dotnet add package Deepgram

# Importing the Deepgram .NET SDK should pull in all dependencies required
```

**`Java`**

```shell Java
# Install the Deepgram Java SDK
# https://github.com/deepgram/deepgram-java-sdk

# Maven — add to pom.xml:
# <dependency>
#   <groupId>com.deepgram</groupId>
#   <artifactId>deepgram-java-sdk</artifactId>
#   <version>0.2.1</version>
# </dependency>

# Gradle — add to build.gradle:
# implementation 'com.deepgram:deepgram-java-sdk:0.7.0'
```

### Make the Request with the SDK

**`JavaScript`**

```javascript JavaScript
const fs = require("fs");
const { DeepgramClient } = require("@deepgram/sdk");

// Add a wav audio container header to the file if you want to play the audio
// using the AudioContext or media player like VLC, Media Player, or Apple Music
// Without this header in the Chrome browser case, the audio will not play.
// prettier-ignore
const wavHeader = [
0x52, 0x49, 0x46, 0x46, // "RIFF"
0x00, 0x00, 0x00, 0x00, // Placeholder for file size
0x57, 0x41, 0x56, 0x45, // "WAVE"
0x66, 0x6D, 0x74, 0x20, // "fmt "
0x10, 0x00, 0x00, 0x00, // Chunk size (16)
0x01, 0x00,             // Audio format (1 for PCM)
0x01, 0x00,             // Number of channels (1)
0x80, 0xBB, 0x00, 0x00, // Sample rate (48000)
0x00, 0xEE, 0x02, 0x00, // Byte rate (48000 * 2)
0x02, 0x00,             // Block align (2)
0x10, 0x00,             // Bits per sample (16)
0x64, 0x61, 0x74, 0x61, // "data"
0x00, 0x00, 0x00, 0x00  // Placeholder for data size
];

const live = async () => {
const text = "Hello, how can I help you today?";

const deepgram = new DeepgramClient({ apiKey: process.env.DEEPGRAM_API_KEY });

const dgConnection = await deepgram.speak.v1.connect({
  model: "aura-2-thalia-en",
  encoding: "linear16",
  sample_rate: 48000,
});

let audioBuffer = Buffer.from(wavHeader);

dgConnection.on("open", () => {
  console.log("Connection opened");

  // Send text data for TTS synthesis
  dgConnection.sendText({ type: "Text", text });

  dgConnection.on("close", () => {
    console.log("Connection closed");
  });

  dgConnection.on("message", (data) => {
    if (data.type === "Metadata") {
      console.dir(data, { depth: null });
    } else if (data.type === "Flushed") {
      console.log("Deepgram Flushed");
      // Write the buffered audio data to a file when the flush event is received
      writeFile();
    } else if (typeof data === "string") {
      console.log("Deepgram audio data received");
      // Concatenate the audio chunks into a single buffer
      const buffer = Buffer.from(data, "base64");
      audioBuffer = Buffer.concat([audioBuffer, buffer]);
    }
  });

  dgConnection.on("error", (err) => {
    console.error(err);
  });
});

dgConnection.connect();
await dgConnection.waitForOpen();

const writeFile = () => {
  if (audioBuffer.length > 0) {
    fs.writeFile("output.wav", audioBuffer, (err) => {
      if (err) {
        console.error("Error writing audio file:", err);
      } else {
        console.log("Audio file saved as output.wav");
      }
    });
    audioBuffer = Buffer.from(wavHeader); // Reset buffer after writing
  }
};
};

live();
```

**`Python`**

```python Python
# For more Python SDK migration guides, visit:
# https://github.com/deepgram/deepgram-python-sdk/tree/main/docs

import time
import wave

from deepgram import (
    DeepgramClient,
)
from deepgram.core.events import EventType
from deepgram.speak.v1.types import SpeakV1Text

AUDIO_FILE = "output.wav"
TTS_TEXT = "Hello, this is a text to speech example using Deepgram. How are you doing today? I am fine thanks for asking."

def main():
    try:
        # use default config
        deepgram: DeepgramClient = DeepgramClient()

        # Generate a generic WAV container header
        # since we don't support containerized audio, we need to generate a header
        header = wave.open(AUDIO_FILE, "wb")
        header.setnchannels(1)  # Mono audio
        header.setsampwidth(2)  # 16-bit audio
        header.setframerate(16000)  # Sample rate of 16000 Hz
        header.close()

        # Create a websocket connection to Deepgram
        with deepgram.speak.v1.connect(
            model="aura-2-thalia-en",
            encoding="linear16",
            sample_rate=16000
        ) as connection:
            def on_message(message) -> None:
                if isinstance(message, bytes):
                    print("Received binary data")
                    with open(AUDIO_FILE, "ab") as f:
                        f.write(message)
                        f.flush()
                else:
                    msg_type = getattr(message, "type", "Unknown")
                    print(f"Received {msg_type} event")

            connection.on(EventType.OPEN, lambda _: print("Connection opened"))
            connection.on(EventType.MESSAGE, on_message)
            connection.on(EventType.CLOSE, lambda _: print("Connection closed"))
            connection.on(EventType.ERROR, lambda error: print(f"Error: {error}"))

            connection.start_listening()

            # Send text to be converted to speech
            connection.send_text(SpeakV1Text(text=TTS_TEXT))

            # Flush to generate the audio
            connection.send_flush()

            # Indicate that we've finished
            time.sleep(7)
            print("\n\nPress Enter to stop...\n\n")
            input()

            connection.send_close()

            print("Finished")

    except ValueError as e:
        print(f"Invalid value encountered: {e}")
    except Exception as e:
        print(f"An unexpected error occurred: {e}")

if __name__ == "__main__":
    main()
```

**`Go`**

```go Go
package main

import (
	"context"
	"fmt"
	"os"
	"strings"

	msginterfaces "github.com/deepgram/deepgram-go-sdk/pkg/api/speak/v1/websocket/interfaces"
	interfaces "github.com/deepgram/deepgram-go-sdk/pkg/client/interfaces/v1"
	speak "github.com/deepgram/deepgram-go-sdk/pkg/client/speak"
)

const (
	TTS_TEXT   = "Hello, this is a text to speech example using Deepgram."
	AUDIO_FILE = "output.mp3"
)

// Implement your own callback
type MyCallback struct{}

func (c MyCallback) Metadata(md *msginterfaces.MetadataResponse) error {
	fmt.Printf("\n[Metadata] Received\n")
	fmt.Printf("Metadata.RequestID: %s\n", strings.TrimSpace(md.RequestID))
	return nil
}

func (c MyCallback) Binary(byMsg []byte) error {
	fmt.Printf("\n[Binary] Received\n")

	file, err := os.OpenFile(AUDIO_FILE, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0o666)
	if err != nil {
		fmt.Printf("Error creating file %s: %v\n", AUDIO_FILE, err)
		return err
	}
	defer file.Close()

	_, err = file.Write(byMsg)
	if err != nil {
		fmt.Printf("Error writing audio data to file: %v\n", err)
		return err
	}

	fmt.Printf("Audio data saved to %s\n", AUDIO_FILE)
	return nil
}

func (c MyCallback) Flush(fl *msginterfaces.FlushedResponse) error {
	fmt.Printf("\n[Flushed] Received\n")
	return nil
}

func (c MyCallback) Warning(wr *msginterfaces.WarningResponse) error {
	fmt.Printf("\n[Warning] Received\n")
	fmt.Printf("Warning.Code: %s\n", wr.WarnCode)
	fmt.Printf("Warning.Description: %s\n\n", wr.WarnMsg)
	return nil
}

func (c MyCallback) Error(er *msginterfaces.ErrorResponse) error {
	fmt.Printf("\n[Error] Received\n")
	fmt.Printf("Error.Code: %s\n", er.ErrCode)
	fmt.Printf("Error.Description: %s\n\n", er.ErrMsg)
	return nil
}

func (c MyCallback) Close(cr *msginterfaces.CloseResponse) error {
	fmt.Printf("\n[Close] Received\n")
	return nil
}

func (c MyCallback) Open(or *msginterfaces.OpenResponse) error {
	fmt.Printf("\n[Open] Received\n")
	return nil
}

func main() {
	// print instructions
	fmt.Print("\n\nPress ENTER to exit!\n\n")

	// init library
	speak.InitWithDefault()

	// Go context
	ctx := context.Background()

	// set the TTS options
	ttsOptions := &interfaces.SpeakOptions{
		Model: "aura-2-thalia-en",
	}

	// create the callback
	callback := MyCallback{}

	// create a new stream using the NewStream function
	dgClient, err := speak.NewWebSocketWithDefaults(ctx, ttsOptions, callback)
	if err != nil {
		fmt.Println("ERROR creating TTS connection:", err)
		return
	}

	// connect the websocket to Deepgram
	bConnected := dgClient.Connect()
	if !bConnected {
		fmt.Println("Client.Connect failed")
		os.Exit(1)
	}

	// Send the text input
	err = dgClient.SpeakWithText(TTS_TEXT)
	if err != nil {
		fmt.Printf("Error sending text input: %v\n", err)
		return
	}

	// Flush the text input
	err = dgClient.Flush()
	if err != nil {
		fmt.Printf("Error sending text input: %v\n", err)
		return
	}

	// wait for user input to exit
	input := bufio.NewScanner(os.Stdin)
	input.Scan()

	// close the connection
	dgClient.Stop()

	fmt.Printf("Program exiting...\n")
}
```

**`Java`**

```java Java
// Main.java (Java example)
// https://github.com/deepgram/deepgram-java-sdk

import java.io.FileOutputStream;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import com.deepgram.DeepgramClient;
import com.deepgram.resources.speak.v1.types.SpeakV1Close;
import com.deepgram.resources.speak.v1.types.SpeakV1CloseType;
import com.deepgram.resources.speak.v1.types.SpeakV1Flush;
import com.deepgram.resources.speak.v1.types.SpeakV1FlushType;
import com.deepgram.resources.speak.v1.types.SpeakV1Text;
import com.deepgram.resources.speak.v1.websocket.V1WebSocketClient;

public class Main {
    public static void main(String[] args) throws Exception {
        DeepgramClient client = DeepgramClient.builder().build();
        V1WebSocketClient wsClient = client.speak().v1().v1WebSocket();
        CountDownLatch closeLatch = new CountDownLatch(1);

        FileOutputStream audioOutput = new FileOutputStream("output.wav");

        wsClient.onSpeakV1Audio(audioData -> {
            try {
                audioOutput.write(audioData.toByteArray());
            } catch (Exception e) {
                System.err.println("Error writing audio: " + e.getMessage());
            }
        });

        wsClient.onFlushed(flushed -> System.out.println("Flush complete"));
        wsClient.onDisconnected(reason -> {
            try { audioOutput.close(); } catch (Exception ignored) {}
            closeLatch.countDown();
        });

        CompletableFuture<Void> connectFuture = wsClient.connect();
        connectFuture.get(10, TimeUnit.SECONDS);

        // Send text chunks
        wsClient.sendText(SpeakV1Text.builder().text("Hello, how can I help you today?").build());

        // Flush to trigger audio generation
        wsClient.sendFlush(SpeakV1Flush.builder().type(SpeakV1FlushType.FLUSH).build());

        Thread.sleep(5000);

        wsClient.sendClose(SpeakV1Close.builder().type(SpeakV1CloseType.CLOSE).build());
        closeLatch.await(10, TimeUnit.SECONDS);
        wsClient.disconnect();

        System.out.println("Audio saved to output.wav");
    }
}
```

**`C#`**

```csharp C#
using Deepgram.Models.Authenticate.v1;
using Deepgram.Models.Speak.v2.WebSocket;
using Deepgram.Logger;

namespace SampleApp
{
    class Program
    {
        static async Task Main(string[] args)
        {
            try
            {
                // Initialize Library with default logging
                // Normal logging is "Info" level
                Library.Initialize();

                // use the client factory with a API Key set with the "DEEPGRAM_API_KEY" environment variable
                var speakClient = ClientFactory.CreateSpeakWebSocketClient();

                // append wav header only once
                bool appendWavHeader = true;

                // Subscribe to the EventResponseReceived event
                await speakClient.Subscribe(new EventHandler<AudioResponse>((sender, e) =>
                {
                    Console.WriteLine($"----> {e.Type} received");

                    // add a wav header
                    if (appendWavHeader)
                    {
                        using (BinaryWriter writer = new BinaryWriter(File.Open("output.wav", FileMode.Append)))
                        {
                            Console.WriteLine("Adding WAV header to output.wav");
                            byte[] wavHeader = new byte[44];
                            int sampleRate = 48000;
                            short bitsPerSample = 16;
                            short channels = 1;
                            int byteRate = sampleRate * channels * (bitsPerSample / 8);
                            short blockAlign = (short)(channels * (bitsPerSample / 8));

                            wavHeader[0] = 0x52; // R
                            wavHeader[1] = 0x49; // I
                            wavHeader[2] = 0x46; // F
                            wavHeader[3] = 0x46; // F
                            wavHeader[4] = 0x00; // Placeholder for file size (will be updated later)
                            wavHeader[5] = 0x00; // Placeholder for file size (will be updated later)
                            wavHeader[6] = 0x00; // Placeholder for file size (will be updated later)
                            wavHeader[7] = 0x00; // Placeholder for file size (will be updated later)
                            wavHeader[8] = 0x57; // W
                            wavHeader[9] = 0x41; // A
                            wavHeader[10] = 0x56; // V
                            wavHeader[11] = 0x45; // E
                            wavHeader[12] = 0x66; // f
                            wavHeader[13] = 0x6D; // m
                            wavHeader[14] = 0x74; // t
                            wavHeader[15] = 0x20; // Space
                            wavHeader[16] = 0x10; // Subchunk1Size (16 for PCM)
                            wavHeader[17] = 0x00; // Subchunk1Size
                            wavHeader[18] = 0x00; // Subchunk1Size
                            wavHeader[19] = 0x00; // Subchunk1Size
                            wavHeader[20] = 0x01; // AudioFormat (1 for PCM)
                            wavHeader[21] = 0x00; // AudioFormat
                            wavHeader[22] = (byte)channels; // NumChannels
                            wavHeader[23] = 0x00; // NumChannels
                            wavHeader[24] = (byte)(sampleRate & 0xFF); // SampleRate
                            wavHeader[25] = (byte)((sampleRate >> 8) & 0xFF); // SampleRate
                            wavHeader[26] = (byte)((sampleRate >> 16) & 0xFF); // SampleRate
                            wavHeader[27] = (byte)((sampleRate >> 24) & 0xFF); // SampleRate
                            wavHeader[28] = (byte)(byteRate & 0xFF); // ByteRate
                            wavHeader[29] = (byte)((byteRate >> 8) & 0xFF); // ByteRate
                            wavHeader[30] = (byte)((byteRate >> 16) & 0xFF); // ByteRate
                            wavHeader[31] = (byte)((byteRate >> 24) & 0xFF); // ByteRate
                            wavHeader[32] = (byte)blockAlign; // BlockAlign
                            wavHeader[33] = 0x00; // BlockAlign
                            wavHeader[34] = (byte)bitsPerSample; // BitsPerSample
                            wavHeader[35] = 0x00; // BitsPerSample
                            wavHeader[36] = 0x64; // d
                            wavHeader[37] = 0x61; // a
                            wavHeader[38] = 0x74; // t
                            wavHeader[39] = 0x61; // a
                            wavHeader[40] = 0x00; // Placeholder for data chunk size (will be updated later)
                            wavHeader[41] = 0x00; // Placeholder for data chunk size (will be updated later)
                            wavHeader[42] = 0x00; // Placeholder for data chunk size (will be updated later)
                            wavHeader[43] = 0x00; // Placeholder for data chunk size (will be updated later)

                            writer.Write(wavHeader);
                            appendWavHeader = false;
                        }
                    }

                    if (e.Stream != null)
                    {
                        using (BinaryWriter writer = new BinaryWriter(File.Open("output.wav", FileMode.Append)))
                        {
                            writer.Write(e.Stream.ToArray());
                        }
                    }
                }));

                // Start the connection
                var speakSchema = new SpeakSchema()
                {
                    Encoding = "linear16",
                    SampleRate = 48000,
                    Model = "aura-2-thalia-en",
                };
                bool bConnected = await speakClient.Connect(speakSchema);
                if (!bConnected)
                {
                    Console.WriteLine("Failed to connect to the server");
                    return;
                }

                // Send some Text to convert to audio
                speakClient.SpeakWithText("Hello World!");

                //Flush the audio
                speakClient.Flush();

                // Wait for the user to press a key
                Console.WriteLine("\n\nPress any key to stop and exit...\n\n\n");
                Console.ReadKey();

                // Stop the connection
                await speakClient.Stop();

                // Terminate Libraries
                Library.Terminate();
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Exception: {ex.Message}");
            }
        }
    }
}
```

> **Info**
>
> To learn more, check out our audio format tips for websockets in the [TTS Chunking for Optimization Guide](/docs/text-chunking-for-tts-optimization) and our [Audio Format Combinations](/docs/tts-media-output-settings#audio-format-combinations) that we offer.

## Text-to-Speech Workflow

Below is a high-level workflow for obtaining an audio stream from user-provided text.

### Establish a WebSocket Connection

To establish a connection, you must provide a few parameters on the URL to describe the type of audio you want. You can check out the [API Reference](/reference/text-to-speech/speak-streaming) to set the audio model, which controls the voice, the encoding, and the sample rate of the audio.

### Sending Text and Retrieving Audio

Send the desired text to transform to audio using the WebSocket message below:

**`JSON`**

```json JSON
{
  "type": "Speak",
  "text": "Your text to transform to speech",
}
```

When you have queued enough text, you can obtain the corresponding audio by sending a `Flush` command.

**`JSON`**

```json JSON
{
  "type": "Flush"
}
```

Upon successfully sending the `Flush`, you will receive an audio byte stream from the websocket connection containing the synthesized text-to-speech. The format will be based on the encoding values provided upon establishing the connection.

### Closing the Connection

When you are finished with the WebSocket, you can close the connection by sending the following `Close` command.

**`JSON`**

```json JSON
{
  "type": "Close"
}
```

## Limits

Keep these limits in mind when making a Deepgram text-to-speech request.

### Use One WebSocket per Conversation

If you are building for conversational AI use cases where a human is talking to a TTS agent, a single websocket per conversation is required. After you establish a connection, you will not be able to change the voice or media output settings.

### Input Text Limit

Sending a request with a text payload longer than the maximum number of characters can result in a [**413: Input Text Exceeds Character Limits**](/docs/errors#413-input-text-exceeded-character-limit) error, and the audio file will not be created.

| Model          | Max Characters |
| -------------- | -------------- |
| Aura-2, Aura-1 | 2000           |

### Unprocessable Content

A [**422: Unprocessable Content**](/docs/errors#422-unprocessable-content) error can be returned if the client fails to send the request successfully.

### Character Throughput Limits

The throughput limit is 2400 characters per minute and is measured by the number of characters sent to the websocket.

### Timeout Limits

An active websocket has a 60-minute timeout period from the initial connection. This timeout exists for connections that are actively being used. If you desire a connection for longer than 60 minutes, create a new websocket connection to Deepgram.

### Flush Message Limits

The maximum number of times you can send the [Flush message](/docs/tts-ws-flush) is 20 times every 60 seconds. After that, you will receive a warning message stating that we cannot process any more flush messages until the 60-second time window has passed.

### Rate Limits

> **Info**
>
> For information on Deepgram's Concurrency Rate Limits, refer to our [API Rate Limits Documentation](/reference/api-rate-limits).

#### Handling Rate Limits

If the number of in-progress requests for a project meets or exceeds the rate limit, new requests will receive a **429: Too Many Requests** error.

> **Info**
>
> For suggestions on handling Concurrency Rate Limits, refer to our [Working with Concurrency Rate Limits Documentation](/docs/working-with-concurrency-rate-limits) guide.

## What's Next?

Now that you've transformed text into speech with Deepgram's API, enhance your knowledge by exploring the following areas.

### Read the Feature Guides

Deepgram's features help you customize your request to produce the best output for your use case. Here are a few guides that can help:

* [Websocket API Reference](/reference/text-to-speech/speak-streaming)
* [Text Chunking for TTS Optimization](/docs/text-chunking-for-tts-optimization)
* [Sending LLM Outputs to the Websocket](/docs/send-llm-outputs-to-the-tts-web-socket)