CosyVoice Python SDK
Use the DashScope Python SDK to integrate CosyVoice real-time speech synthesis into your application through non-streaming, one-way streaming, or bidirectional streaming modes.
Service endpoint
The SDK endpoint is determined by the workspace/region parameters of the Client constructor (corresponding to the addresses below, which include WorkspaceId). To switch to a different region, change the Client constructor parameters to the values for that region.
China (Beijing)
wss://{WorkspaceId}.cn-beijing.maas.aliyuncs.com/api-ws/v1/inference
Replace {WorkspaceId} with your actual workspace ID.
Singapore
wss://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/api-ws/v1/inference
Replace {WorkspaceId} with your actual workspace ID.
ImportantAlibaba Cloud Model Studio has released workspace-specific domains for the China (Beijing) and Singapore regions. The new dedicated domains deliver superior performance and higher stability for inference requests. We recommend migrating to the new domains:
- China (Beijing): from
dashscope.aliyuncs.comto{WorkspaceId}.cn-beijing.maas.aliyuncs.com - Singapore: from
dashscope-intl.aliyuncs.comto{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com
Core resource (client.audio.tts)
Resource path: client.audio.tts
Capabilities: WebSocket-based speech synthesis with three calling modes: one-shot synthesis (synthesize), streaming synthesis (stream), and bidirectional streaming sessions (connect).
Calling modes
# One-shot synthesis: returns the complete audio as bytes
audio = client.audio.tts.synthesize(
text, # text is required (positional parameter)
model="cosyvoice-v3-plus", # The model the voice belongs to
voice="longanyang", # The voice
format="mp3", # Audio encoding format. Default: "mp3"
sample_rate=22050, # Sample rate. Default: 22050
)
# Streaming synthesis: yields audio bytes chunk by chunk (write or play as you receive)
for chunk in client.audio.tts.stream(text, model=..., voice=...):
...
# Bidirectional streaming session: submit text in segments. Entering the with context establishes the connection automatically
with client.audio.tts.connect(model=..., voice=...) as session:
session.send_text(text) # Can be called multiple times to append text
session.stop() # Notifies the server that the input is complete
for chunk in session.audio_chunks():
...
synthesize() - non-streaming
Method signature:
def synthesize(self, text: str, *, model: str = "cosyvoice-v1",
voice: str = "longxiaochun", format: str = "mp3",
sample_rate: int = 22050, volume: int = None,
rate: float = None, pitch: float = None,
language_hints: list = None, enable_ssml: bool = None,
parameters: Dict = None, ...) -> bytes
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
text | str | Yes | The full text to synthesize. Maximum length: 20,000 characters. |
Return value: bytes containing the complete audio data.
Description: This blocking call waits and returns the complete audio data at once. It is best suited for short text where real-time streaming is not required. Each synthesize or stream call creates its own session; no instance reuse is required.
stream() - streaming
Method signature:
def stream(self, text: str, *, model: str, voice: str,
format: str = "mp3", sample_rate: int = 22050, ...) -> Iterator[bytes]
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
text | str | Yes | The full text to synthesize. Maximum length: 20,000 characters. |
Return value: Iterator[bytes] that yields audio data chunk by chunk.
Description: Streaming synthesis that sends the complete text at once and yields the audio chunk by chunk (Iterator[bytes]). Best suited for short-text scenarios that require low latency. To submit text in segments, use a bidirectional streaming session (connect).
send_text() - streaming input (session method)
Submits text in segments during a bidirectional streaming session (client.audio.tts.connect(...)).
Method signature:
def send_text(self, text: str) -> None
Parameters: text is the current text segment to synthesize. You can call send_text multiple times to append text. Maximum per call: 20,000 characters. Cumulative maximum: 200,000 characters. Sending text and receiving audio must run concurrently (use send_text together with audio_chunks()/events() and stop).
stop() - end streaming input (session method)
Method signature:
def stop(self) -> None
Description: Notifies the server that all text has been sent. This call does not block the current thread; the receiving thread must continue consuming audio_chunks()/events() until the remaining text is synthesized and all audio data is returned. Failing to call this method may result in trailing text not being converted to speech.
cancel() - cancel streaming synthesis (session method)
Method signature:
def cancel(self) -> None
Description: Cancels the current streaming speech synthesis turn. After calling this method, the SDK immediately ends the current task. After cancellation, the session can no longer send text. Create a new session to continue synthesis.
ImportantModel limitations:
- China (Beijing): CosyVoice models require v2 or later.
- Singapore: CosyVoice models do not support this feature.
Session metrics and response (session properties)
session.first_package_delay
session.usage
session.last_response
first_package_delay:float, the latency in milliseconds from sending text to receiving the first audio chunk. Read this after synthesis completes.usage:dict, the usage information of the most recent synthesis task.last_response:dict, the response message of the most recent synthesis task (containingheader/payloadkeys). The request ID of the most recent request can be retrieved from theheaderofsession.last_response, useful for troubleshooting and log correlation.
Request parameters
The following parameters are set through the synthesize/stream/connect methods of client.audio.tts to control the model, voice, format, and audio characteristics of the synthesized audio (model, voice, format, sample_rate, volume, rate, pitch, language_hints, and enable_ssml are named parameters; the other parameters are passed through the parameters dictionary).
| Parameter | Type | Required | Description |
|---|---|---|---|
model | str | Yes | The model name. |
voice | str | Yes | The voice used for speech synthesis.
|
format | str | No | Audio encoding format. Default: mp3. Supports mp3, wav, pcm, and other formats. The sample rate is set through the |
volume | int | No | The volume level. Default value: 50. Valid values: [0, 100]. |
rate | float | No | The speech rate. Default value: 1.0. Valid values: [0.5, 2.0]. |
pitch | float | No | The pitch. Default value: 1.0. Valid values: [0.5, 2.0]. |
bit_rate | int | No | The audio bit rate in kbps. When the audio format is mp3 or opus, use bit_rate to adjust the bit rate.Default value: 32. Valid values: [6, 510]. Set |
word_timestamp_enabled | bool | No | Specifies whether to enable word-level timestamps. Default value: false. Available only in streaming output mode. Supported voices: cloned voices of cosyvoice-v3.5-plus, cosyvoice-v3.5-flash, cosyvoice-v3-flash, cosyvoice-v3-plus, cosyvoice-v2, and system voices marked as supported in CosyVoice Voice list. Cloned voices of other models do not support this feature. Set |
seed | int | No | A random seed for controlling variation in the synthesis output. When the model version, text, voice, and other parameters are unchanged, using the same seed produces identical results. Default value: 0. Valid values: [0, 65535]. |
language_hints | list[str] | No | Important
Specifies the target language for speech synthesis to improve output quality. When digit pronunciation, abbreviation expansion, symbol reading, or minority-language synthesis doesn't meet expectations, use this parameter. For example:
Valid values
|
instruction | str | No | Controls synthesis characteristics such as dialect, emotion, or speaking style. For usage details, see Instruction control. |
enable_aigc_tag | bool | No | Specifies whether to embed an AIGC watermark in the generated audio. When set to true, the watermark is embedded in audio files of supported formats (wav/mp3/opus). Default value: false. NoteOnly cosyvoice-v3-flash, cosyvoice-v3-plus, cosyvoice-v2 support this feature. Set |
aigc_propagator | str | No | Sets the ContentPropagator field in the AIGC watermark, identifying the content propagator. Takes effect only when enable_aigc_tag is true.Default value: Alibaba Cloud UID. NoteOnly cosyvoice-v3-flash, cosyvoice-v3-plus, cosyvoice-v2 support this feature. Set through the |
aigc_propagate_id | str | No | Sets the PropagateID field in the AIGC watermark, uniquely identifying a specific propagation action. Takes effect only when enable_aigc_tag is true.Default value: The request ID of the current speech synthesis request. NoteOnly cosyvoice-v3-flash, cosyvoice-v3-plus, cosyvoice-v2 support this feature. Set through the |
hot_fix | dict | No | Configures pronunciation corrections and text replacements applied before synthesis. NoteThis feature isn't supported by cosyvoice-v2. Parameters:
Example: |
enable_markdown_filter | bool | No | NoteOnly cloned voices of cosyvoice-v3-flash support this feature. Specifies whether to enable Markdown filtering. When enabled, the system automatically strips Markdown markup symbols from the input text before synthesis, preventing them from being read aloud. Default value: false. Valid values:
Set |
Session and event iteration
Streaming synthesis no longer uses callbacks: audio and server events are received through the session's iteration methods. stream()/synthesize() require no session management. The lifecycle of a bidirectional streaming session (client.audio.tts.connect(...)) is: entering the with context establishes the connection automatically → multiple send_text() calls → stop() → consume all results → exiting the context closes the connection automatically.
audio_chunks() - iterate audio data
Method signature:
def audio_chunks(self) -> Iterator[bytes]
Return value: Iterator[bytes] that yields binary audio data chunks one by one, in the format specified by the format parameter.
Description: Iterate in the receiving thread to receive audio data chunk by chunk; multiple chunks are yielded during synthesis. Write the data to a file or feed it to a playback device. Only one events() or audio_chunks() reader is allowed per session.
events() - iterate server events
Method signature:
def events(self) -> Generator[Union[Dict, bytes], None, None]
Return value: Yields both server response events (dict containing header (request information) and payload (output information)) and audio bytes. The payload.output field contains event type, original text, and other details. See output field in events() messages.
Description: Iterate in the receiving thread over server responses, which contain the output information of synthesis events (event type, original text, sentence information, etc.). You can access payload.output directly for details.
Error handling
When an error occurs during synthesis, the WebSocket layer raises a DashScopeError exception, and the connection closes automatically after the exception is raised. Use try/except where you send or receive to catch and log errors for troubleshooting:
from dashscope.v2 import DashScopeError
try:
...
except DashScopeError as e:
print(e.code, e.message, e.request_id)
Session lifecycle
- The connection is established automatically when entering the
withcontext (you can initialize audio output streams or open file resources beforehand). - After all text has been synthesized and all audio data has been yielded, the
events()/audio_chunks()iteration ends. Performance metrics such asfirst_package_delaycan then be read. - The connection closes automatically when exiting the
withcontext (you can release resources such as audio playback devices afterwards).
output field in events() messages
The event dict yielded by the session's events() iteration contains a payload.output field with synthesis event output information. Use this field to track synthesis progress and retrieve per-sentence details. The structure of the output field is as follows:
| Field | Type | Description |
|---|---|---|
type | str | Event type. Values: sentence-begin (sentence synthesis started), sentence-synthesis (sentence synthesis in progress), or sentence-end (sentence synthesis finished). |
original_text | str | The original text of the current sentence. Returned in sentence-begin and sentence-end events. |
sentence | dict | Sentence information. Contains index (sentence sequence number) and words (word list with timestamp information when word_timestamp_enabled is on). |
Message example:
{
"header": {
"task_id": "xxx",
"event": "result-generated",
"attributes": {}
},
"payload": {
"output": {
"type": "sentence-begin",
"original_text": "How is the weather today?",
"sentence": {
"index": 0,
"words": []
}
}
}
}
Parsing example:
def handle_event(event):
output = (event.get('payload') or {}).get('output') or {}
event_type = output.get('type', '')
original_text = output.get('original_text', '')
if event_type:
print(f'Event type: {event_type}, Original text: {original_text}')
Code examples
The SDK provides the key interfaces for speech synthesis and supports the following calling modes:
- Non-streaming: A blocking call that sends the complete text at once and returns the full audio directly. Best suited for short-text speech synthesis.
- Unidirectional streaming: A non-blocking call that sends the complete text at once and receives data by iterating over the audio chunks returned by
stream. Best suited for short-text scenarios that require low latency. - Bidirectional streaming: A non-blocking call that sends text in multiple segments and receives incrementally synthesized audio in real time by iterating the session's
audio_chunks()/events(). Best suited for long-text scenarios that require low latency.
For more examples, see GitHub.
Non-streaming
The text sent in a single call must not exceed 20,000 characters. Exceeding this limit raises a DashScopeError exception.
ImportantEach synthesize or stream call creates its own session. No instance reuse is required.
# coding=utf-8
import os
import dashscope
# The API Keys for Singapore and Beijing regions are different. Get API Key: https://help.aliyun.com/model-studio/get-api-key
# If the environment variable is not configured, use your Model Studio API Key instead: api_key="sk-xxx"
# The following configuration is for the China (Beijing) region. Replace "{WorkspaceId}" with your actual workspace ID. The configuration varies by region.
client = dashscope.Client(
api_key=os.environ.get('DASHSCOPE_API_KEY'),
workspace="{WorkspaceId}",
region="cn-beijing",
)
# Model
model = "cosyvoice-v3-plus"
# Voice
voice = "longanyang"
# Send text for synthesis and get binary audio
audio = client.audio.tts.synthesize("How is the weather today?", model=model, voice=voice)
# Save audio to local file
with open('output.mp3', 'wb') as f:
f.write(audio)
dashscope CLI
export DASHSCOPE_API_KEY="your-api-key"
# Replace {WorkspaceId} with your Workspace ID, and cn-beijing with the corresponding region (Singapore: ap-southeast-1, US East: us-east-1)
export DASHSCOPE_HTTP_BASE_URL="https://{WorkspaceId}.cn-beijing.maas.aliyuncs.com/api/v1"
dashscope speech-synthesis create -m cosyvoice-v2 -t "Hello world" --voice longxiaochun_v2
ImportantThe SDK Expert interactive assistant can accomplish the same development and troubleshooting in natural language. See
DashScope SDK Expert.For the complete region table, see Base URL overview.
One-way streaming
The text sent in a single call must not exceed 20,000 characters. Exceeding this limit raises a DashScopeError exception.
ImportantEach synthesize or stream call creates its own session. No instance reuse is required.
# coding=utf-8
import os
from datetime import datetime
import dashscope
def get_timestamp():
now = datetime.now()
formatted_timestamp = now.strftime("[%Y-%m-%d %H:%M:%S.%f]")
return formatted_timestamp
# The API Keys for Singapore and Beijing regions are different. Get API Key: https://help.aliyun.com/model-studio/get-api-key
# If the environment variable is not configured, use your Model Studio API Key instead: api_key="sk-xxx"
# The following configuration is for the China (Beijing) region. Replace "{WorkspaceId}" with your actual workspace ID. The configuration varies by region.
client = dashscope.Client(
api_key=os.environ.get('DASHSCOPE_API_KEY'),
workspace="{WorkspaceId}",
region="cn-beijing",
)
# Model
model = "cosyvoice-v3-plus"
# Voice
voice = "longanyang"
# Send the complete text at once and receive binary audio chunk by chunk
with open("output.mp3", "wb") as f:
for chunk in client.audio.tts.stream("How is the weather today?", model=model, voice=voice):
print(get_timestamp() + " Binary audio length: " + str(len(chunk)))
f.write(chunk)
print("Speech synthesis completed, all results received: " + get_timestamp())
Bidirectional streaming
The text sent per call must not exceed 20,000 characters. The cumulative text must not exceed 200,000 characters.
-
During streaming input, call
send_textmultiple times to submit text segments in sequence. The server automatically performs sentence segmentation on received text:- Complete sentences are synthesized immediately
- Incomplete sentences are buffered until complete
When
stopis called, the server force-synthesizes all received but unprocessed text (including incomplete sentences). -
The interval between text segments must not exceed 23 seconds. Otherwise, a "request timeout after 23 seconds" exception is raised.
If there's no text to send, call
stoppromptly to end the task.ImportantAlways call
stop. Failing to do so may cause trailing text to not be converted to speech.The server enforces a 23-second timeout. This value can't be modified on the client side.
# coding=utf-8
#
# pyaudio installation instructions:
# For macOS, run the following commands:
# brew install portaudio
# pip install pyaudio
# For Debian/Ubuntu, run the following commands:
# sudo apt-get install python-pyaudio python3-pyaudio
# or
# pip install pyaudio
# For CentOS, run the following commands:
# sudo yum install -y portaudio portaudio-devel && pip install pyaudio
# For Microsoft Windows, run the following command:
# python -m pip install pyaudio
import os
import time
import pyaudio
from concurrent.futures import ThreadPoolExecutor
import dashscope
from datetime import datetime
def get_timestamp():
now = datetime.now()
formatted_timestamp = now.strftime("[%Y-%m-%d %H:%M:%S.%f]")
return formatted_timestamp
# The API Keys for Singapore and Beijing regions are different. Get API Key: https://help.aliyun.com/model-studio/get-api-key
# If the environment variable is not configured, use your Model Studio API Key instead: api_key="sk-xxx"
# The following configuration is for the China (Beijing) region. Replace "{WorkspaceId}" with your actual workspace ID. The configuration varies by region.
client = dashscope.Client(
api_key=os.environ.get('DASHSCOPE_API_KEY'),
workspace="{WorkspaceId}",
region="cn-beijing",
)
# Model
model = "cosyvoice-v3-plus"
# Voice
voice = "longanyang"
test_text = [
"Streaming text-to-speech SDK,",
"converts input text",
"into binary audio data.",
"Compared with non-streaming speech synthesis,",
"streaming synthesis offers better real-time performance.",
"Users hear near-synchronous audio output while typing,",
"greatly improving interaction experience",
"and reducing wait time.",
"Ideal for large language model (LLM) integration,",
"where text is streamed for speech synthesis.",
]
# Create a bidirectional streaming synthesis session: entering the with context establishes the connection automatically
with client.audio.tts.connect(
model=model,
voice=voice,
format="pcm",
sample_rate=22050,
) as session:
# Initialize the player
player = pyaudio.PyAudio()
stream = player.open(format=pyaudio.paInt16, channels=1, rate=22050, output=True)
# Receiving thread: iterate audio data and play it in real time
def receive_audio():
for chunk in session.audio_chunks():
print(get_timestamp() + " Binary audio length: " + str(len(chunk)))
stream.write(chunk)
with ThreadPoolExecutor(max_workers=1) as pool:
receiver = pool.submit(receive_audio)
# Stream the text to synthesize
for text in test_text:
session.send_text(text)
time.sleep(0.1)
# End streaming speech synthesis
session.stop()
receiver.result()
print("Speech synthesis completed, all results received: " + get_timestamp())
# Stop the player
stream.stop_stream()
stream.close()
player.terminate()
# The first text submission requires establishing a WebSocket connection, so the first packet latency includes the connection setup time
print('[Metric] first packet latency: {} ms'.format(session.first_package_delay))