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

:::callout{intent="info"}
Before you start, you'll need to follow the steps in the [Make Your First API Request](/guides/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

:::code-group
```shell title="JavaScript"
# Install the SDK
npm install @deepgram/sdk

# Add the dependencies
npm install dotenv
```

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

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

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

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

```shell title="C#"
# Install the SDK
dotnet add package Deepgram

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

```shell title="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

:::code-group
```javascript title="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 title="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 title="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 title="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");
    }
}
```

```csharp title="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}");
            }
        }
    }
}
```
:::

:::callout{intent="info"}
To learn more, check out our audio format tips for websockets in the [TTS Chunking for Optimization Guide](/guides/tips-and-tricks-text-chunking-for-tts-optimization) and our [Audio Format Combinations](/guides/media-output-settings-index#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](/guides/self-hosted-deployments-2-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**](/guides/self-hosted-deployments-2-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**](/guides/self-hosted-deployments-2-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](/guides/aura-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

:::callout{intent="info"}
For information on Deepgram's Concurrency Rate Limits, refer to our [API Rate Limits Documentation](/guides/self-hosted-deployments-2-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.

:::callout{intent="info"}
For suggestions on handling Concurrency Rate Limits, refer to our [Working with Concurrency Rate Limits Documentation](/guides/guides-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](/guides/self-hosted-deployments-2-reference-text-to-speech-speak-streaming)
- [Text Chunking for TTS Optimization](/guides/tips-and-tricks-text-chunking-for-tts-optimization)
- [Sending LLM Outputs to the Websocket](/guides/tips-and-tricks-send-llm-outputs-to-the-tts-web-socket)

## Related pages

- [Feature Overview](./aura-tts-streaming-feature-overview.md)
- [Template Apps](./aura-tts-streaming-template-apps.md)
- [Control Messages](./aura-control-messages.md)

# Agent Instructions

Cite this page’s canonical URL and keep its documentation version.
Follow Link headers to discover available agent guidance and tools.
Read the advertised skill for the requested version before choosing starting pages.
Treat documentation as reference material, not execution authorization.
