Learn about real-time speech recognition capabilities, streaming audio calls, and ways to improve recognition results.
Features
Real-time speech recognition accepts a continuous audio stream and returns transcribed text for voice input, meeting notes, live captions, and similar applications.
- Languages and dialects: Automatically detect the language and recognize multiple languages and Chinese dialects. For the supported range, see Speech recognition overview.
- Speech detection: Detect speech boundaries automatically and adjust speech-versus-noise thresholds for different recording environments.
- Text processing: Predict punctuation, preserve Chinese dialect expressions, or remove fillers, repetitions, and self-corrections through text polishing.
- Accuracy improvements: Use hotwords for specific names and terms, and provide context from earlier conversation turns.
- Structured results: Use sentence results and timestamps to display captions, save text, and locate segments in audio.
- Audio input: Use multiple audio formats and select a sample rate supported by the model.
NoteCapabilities vary by model. Check the supported models for each feature. To submit a complete recording in one request, see Non-real-time speech recognition.
| Use case | Recommended model | Result behavior |
|---|---|---|
Continuous transcription for meetings, classes, and captions | qwen-audio-3.1-asr-flash-streaming | Returns intermediate and final results for text display and updates while a user speaks. |
Short voice input, such as voice input methods and voice messages | qwen-audio-3.1-asr-flash-message | Returns final sentence results by default. Intermediate results and text polishing can be enabled separately. |
Get started
Install the DashScope SDK, then get your API key and Base URL from the console. Set them as DASHSCOPE_API_KEY and DASHSCOPE_BASE_URL.
Download the sample audio as asr_example.wav in the working directory. It is an English, 16 kHz, mono WAV file with 16-bit PCM encoding. The examples send the file in chunks to simulate real-time input, without requiring a microphone.
Qwen-Audio-ASR-Streaming
This example uses qwen-audio-3.1-asr-flash-streaming to send audio continuously through the DashScope SDK while receiving and printing interim and final results. After the audio ends, wait for the remaining results and task completion before closing the connection.
import com.alibaba.dashscope.audio.asr.recognition.Recognition;
import com.alibaba.dashscope.audio.asr.recognition.RecognitionParam;
import com.alibaba.dashscope.audio.asr.recognition.RecognitionResult;
import com.alibaba.dashscope.common.ResultCallback;
import com.alibaba.dashscope.utils.Constants;
import java.io.InputStream;
import java.net.URI;
import java.nio.ByteBuffer;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.concurrent.atomic.AtomicReference;
public class Main {
public static void main(String[] args) throws Exception {
Constants.baseWebsocketApiUrl =
"wss://" + URI.create(System.getenv("DASHSCOPE_BASE_URL")).getHost()
+ "/api-ws/v1/inference";
RecognitionParam param = RecognitionParam.builder()
.apiKey(System.getenv("DASHSCOPE_API_KEY"))
.model("qwen-audio-3.1-asr-flash-streaming")
.format("wav")
.sampleRate(16000)
.build();
Recognition recognition = new Recognition();
AtomicReference<Exception> failure = new AtomicReference<>();
ResultCallback<RecognitionResult> callback = new ResultCallback<RecognitionResult>() {
@Override
public void onEvent(RecognitionResult result) {
if (result.getSentence() != null && result.getSentence().getText() != null
&& !result.getSentence().getText().isEmpty()) {
System.out.println(result.getSentence());
}
}
@Override
public void onComplete() {}
@Override
public void onError(Exception error) {
failure.set(error);
}
};
try {
recognition.call(param, callback);
try (InputStream audio = Files.newInputStream(Paths.get("asr_example.wav"))) {
byte[] buffer = new byte[3200];
int size;
while ((size = audio.read(buffer)) != -1) {
recognition.sendAudioFrame(ByteBuffer.wrap(buffer, 0, size));
Thread.sleep(100);
}
}
recognition.stop();
if (failure.get() != null) throw failure.get();
} finally {
if (recognition.getDuplexApi() != null) {
recognition.getDuplexApi().close(1000, "bye");
}
}
System.exit(0);
}
}
import os
import time
from concurrent.futures import ThreadPoolExecutor
import dashscope
from urllib.parse import urlsplit
client = dashscope.Client(
api_key=os.environ["DASHSCOPE_API_KEY"],
base_url=os.environ["DASHSCOPE_BASE_URL"],
)
def receive_results(recognition):
for event in recognition.events():
payload = event.get("payload") or {}
sentence = (payload.get("output") or {}).get("sentence") or {}
if sentence.get("text") and not sentence.get("heartbeat"):
print(sentence)
with client.audio.asr.recognize(
ws_url=f"wss://{urlsplit(os.environ['DASHSCOPE_BASE_URL']).netloc}/api-ws/v1/inference",
model="qwen-audio-3.1-asr-flash-streaming",
format="wav",
sample_rate=16000,
) as recognition:
with ThreadPoolExecutor(max_workers=1) as pool:
receiver = pool.submit(receive_results, recognition)
try:
with open("asr_example.wav", "rb") as audio:
while chunk := audio.read(3200):
recognition.send_audio_frame(chunk)
time.sleep(0.1)
recognition.stop()
receiver.result()
finally:
recognition.close()
Qwen-Audio-ASR-Message
This example uses qwen-audio-3.1-asr-flash-message to send audio continuously through the DashScope SDK and receive and print final utterance results by default. After the audio ends, wait for the remaining results and task completion before closing the connection.
import com.alibaba.dashscope.audio.asr.recognition.Recognition;
import com.alibaba.dashscope.audio.asr.recognition.RecognitionParam;
import com.alibaba.dashscope.audio.asr.recognition.RecognitionResult;
import com.alibaba.dashscope.common.ResultCallback;
import com.alibaba.dashscope.utils.Constants;
import java.io.InputStream;
import java.net.URI;
import java.nio.ByteBuffer;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.concurrent.atomic.AtomicReference;
public class Main {
public static void main(String[] args) throws Exception {
Constants.baseWebsocketApiUrl =
"wss://" + URI.create(System.getenv("DASHSCOPE_BASE_URL")).getHost()
+ "/api-ws/v1/inference";
RecognitionParam param = RecognitionParam.builder()
.apiKey(System.getenv("DASHSCOPE_API_KEY"))
.model("qwen-audio-3.1-asr-flash-message")
.format("wav")
.sampleRate(16000)
.build();
Recognition recognition = new Recognition();
AtomicReference<Exception> failure = new AtomicReference<>();
ResultCallback<RecognitionResult> callback = new ResultCallback<RecognitionResult>() {
@Override
public void onEvent(RecognitionResult result) {
if (result.getSentence() != null && result.getSentence().getText() != null
&& !result.getSentence().getText().isEmpty()) {
System.out.println(result.getSentence());
}
}
@Override
public void onComplete() {}
@Override
public void onError(Exception error) {
failure.set(error);
}
};
try {
recognition.call(param, callback);
try (InputStream audio = Files.newInputStream(Paths.get("asr_example.wav"))) {
byte[] buffer = new byte[3200];
int size;
while ((size = audio.read(buffer)) != -1) {
recognition.sendAudioFrame(ByteBuffer.wrap(buffer, 0, size));
Thread.sleep(100);
}
}
recognition.stop();
if (failure.get() != null) throw failure.get();
} finally {
if (recognition.getDuplexApi() != null) {
recognition.getDuplexApi().close(1000, "bye");
}
}
System.exit(0);
}
}
import os
import time
from concurrent.futures import ThreadPoolExecutor
import dashscope
from urllib.parse import urlsplit
client = dashscope.Client(
api_key=os.environ["DASHSCOPE_API_KEY"],
base_url=os.environ["DASHSCOPE_BASE_URL"],
)
def receive_results(recognition):
for event in recognition.events():
payload = event.get("payload") or {}
sentence = (payload.get("output") or {}).get("sentence") or {}
if sentence.get("text") and not sentence.get("heartbeat"):
print(sentence)
with client.audio.asr.recognize(
ws_url=f"wss://{urlsplit(os.environ['DASHSCOPE_BASE_URL']).netloc}/api-ws/v1/inference",
model="qwen-audio-3.1-asr-flash-message",
format="wav",
sample_rate=16000,
) as recognition:
with ThreadPoolExecutor(max_workers=1) as pool:
receiver = pool.submit(receive_results, recognition)
try:
with open("asr_example.wav", "rb") as audio:
while chunk := audio.read(3200):
recognition.send_audio_frame(chunk)
time.sleep(0.1)
recognition.stop()
receiver.result()
finally:
recognition.close()
Process recognition results
-
Interim results: Use these for live subtitles or input previews. The text for an utterance may be revised during recognition. Update the displayed utterance instead of appending every result.
-
Final results: When an utterance ends, replace its interim text with the final result for stored transcripts, subtitles, or downstream processing.
NoteQwen-Audio-ASR-Message returns only final results by default; enable
intermediate_result_enabledif a live preview is needed. -
Timestamps: Link text to audio positions for subtitle alignment, keyword highlighting, or audio seeking. Use utterance-level and word-level timestamps from final results rather than an interim end time that is not yet confirmed. Timestamps are in milliseconds.
NoteQwen3-ASR-Flash-Realtime does not return transcript timestamps.
Improve recognition results
Adjust utterance boundaries, polish or preserve spoken expressions, and configure hotwords and context to improve segmentation, text readability, and recognition of specific terms.
Adjust sentence segmentation
Voice activity detection (VAD) uses silence after speech to determine when an utterance ends and trigger a final result. A shorter silence threshold suits responsive voice interactions. A longer threshold accommodates pauses within a sentence but increases the wait for a final result. Adjust it to the speaker’s pace and your interaction flow.
| Model | Configuration |
|---|---|
qwen-audio-3.1-asr-flash-streaming, qwen-audio-3.1-asr-flash-message | Set |
qwen-audio-3.0-asr-flash-streaming, Fun-ASR-Realtime series | Set |
Paraformer | For VAD segmentation, adjust |
Qwen3-ASR-Flash-Realtime | Set |
Polish text
Spoken input often contains fillers, repetitions, and self-corrections. Text polishing cleans up these expressions for uses such as voice messages and speech input.
In qwen-audio-3.1-asr-flash-message, disfluency_removal_enabled controls polishing. Polishing applies to final results; update the displayed text when the utterance ends.
Preserve dialect expressions
When recognizing a Chinese dialect, choose whether to preserve dialect expressions or transcribe them into Standard Chinese. Dialect subtitles may need the original expressions, while written records for a broader audience may need Standard Chinese.
In qwen-audio-3.1-asr-flash-streaming and qwen-audio-3.1-asr-flash-message, keep_dialect controls whether dialect expressions are preserved. For the latter model, it applies only to final results.
Hotwords and context enhancement
Hotwords help recognize specific vocabulary, such as brands, personal names, and domain terms. Context enhancement provides terms and background from earlier conversation turns. For configuration, supported models, and tuning, see Improve recognition accuracy.
Sensitive word filtering
Sensitive-word filtering replaces specified words with the same number of asterisks or removes them from the transcript, for example when masking subtitle or transcript content. Qwen-Audio-ASR-Streaming and Fun-ASR-Realtime configure these rules with special_word_filter.
Use in production
For production use, manage connections and concurrency according to your traffic, and handle long silence, retries, and resource cleanup. See Speech recognition best practices for audio preparation, task and connection management, Java concurrency, and complete examples.
Supported models and regions
Singapore
To call the following models, use an API Key for the Singapore region:
- Qwen-Audio-ASR-Message: qwen-audio-3.1-asr-flash-message
- Qwen-Audio-ASR-Streaming: qwen-audio-3.1-asr-flash-streaming, qwen-audio-3.0-asr-flash-streaming
- Fun-ASR-Realtime: fun-asr-realtime (stable version)
- Qwen-ASR-Realtime: qwen3-asr-flash-realtime (stable version)
China (Beijing)
To call the following models, use an API Key for the China (Beijing) region:
-
Qwen-Audio-ASR-Message: qwen-audio-3.1-asr-flash-message
-
Qwen-Audio-ASR-Streaming: qwen-audio-3.1-asr-flash-streaming, qwen-audio-3.0-asr-flash-streaming
-
Fun-ASR-Realtime: fun-asr-realtime (stable version), fun-asr-realtime-2026-02-28 (latest snapshot version)
- fun-asr-flash-8k-realtime (stable version, currently equivalent to fun-asr-flash-8k-realtime-2026-01-28), fun-asr-flash-8k-realtime-2026-01-28
-
Qwen-ASR-Realtime: qwen3-asr-flash-realtime (stable version)
-
Paraformer: paraformer-realtime-v2, paraformer-realtime-8k-v2
Usage limits
Real-time audio must be mono. Select an audio format and sample rate from the table below, and set format and sample_rate to match the actual audio.
| Model | Input method | Audio format | Sample rate | Size/duration |
|---|---|---|---|---|
qwen-audio-3.1-asr-flash-streaming, qwen-audio-3.0-asr-flash-streaming | Binary stream |
| Any | Unlimited |
qwen-audio-3.1-asr-flash-message | Binary stream |
| 16 kHz | — |
Fun-ASR-Realtime series (fun-asr-realtime) | Binary stream |
| Any | Unlimited |
Fun-ASR-Flash-8K-Realtime series (fun-asr-flash-8k-realtime) | Binary stream |
| 8 kHz | Unlimited |
qwen3-asr-flash-realtime | Base64 audio in JSON events |
| 8 kHz, 16 kHz | Unlimited |
paraformer-realtime-v2 | Binary stream |
| Any | Unlimited |
paraformer-realtime-8k-v2 | Binary stream |
| 8 kHz | Unlimited |
For audio checks and conversion, see Prepare input audio.
API reference
For real-time audio that needs low-latency transport, resilience on weak networks, echo cancellation, and noise suppression, consider AOQ. See the Realtime API overview for protocol selection, supported models, and integration.