Skip to main content
Omni-modal

Realtime audio and video understanding

Live audio and video chat

How to use

1. Establish a connection

The general WebSocket and WebRTC connection examples use qwen3.8-omni-flash-realtime. Model-specific configuration examples use their indicated models. Qwen-Omni-Realtime supports two protocols: WebSocket and WebRTC. WebSocket is suitable for server-side integration and quick setup. WebRTC is designed for browser-based and low-latency voice scenarios, with audio transmitted directly over UDP and built-in echo cancellation and noise reduction. For session duration and conversation history limits, see Limitations.
  • WebSocket
  • WebRTC
  • AOQ
  • Native WebSocket connection
  • DashScope SDK
The following configuration items are required:
Configuration itemDescription
Endpointwss://maas.qwencloudapi.com/api-ws/v1/realtime
Query parameterThe query parameter is model. Set it to the name of the model you want to access. See Model selection. Example: ?model=qwen3.8-omni-flash-realtime
Request headerUse Bearer Token for authentication: Authorization: Bearer DASHSCOPE_API_KEY. DASHSCOPE_API_KEY is the API Key that you requested on QwenCloud.
# pip install websocket-client
import json
import websocket
import os

API_KEY=os.getenv("DASHSCOPE_API_KEY")
API_URL = "wss://maas.qwencloudapi.com/api-ws/v1/realtime?model=qwen3.8-omni-flash-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 the session

Send the session.update client event: Use session.update to configure output modalities, voice, audio format, and VAD. The following Qwen3.8-Omni-Flash-Realtime WebSocket Manual-mode configuration matches the minimal audio example:
{
  "type": "session.update",
  "session": {
    "modalities": [
      "text",
      "audio"
    ],
    "turn_detection": null,
    "audio": {
      "input": {
        "format": {
          "type": "pcm",
          "sample_rate": 16000,
          "sample_format": "s16le",
          "channels": 1,
          "packing": "interleaved",
          "channel_layout": "mono"
        }
      },
      "output": {
        "voice": "longanlingxin",
        "format": {
          "type": "pcm",
          "sample_rate": 24000
        }
      }
    }
  }
}
The following example uses the Tina voice and semantic_vad configuration of Qwen3.5-Omni-Realtime.
{
  // The ID of this event, generated by the client.
  "event_id": "event_ToPZqeobitzUJnt3QqtWg",
  // The event type. This is fixed to session.update.
  "type": "session.update",
  // Session configuration.
  "session": {
    // The output modalities. Supported values are ["text"] (text only) or ["text", "audio"] (text and audio).
    "modalities": [
      "text",
      "audio"
    ],
    // The voice for the output audio.
    "voice": "Tina",
    // 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",
    // The system message. It is used to set 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 friendly. Always respond with a professional and helpful attitude. Do not provide unconfirmed information or information beyond the scope of the hotel's services.",
    // Specifies whether to enable voice activity detection. To enable it, pass a configuration object. The server will automatically detect the start and end of speech based on this object.
    // Set to null to let the client decide when to initiate a model response.
    "turn_detection": {
      // The VAD type. Valid values: server_vad and semantic_vad. We recommend that you set this parameter to semantic_vad for models like qwen3.5-omni-realtime.
      "type": "semantic_vad",
      // The VAD detection threshold. Increase this value in noisy environments and decrease it in quiet environments.
      "threshold": 0.5,
      // The duration of silence to detect the end of speech. If this value is exceeded, a model response is triggered.
      "silence_duration_ms": 800
    }
  }
}
Qwen3.5-Omni-Realtime models support cloned voices in addition to preset voices. See Voice cloning to create a custom voice from an audio sample.
For multichannel audio, video aggregation, MCP configuration, and SDK examples, see Advanced session configuration.

3. Input audio and images

  • WebSocket
  • WebRTC
The client sends Base64-encoded audio and image data to the server buffer using the input_audio_buffer.append and input_image_buffer.append events. Audio input is required. Image input is optional and can come from local files or a real-time video stream.
When server-side Voice Activity Detection (VAD) is enabled, the server automatically submits the data and triggers a response when it detects the end of speech. When VAD is disabled (manual mode), the client must call the input_audio_buffer.commit event to submit the data.

4. Receive model responses

The format of the model response depends on the configured output modalities.
  • WebSocket
  • WebRTC

Model selection

Use Qwen3.8-Omni-Flash-Realtime for real-time audio/video conversations. It accepts streaming audio/video, returns text and audio, and supports custom function calling, MCP, multichannel audio, and voice cloning. See Establish a connection. Supports speech recognition for 113 languages and dialects and speech generation for 36 languages and dialects. See the voice list for voices and audio previews.
Qwen3.5-Omni-Realtime provides the following capabilities:
  • 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.

Limitations

  • Mutually exclusive features: Web search and tool calling cannot be enabled at the same time.
  • Session duration: A single WebSocket session can last up to 120 minutes. The connection closes automatically at this limit.
qwen3.8-omni-flash-realtime supports up to 196608 total input tokens. Input length is calculated using the same rules as Qwen3.5-Omni-Realtime.

Conversation history limits

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. It is not the duration of the entire session.
ModelAudio max turnsVideo max turnsAudio max durationVideo max duration
qwen3.8-omni-flash-realtime100 turns50 turns600 seconds240 seconds
qwen3.5-omni-plus-realtime100 turns50 turns600 seconds240 seconds
qwen3.5-omni-flash-realtime80 turns50 turns480 seconds120 seconds
  • 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.

Getting started

Get an API key and set it as an environment variable.
  • DashScope Python SDK
  • DashScope Java SDK
  • WebSocket (Python)
Prepare the runtime environmentYour Python version must be 3.10 or later.First, install PyAudio based on your operating system.
  • macOS
  • Debian/Ubuntu
  • CentOS
  • Windows
brew install portaudio && pip install pyaudio
After installation completes, install the remaining dependencies using pip:
pip install websocket-client dashscope
Choose an interaction mode
  • VAD mode (automatically detects the start and end of speech) The server automatically determines when the user starts and stops speaking and responds accordingly.
  • Manual mode (press to talk, release to send) The client controls the start and end of speech. After the user finishes speaking, the client must actively send a message to the server.
  • VAD mode
  • Manual mode
Create a new Python file named vad_dash.py and copy the following code into the file:
# Dependencies: dashscope >= 1.26.5, pyaudio
import os
import base64
import time
import pyaudio
from dashscope.audio.qwen_omni import MultiModality, AudioFormat,OmniRealtimeCallback,OmniRealtimeConversation
import dashscope

# Configuration parameters: URL, API key, voice, model, model role
url = 'wss://maas.qwencloudapi.com/api-ws/v1/realtime'
# Configure the API key. If you have not set an environment variable, replace the following line with dashscope.api_key = "sk-xxx"
dashscope.api_key = os.getenv('DASHSCOPE_API_KEY')
# Specify the voice
voice = 'Tina'
# Specify the model
model = 'qwen3.8-omni-flash-realtime'
# Specify the model role
instructions = "You are Xiaoyun, a personal assistant. Please 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':
      # Print the transcribed text
      print(f"\r[User] {response['transcript']}")
    elif response['type'] == 'response.audio_transcript.done':
      # Print the assistant's reply text
      print(f"[LLM] {response['transcript']}")

# 1. Initialize the audio device
pya = pyaudio.PyAudio()
# 2. Create the callback function and session
callback = SimpleCallback(pya)
conv = OmniRealtimeConversation(model=model, callback=callback, url=url)
# 3. Establish the connection 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 have a real-time conversation with Qwen-Omni-Realtime through your microphone. The system detects the start and end of your speech and automatically sends it to the server without manual intervention.
Sample code on GitHubDownload complete sample code from GitHub, which includes:
  1. Audio conversation: Captures real-time audio from a microphone with VAD mode (enable_turn_detection = True) and supports voice interruption.
  2. Audio and video conversation: Captures real-time audio and video from a microphone and camera with VAD mode and supports voice interruption.
  3. Local call: Uses local audio and images as input with Manual mode (enable_turn_detection = False).
Use headphones for audio playback to prevent echoes from triggering voice interruption.
For the VAD/Manual mode interaction flow and the Qwen3.8-Omni-Flash-Realtime MCP interaction flow (tool discovery, invocation, approval, and continuation), see Interaction process.

Multichannel audio, video aggregation, and MCP

The following configuration applies to Qwen3.8-Omni-Flash-Realtime. Configure these options as needed before sending the first audio segment.
  • Multichannel audio (WebSocket access): configure session.audio.input.format before the first audio segment. It supports 1, 2, or 4 channels; multichannel input requires PCM, 16000 Hz, s16le, and interleaved samples. Use raw_mic_array for 2 channels and foa_ambix for 4. See client events for constraints and JSON fragments.
  • Video aggregation: session.video.input.representation_compact defaults to none. Use normal to aggregate video representations and reduce computation when fine visual detail is unnecessary. Set it before the first audio segment; it cannot change after audio input starts.
  • Voices: The default voice is Tina. Use session.audio.output.voice for new integrations; it takes precedence over session.voice. Available voices include longanlingxin (Long An Ling Xin), a warm and empathetic voice. See voices for the new longanlingxin voice and voice cloning.
  • MCP: configure a public HTTPS MCP Streamable HTTP service in session.tools. Approval is required by default. Wait for discovery to complete before creating a Response that needs the tool. See the MCP interaction flow for results, rejections, failures, and continuation.

Configure video aggregation with the SDK

Qwen3.8-Omni-Flash-Realtime requires DashScope Python SDK 1.26.5 or later, or Java SDK 2.22.15 or later. Pass the video aggregation parameter through the SDK: supply video to Python's update_session, or use OmniRealtimeConfig.builder().parameters(...) in Java. This example configures a Manual-mode session with text and audio output, the Tina voice, input transcription disabled, and video aggregation enabled. Establish a connection using the SDK examples below with the model set to qwen3.8-omni-flash-realtime. Replace that example's session configuration call with this one, and call it once before sending the first audio segment. conversation is the connected SDK session. The SDK also sends voice and VAD settings; configure any different voice, VAD, or transcription settings in the same call. In Java, import java.util.Arrays, java.util.Map, java.util.HashMap, com.alibaba.dashscope.audio.omni.OmniRealtimeConfig, and com.alibaba.dashscope.audio.omni.OmniRealtimeModality.
from dashscope.audio.qwen_omni import MultiModality

conversation.update_session(
  output_modalities=[MultiModality.AUDIO, MultiModality.TEXT],
  voice="Tina",
  enable_turn_detection=False,
  enable_input_audio_transcription=False,
  video={
    "input": {
      "representation_compact": "normal",
    },
  },
)
Web search lets the model reply using real-time retrieved data for scenarios that need up-to-date information, such as stock prices or weather forecasts. The model autonomously decides whether to search.
Qwen3.8-Omni-Flash-Realtime and the Qwen3.5-Omni-Realtime series support web search. It is disabled by default. Enable it using the session.update event.For billing details, see the agent policy in the Billing details.

How to enable

In the session.update event, add these parameters:
  • enable_search: Set to true to enable web search.
  • search_options.enable_source: Set to true to return a list of search result sources.
For full parameter details, see session.update.

Response format

After you enable web search, the response.done event includes a new plugins field in the usage object. This field records search usage metrics:
{
  "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 examples

The following examples show how to enable web search.
  • DashScope Python SDK
  • DashScope Java SDK
  • WebSocket (Python)
In the update_session call, pass the enable_search and search_options parameters:
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')
url = 'wss://maas.qwencloudapi.com/api-ws/v1/realtime'
model = 'qwen3.8-omni-flash-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':
      # 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':
      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 Xiao Yun, 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 (press 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 rules

Qwen-Omni-Realtime is billed based on the number of tokens used for different input modalities, such as audio and images. For more information about billing, see Pricing. For speech output, qwen3.8-omni-flash-realtime bills both the audio and its corresponding text at their respective output rates. Qwen3.5-Omni-Realtime models bill only the audio; the corresponding text is not charged. MCP tool invocations incur no additional fee; model inference remains subject to model pricing. See the MCP interaction flow for the calling procedure and limits.
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
  • Video
  • Qwen3.8-Omni-Flash-Realtime and Qwen3.5-Omni-Realtime: Input audio total tokens = Audio duration (in seconds) x 7; Output audio total tokens = Audio duration (in seconds) x 12.5
If the audio duration is less than 1 second, it is calculated as 1 second.For qwen3.8-omni-flash-realtime, spatial audio input uses twice as many input audio tokens as regular audio. The multiplier is the same for 2-channel and 4-channel input.

Rate limits

For more information about model rate limit rules, see Rate limits.

Error codes

If a call fails, see Error codes.

Voice list

For the voices each omni-modal model supports, including samples and voice parameter values, see Omni-modal voice list.