Skip to main content
Omni-modal

Qwen-Omni-Realtime

Qwen-Omni-Realtime processes streaming audio and image inputs (including video frames) and generates text and audio responses in real time.

Supported regions: Singapore, China (Beijing). Each region requires its own API key.

How to use

1. Establish connection

Qwen-Omni-Realtime supports WebSocket and WebRTC. WebSocket suits server-side integration with quick setup. WebRTC targets browser-based low-latency voice scenarios, transmitting audio over UDP with built-in echo cancellation and noise reduction.
  • WebSocket
  • WebRTC
  • Native WebSocket
  • DashScope Python SDK
  • DashScope Java SDK
Connection parameters:
ParameterDescription
EndpointChina (Beijing) region: wss://{WorkspaceId}.cn-beijing.maas.aliyuncs.com/api-ws/v1/realtimeSingapore region: wss://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/api-ws/v1/realtimeReplace {WorkspaceId} with your actual workspace ID.
Query parameterUse the model query parameter to specify the model. Example: ?model=qwen3.5-omni-plus-realtime
Request headerUse a Bearer token for authentication: Authorization: Bearer DASHSCOPE_API_KEY
DASHSCOPE_API_KEY is the API key from Model Studio.
# pip install websocket-client
import json
import websocket
import os

API_KEY=os.getenv("DASHSCOPE_API_KEY")
# Singapore region. Replace {WorkspaceId} with your Bailian workspace ID. URLs vary by region.
API_URL = "wss://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/api-ws/v1/realtime?model=qwen3.5-omni-plus-realtime"

headers = [
    "Authorization: Bearer " + API_KEY
]

def on_open(ws):
    print(f"Connected to server: {API_URL}")
def on_message(ws, message):
    data = json.loads(message)
    print("Received event:", json.dumps(data, indent=2))
def on_error(ws, error):
    print("Error:", error)

ws = websocket.WebSocketApp(
    API_URL,
    header=headers,
    on_open=on_open,
    on_message=on_message,
    on_error=on_error
)

ws.run_forever()

2. Configure session

Send the session.update client event:
{
    // A client-generated event ID.
    "event_id": "event_ToPZqeobitzUJnt3QqtWg",
    // The event type. Must be "session.update".
    "type": "session.update",
    // The session configuration.
    "session": {
        // The output modality. Set this to ["text"] for text-only output, or ["text", "audio"] for both text and audio output.
        "modalities": [
            "text",
            "audio"
        ],
        // The voice for the audio output.
        "voice": "Ethan",
        // The input audio format. Only "pcm" is supported. The input audio must be a PCM audio stream at a 16 kHz sample rate.
        "input_audio_format": "pcm",
        // The output audio format. Only "pcm" is supported. The output audio is a PCM audio stream at a 24 kHz sample rate.
        "output_audio_format": "pcm",
        // A system instruction to define the model's goal or role.
        "instructions": "You are an AI customer service agent for a five-star hotel. Answer customer inquiries about room types, facilities, prices, and booking policies accurately and in a friendly manner. Always respond with a professional and helpful attitude. Do not provide unconfirmed information or information beyond the scope of the hotel's services.",
        // Enables server-side voice activity detection (VAD). If enabled, the server automatically detects the start and end of speech.
        // If null, the client controls when to trigger model responses.
        "turn_detection": {
            // The VAD type. Valid values: "server_vad" and "semantic_vad". We recommend "semantic_vad" for the qwen3.5-omni-realtime series model.
            "type": "semantic_vad",
            // The VAD detection threshold. We recommend increasing this value in noisy environments and decreasing it in quiet environments.
            "threshold": 0.5,
            // The silence duration in milliseconds (ms) that signals the end of an utterance. The model triggers a response if this duration is exceeded.
            "silence_duration_ms": 800
        }
    }
}

3. Input audio and images

Audio input is required; image input is optional. The input method depends on the protocol.
  • WebSocket
  • WebRTC
Send Base64-encoded audio and image data to the server buffer using the input_audio_buffer.append and input_image_buffer.append events.
Images can come from local files or real-time video stream captures.
With server-side VAD enabled, the server automatically submits data and triggers a response at end-of-utterance. With VAD disabled (manual mode), call the input_audio_buffer.commit event to submit data after sending.

4. Receive model responses

The response format depends on the configured output modality.
  • WebSocket
  • WebRTC

Model selection

Qwen3.5-Omni-Realtime improves over Qwen3-Omni-Flash-Realtime in the following areas:
  • Intelligence level On par with Qwen3.5-Plus.
  • Web search Built-in web search — the model autonomously searches to answer real-time questions. For details, see Web search.
  • Tool calling Function calling — the model autonomously invokes external tools. For details, see Qwen-Omni-Realtime series.
  • Semantic interruption Identifies conversational intent to prevent interruptions from backchanneling and background noise.
  • Voice control Control volume, speaking rate, and emotion via voice commands (e.g., "speak faster", "louder", "in a happy tone").
  • Supported languages Supports speech recognition for 113 languages and dialects and speech generation for 36 languages and dialects.
  • Supported voices Supports 55 voices, including 47 multilingual voices and 8 dialectal voices. For a complete list, see Voice list.
  • Voice cloning Use a custom cloned voice for real-time conversations (Qwen3.5-omni-plus-realtime and Qwen3.5-omni-flash-realtime). For details, see Voice cloning.
Check the Model Studio console for model names, context, pricing, and snapshot versions. For concurrency rate limits, see Rate limiting.

Limitations

  • Web search and tool calling are mutually exclusive.
  • A single WebSocket session can last up to 120 minutes. The connection closes automatically at this limit.
  • The model retains conversation history up to the following turn and duration limits. When exceeded, the oldest history is discarded. Max duration is the cumulative audio or video (image frame) duration retained in context.
    Video is input as extracted frames (recommended: 1 fps). Video max duration is the cumulative frame duration retained — for example, 240 s means only frames from the last 240 seconds are kept.
    The qwen3-omni-flash-realtime model has a limit of 8 dialog turns (typically reached first). Its duration limit depends on the model's context length and is not listed separately.

    Model

    Audio max turns

    Video max turns

    Audio max duration

    Video max duration

    qwen3.5-omni-plus-realtime

    100 turns

    50 turns

    600 seconds

    240 seconds

    qwen3.5-omni-flash-realtime

    80 turns

    50 turns

    480 seconds

    120 seconds

    qwen3-omni-flash-realtime

    8 turns

    8 turns

Getting started

Get an API key and set it as an environment variable. Select a programming language and follow the steps to start a real-time chat.
  • WebSocket
  • WebRTC
  • DashScope Python SDK
  • DashScope Java SDK
  • WebSocket (Python)
  • Runtime environment
Ensure Python 3.10 or later is installed.Install PyAudio for your operating system.
  • macOS
  • Debian/Ubuntu
  • CentOS
  • Windows
brew install portaudio && pip install pyaudio
Install the other dependencies:
pip install websocket-client dashscope
  • Interaction mode
    • VAD mode (Voice Activity Detection, automatically detects the start and end of speech) The server responds after detecting the end of the user's speech.
    • Manual mode (press to speak, release to send) The client controls the start and end of speech. After speaking, your application must notify the server.
    • VAD mode
    • Manual mode
    Create a Python file named vad_dash.py and copy the following code into the file:
    # Dependencies: dashscope >= 1.23.9, pyaudio
    import os
    import base64
    import time
    import pyaudio
    from dashscope.audio.qwen_omni import MultiModality, AudioFormat,OmniRealtimeCallback,OmniRealtimeConversation
    import dashscope
    
    # Configuration: URL, API key, voice, model, model role
    # Specify the region. 'intl' for Singapore region, 'cn' for China (Beijing) region. Replace {WorkspaceId} with your Bailian workspace ID.
    region = 'intl'
    base_domain = '{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com' if region == 'intl' else '{WorkspaceId}.cn-beijing.maas.aliyuncs.com'
    url = f'wss://{base_domain}/api-ws/v1/realtime'
    # Configure the API key. If the environment variable is not set, replace the following line with your API key: dashscope.api_key = "sk-xxx"
    dashscope.api_key = os.getenv('DASHSCOPE_API_KEY')
    # Specify the voice.
    voice = 'Ethan'
    # Specify the model.
    model = 'qwen3.5-omni-plus-realtime'
    # Specify the model role.
    instructions = "You are Xiaoyun, a personal assistant. Answer the user's questions in a humorous and witty way."
    class SimpleCallback(OmniRealtimeCallback):
        def __init__(self, pya):
            self.pya = pya
            self.out = None
        def on_open(self):
            # Initialize the audio output stream.
            self.out = self.pya.open(
                format=pyaudio.paInt16,
                channels=1,
                rate=24000,
                output=True
            )
        def on_event(self, response):
            if response['type'] == 'response.audio.delta':
                # Play the audio.
                self.out.write(base64.b64decode(response['delta']))
            elif response['type'] == 'conversation.item.input_audio_transcription.delta':
                # Streaming preview: text is the confirmed prefix, stash is the unconfirmed suffix.
                preview = response.get('text', '') + response.get('stash', '')
                print(f"\r[User] {preview}", end='', flush=True)
            elif response['type'] == 'conversation.item.input_audio_transcription.completed':
                # Transcription completed. Print the final text and move to a new line.
                print(f"\r[User] {response['transcript']}")
            elif response['type'] == 'response.audio_transcript.done':
                # Print the assistant's response text.
                print(f"[LLM] {response['transcript']}")
    
    # 1. Initialize the audio device.
    pya = pyaudio.PyAudio()
    # 2. Create the callback function and conversation.
    callback = SimpleCallback(pya)
    conv = OmniRealtimeConversation(model=model, callback=callback, url=url)
    # 3. Connect and configure the session.
    conv.connect()
    conv.update_session(output_modalities=[MultiModality.AUDIO, MultiModality.TEXT], voice=voice, instructions=instructions)
    # 4. Initialize the audio input stream.
    mic = pya.open(format=pyaudio.paInt16, channels=1, rate=16000, input=True)
    # 5. Main loop to process audio input.
    print("Conversation started. Speak into the microphone (Ctrl+C to exit)...")
    try:
        while True:
            audio_data = mic.read(3200, exception_on_overflow=False)
            conv.append_audio(base64.b64encode(audio_data).decode())
            time.sleep(0.01)
    except KeyboardInterrupt:
        # Clean up resources.
        conv.close()
        mic.close()
        callback.out.close()
        pya.terminate()
        print("\nConversation ended")
    
    Run vad_dash.py to start a real-time conversation through your microphone. The system detects speech and streams audio to the server.

Interaction flow

  • VAD mode
  • Manual mode
Set session.turn_detection.type in session.update to "server_vad" or "semantic_vad" to enable VAD mode. Suitable for voice call scenarios. Both WebSocket and WebRTC support VAD mode with the same server events; they differ only in how audio and images are transmitted.
WebRTC only supports VAD mode and does not support Manual mode. With WebRTC, audio is transmitted directly via RTP without sending input_audio_buffer.append events; images are transmitted via video tracks without support for input_image_buffer.append events. Control commands and server events are transmitted via DataChannel with the same event types as WebSocket.
The interaction flow is as follows:
  1. The client sends audio data. WebSocket sends it via input_audio_buffer.append events; WebRTC transmits it automatically via the audio track (RTP) without manually sending events.
  2. The server detects the start of speech and sends the input_audio_buffer.speech_started event via DataChannel (WebRTC) or WebSocket.
  3. The server detects the end of speech and sends the input_audio_buffer.speech_stopped event.
  4. The server commits the audio buffer and sends the input_audio_buffer.committed event.
  5. The server begins generating a response, sending conversation.item.created and other events. Audio responses are returned incrementally via the WebSocket response.audio.delta event, or transmitted directly via the WebRTC audio track (RTP).
  6. During the response, the server returns incremental text transcription via response.audio_transcript.delta events, and sends the response.done event when the response is complete.
LifecycleClient eventsServer events
Session initializationsession.update
Session configuration
session.created
Session created.
session.updated
Session configuration updated.
User audio inputinput_audio_buffer.append
WebSocket: Appends audio to the buffer via this event.
input_image_buffer.append
WebSocket: Appends an image to the buffer via this event.
WebRTC: Audio is transmitted automatically via the RTP audio track, and images are transmitted via the video track. These events are not needed.
input_audio_buffer.speech_started
Speech start detected.
input_audio_buffer.speech_stopped
Speech end detected.
input_audio_buffer.committed
Audio buffer committed.
Server audio outputNoneresponse.created
Response generation started.
response.output_item.added
New output item added to the response.
conversation.item.created
Conversation item created.
response.content_part.added
New content part added to the assistant message.
response.audio_transcript.delta
Incrementally generated transcribed text.
response.audio.delta
WebSocket: Incrementally generated audio from the model is returned via this event. WebRTC: Audio is transmitted directly via the RTP audio track; this event is not returned.
response.audio_transcript.done
Text transcription completed.
response.audio.done
Audio generation completed.
response.content_part.done
Streaming of the assistant's text or audio content is complete.
response.output_item.done
The assistant's entire output item has finished streaming.
response.done
Response completed.
conversation.item.input_audio_transcription.completed
User audio input transcription completed (requires enabling input_audio_transcription in session.update).
Web search lets the model use real-time data to answer questions about timely information such as stock prices and weather. The model automatically determines if a search is needed.
Only qwen3.5-omni-plus-realtime supports web search. Disabled by default; enable it with session.update.
For billing, see the agent policy in billing rules.
Add the following parameters to the session.update event:
  • enable_search: Set to true to enable the web search feature.
  • search_options.enable_source: Set to true to include the sources of the search results in the response.
For more parameters, see session.update.

Response format

When web search is enabled, the usage object in response.done includes a plugins field with search metering information:
{
    "usage": {
        "total_tokens": 2937,
        "input_tokens": 2554,
        "output_tokens": 383,
        "input_tokens_details": {
            "text_tokens": 2512,
            "audio_tokens": 42
        },
        "output_tokens_details": {
            "text_tokens": 90,
            "audio_tokens": 293
        },
        "plugins": {
            "search": {
                "count": 1,
                "strategy": "agent"
            }
        }
    }
}

Code example

Enable web search in a real-time conversation:
  • DashScope Python SDK
  • DashScope Java SDK
  • WebSocket (Python)
Pass the enable_search and search_options parameters in the update_session call:
import os
import base64
import time
import json
import pyaudio
from dashscope.audio.qwen_omni import MultiModality, AudioFormat, OmniRealtimeCallback, OmniRealtimeConversation
import dashscope

dashscope.api_key = os.getenv('DASHSCOPE_API_KEY')
# Singapore region. Replace {WorkspaceId} with your Bailian workspace ID. URLs vary by region.
url = 'wss://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/api-ws/v1/realtime'
model = 'qwen3.5-omni-plus-realtime'
voice = 'Tina'

class SearchCallback(OmniRealtimeCallback):
    def __init__(self, pya):
        self.pya = pya
        self.out = None
    def on_open(self):
        self.out = self.pya.open(format=pyaudio.paInt16, channels=1, rate=24000, output=True)
    def on_event(self, response):
        if response['type'] == 'response.audio.delta':
            self.out.write(base64.b64decode(response['delta']))
        elif response['type'] == 'conversation.item.input_audio_transcription.delta':
            preview = response.get('text', '') + response.get('stash', '')
            print(f"\r[User] {preview}", end='', flush=True)
        elif response['type'] == 'conversation.item.input_audio_transcription.completed':
            print(f"\r[User] {response['transcript']}")
        elif response['type'] == 'response.audio_transcript.done':
            print(f"[LLM] {response['transcript']}")
        elif response['type'] == 'response.done':
            usage = response.get('response', {}).get('usage', {})
            plugins = usage.get('plugins', {})
            if plugins.get('search'):
                print(f"[Search] count={plugins['search']['count']}, strategy={plugins['search']['strategy']}")

pya = pyaudio.PyAudio()
callback = SearchCallback(pya)
conv = OmniRealtimeConversation(model=model, callback=callback, url=url)
conv.connect()
conv.update_session(
    output_modalities=[MultiModality.AUDIO, MultiModality.TEXT],
    voice=voice,
    instructions="You are Xiaoyun, a personal assistant.",
    enable_search=True,
    search_options={'enable_source': True}
)
mic = pya.open(format=pyaudio.paInt16, channels=1, rate=16000, input=True)
print("Web search is enabled. Speak into the microphone (Ctrl+C to exit)...")
try:
    while True:
        audio_data = mic.read(3200, exception_on_overflow=False)
        conv.append_audio(base64.b64encode(audio_data).decode())
        time.sleep(0.01)
except KeyboardInterrupt:
    conv.close()
    mic.close()
    callback.out.close()
    pya.terminate()
    print("\nConversation ended.")

API reference

Billing and rate limits

Billing

Billing is token-based, metered by modality (audio, image, text). Check the Model Studio console for pricing.
In a multi-turn real-time conversation, each time the model generates a response, it processes all historical conversation content within the context window — including audio, images, and text from previous turns — together with the new input of the current turn as input tokens. As a result, input tokens accumulate with each turn rather than being counted only for the new input in the current turn.For example, if a 10-second audio input converts to 70 tokens (Qwen3.5-Omni-Realtime), and that audio is still within the context window at turn 3, it will still be counted toward the input tokens for turn 3. The actual billed input tokens = tokens from all historical turns within the context window + tokens from the new input in the current turn.
  • Audio
  • Image
  • Qwen3.5-Omni-Realtime:
    • Input audio: total tokens = Audio duration (seconds) * 7
    • Output audio: total tokens = Audio duration (seconds) * 12.5
  • Qwen3-Omni-Flash-Realtime: Input and output audio use the same formula: total tokens = Audio duration (seconds) * 12.5
  • Qwen-Omni-Turbo-Realtime: Input and output audio use the same formula: total tokens = Audio duration (seconds) * 25 Audio durations of less than 1 second are billed as 1 second.

Rate limiting

For model rate limits, see Rate limiting.

Error codes

If the model call fails and returns an error message, see Error codes for resolution.

Voice list

For a list of voices available for the Qwen-Omni-Realtime model, see Voices.
Token Plan
Model Playground
Statistics and Monitoring
Support