Skip to main content

Text to Speech

Convert text into natural speech, for example to read chatbot answers aloud.

ModeRequestInputOutput
Full textPOST https://api.deepseek.com/v1/tools/ttsThe complete text in a JSON bodyAudio streamed in the response body
StreamingWebSocket wss://api.deepseek.com/v1/tools/ttsText sent in chunks, for example while the model is still generatingAudio frames

Voices​

voice_idVoice
stellaStella. Female. Default
tideTide. Male

Languages​

Mandarin Chinese (zh), Cantonese (yue), English (en), Japanese (ja), Korean (ko), Malay (ms), Arabic (ar), Indonesian (id), Thai (th) and Vietnamese (vi).

There is no language parameter: the language is detected from the text.


Full Text Request​

Send the text as JSON:

FieldRequiredDescription
textYesThe text to read. Must not be empty, and at most 10,000 billed characters. The request body may be at most 64 KiB
voice_idNostella (default) or tide
formatNowav (default) or pcm
filter_markdownNoWhether to remove Markdown formatting before reading. Defaults to true
curl https://api.deepseek.com/v1/tools/tts \
-H "Authorization: Bearer <DeepSeek API Key>" \
-H "Content-Type: application/json" \
-d '{"text": "Hello from DeepSeek.", "voice_id": "stella"}' \
--output speech.wav

The audio is streamed as it is synthesized, and one request returns at most 30 minutes of audio. wav returns audio/wav; pcm returns raw 16-bit mono PCM at 24 kHz.

StatusCause
400There is no readable text, or the text did not pass content review (Content Exists Risk)
429Your account has too many requests in progress
503The service is busy. Retry later
504Synthesis timed out

If synthesis fails after the audio has started, the response is cut off, so treat a response that ends early as a failure.

Speed control and resuming an interrupted request are not supported.


Streaming over WebSocket​

  1. Connect to wss://api.deepseek.com/v1/tools/tts. Optional query parameters: voice_id, format (pcm by default, or opus) and filter_markdown. opus returns Opus packets without a container, which cannot be saved directly as an Ogg file.
  2. There are two ways to authenticate: send the header Authorization: Bearer <API key>, or pass the key as a WebSocket subprotocol, Sec-WebSocket-Protocol: tts, bearer.<API key>.
  3. Send the text as JSON text messages, {"event": "text", "text": "..."}, and send {"event": "finish_text"} after the last chunk.
  4. Audio arrives in binary messages. The session ends with a text message {"event": "finish", "code": "...", "message": "..."}, where code is success or an error code such as invalid_input, content_filter, input_timeout or service_error.

Limits: the session ends if no text arrives for 60 seconds; a session lasts at most 30 minutes; the text of one session may be at most 10,000 billed characters and 64 KiB. The server sends a WebSocket ping every 25 seconds.

In Python, with the websockets package:

# Set DEEPSEEK_API_KEY to your API key before running this example.
# Requires the websockets package, version 14 or later.
import asyncio
import json
import os

import websockets


async def main():
url = "wss://api.deepseek.com/v1/tools/tts?voice_id=stella&format=pcm"
headers = {"Authorization": f"Bearer {os.environ['DEEPSEEK_API_KEY']}"}
async with websockets.connect(url, additional_headers=headers) as ws:
for chunk in ["Hello ", "from ", "DeepSeek."]:
await ws.send(json.dumps({"event": "text", "text": chunk}))
await ws.send(json.dumps({"event": "finish_text"}))

with open("speech.pcm", "wb") as f:
async for message in ws:
if isinstance(message, bytes):
f.write(message) # raw 16-bit signed mono PCM at 24 kHz
continue
finish = json.loads(message)
if finish["code"] != "success":
raise RuntimeError(f"{finish['code']}: {finish['message']}")
break


asyncio.run(main())

Billing​

Billing unitList priceLimited-time price (50% off)
Per 10,000 characters$0.45$0.225

A Chinese character counts as 2 characters; any other character, such as a letter, digit or space, counts as 1.


Rate Limits​

An account can have at most 50 requests in progress; beyond that a request returns 429.