RESTful API
The long-text speech synthesis RESTful API accepts text through a POST request and generates an audio file asynchronously. After submitting a task, poll for the result or receive it through a callback, then use the returned audio URL to download or play the file.
Features
PCM, WAV, and MP3 audio formats.
Sample rates of 8000 Hz and 16000 Hz. Support for other sample rates depends on the voice.
Configurable voice, speech rate, pitch, and volume.
Sentence-level timestamps for synchronizing audio playback with subtitles.
Polling and callbacks for retrieving results.
For the complete voice list, supported sample rates, and parameters, see Long-text speech synthesis API reference.
We recommend calling the API from a server to avoid exposing authentication tokens in a browser. A server-side Node.js example is provided below.
Prerequisites
Activate the commercial edition of long-text speech synthesis. No trial edition is available for this feature.
Create a project and obtain its Appkey. For instructions, see Manage projects.
Obtain a valid NLS token. For instructions, see Obtain a token.
Endpoints
|
Access type |
Requirements |
URL |
|
Public access |
Internet connectivity. |
|
|
VPC access |
An Alibaba Cloud ECS instance in China (Shanghai). |
|
The public endpoint supports HTTP and HTTPS. HTTPS is recommended. The VPC endpoint supports only HTTP. The following examples use the public HTTPS endpoint.
Code examples
The examples read the Appkey and NLS token from the NLS_APP_KEY and NLS_TOKEN environment variables, submit a synthesis task, poll for the result, and print the task ID and audio download URL. Replace text with the text to synthesize.
Java
This example uses the HTTP client in JDK 11 or later and Fastjson. Add the following Maven dependency:
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.83</version>
</dependency>
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import java.net.URI;
import java.net.URLEncoder;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.Map;
public class LongTts {
private static final String URL = "https://nls-gateway-cn-shanghai.aliyuncs.com/rest/v1/tts/async";
private static final HttpClient CLIENT = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(30)).build();
private static JSONObject call(HttpRequest request) throws Exception {
HttpResponse<String> response = CLIENT.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() < 200 || response.statusCode() >= 300) {
throw new IllegalStateException("HTTP " + response.statusCode());
}
JSONObject result = JSON.parseObject(response.body());
if (result.getIntValue("error_code") != 20000000) {
throw new IllegalStateException("NLS error " + result.get("error_code")
+ "; request_id=" + result.getString("request_id"));
}
return result;
}
private static String encode(String value) {
return URLEncoder.encode(value, StandardCharsets.UTF_8);
}
public static void main(String[] args) throws Exception {
String appkey = System.getenv("NLS_APP_KEY");
String token = System.getenv("NLS_TOKEN");
if (appkey == null || token == null) {
throw new IllegalArgumentException("Set NLS_APP_KEY and NLS_TOKEN");
}
Map<String, Object> body = Map.of(
"header", Map.of("appkey", appkey, "token", token),
"payload", Map.of("enable_notify", false, "tts_request", Map.of(
"text", "Welcome to the text-to-speech service.",
"voice", "abby", "format", "wav", "sample_rate", 16000,
"enable_subtitle", true)));
JSONObject result = call(HttpRequest.newBuilder(URI.create(URL))
.timeout(Duration.ofSeconds(30)).header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(JSON.toJSONString(body))).build());
String taskId = result.getJSONObject("data").getString("task_id");
System.out.println("task_id: " + taskId);
String query = "?appkey=" + encode(appkey) + "&token=" + encode(token)
+ "&task_id=" + encode(taskId);
while (true) {
result = call(HttpRequest.newBuilder(URI.create(URL + query))
.timeout(Duration.ofSeconds(30)).GET().build());
String state = result.getString("error_message");
if ("QUEUEING".equals(state) || "RUNNING".equals(state)) {
Thread.sleep(10000);
continue;
}
JSONObject data = result.getJSONObject("data");
String address = data == null ? null : data.getString("audio_address");
if (address == null || address.isEmpty()) {
throw new IllegalStateException("No audio URL; request_id=" + result.getString("request_id"));
}
System.out.println(address);
break;
}
}
}
For additional Java examples, download nls-restful-java-demo.
Python
This example uses the Python 3 standard library. No third-party dependencies are required.
import json
import os
import time
from urllib.parse import urlencode
from urllib.request import Request, urlopen
from urllib.error import HTTPError
URL = "https://nls-gateway-cn-shanghai.aliyuncs.com/rest/v1/tts/async"
appkey = os.environ["NLS_APP_KEY"]
token = os.environ["NLS_TOKEN"]
def call(request):
try:
with urlopen(request, timeout=30) as response:
result = json.load(response)
except HTTPError as error:
raise RuntimeError(f"HTTP {error.code}") from None
if result.get("error_code") != 20000000:
raise RuntimeError(f"NLS error {result.get('error_code')}; "
f"request_id={result.get('request_id')}")
return result
body = {
"header": {"appkey": appkey, "token": token},
"payload": {
"tts_request": {
"text": "Welcome to the text-to-speech service.",
"voice": "abby", "format": "wav", "sample_rate": 16000,
"enable_subtitle": True
},
"enable_notify": False
}
}
result = call(Request(URL, data=json.dumps(body).encode("utf-8"),
headers={"Content-Type": "application/json"}))
task_id = result["data"]["task_id"]
print("task_id:", task_id)
query = urlencode({"appkey": appkey, "token": token, "task_id": task_id})
while True:
result = call(Request(URL + "?" + query))
if result.get("error_message") in ("QUEUEING", "RUNNING"):
time.sleep(10)
continue
address = (result.get("data") or {}).get("audio_address")
if not address:
raise RuntimeError("No audio URL; request_id=" + str(result.get("request_id")))
print(address)
break
PHP
This example uses PHP 8.5 with the cURL extension enabled.
<?php
$url = 'https://nls-gateway-cn-shanghai.aliyuncs.com/rest/v1/tts/async';
$appkey = getenv('NLS_APP_KEY');
$token = getenv('NLS_TOKEN');
if (!$appkey || !$token) {
throw new RuntimeException('Set NLS_APP_KEY and NLS_TOKEN');
}
function callApi($url, $body = null) {
$ch = curl_init($url);
curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 30]);
if ($body !== null) {
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
CURLOPT_POSTFIELDS => json_encode($body, JSON_THROW_ON_ERROR)
]);
}
$raw = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($raw === false || $status < 200 || $status >= 300) {
throw new RuntimeException('HTTP request failed; status=' . $status);
}
$result = json_decode($raw, true, 512, JSON_THROW_ON_ERROR);
if (($result['error_code'] ?? null) !== 20000000) {
throw new RuntimeException('NLS error ' . ($result['error_code'] ?? '') .
'; request_id=' . ($result['request_id'] ?? ''));
}
return $result;
}
$body = [
'header' => ['appkey' => $appkey, 'token' => $token],
'payload' => [
'tts_request' => [
'text' => 'Welcome to the text-to-speech service.',
'voice' => 'abby', 'format' => 'wav', 'sample_rate' => 16000,
'enable_subtitle' => true
],
'enable_notify' => false
]
];
$result = callApi($url, $body);
$taskId = $result['data']['task_id'];
echo 'task_id: ' . $taskId . PHP_EOL;
$query = http_build_query(['appkey' => $appkey, 'token' => $token, 'task_id' => $taskId]);
while (true) {
$result = callApi($url . '?' . $query);
if (in_array($result['error_message'] ?? '', ['QUEUEING', 'RUNNING'], true)) {
sleep(10);
continue;
}
if (empty($result['data']['audio_address'])) {
throw new RuntimeException('No audio URL; request_id=' . ($result['request_id'] ?? ''));
}
echo $result['data']['audio_address'] . PHP_EOL;
break;
}
Node.js
This example uses the built-in fetch API in Node.js 22 and ES module syntax.
const url = 'https://nls-gateway-cn-shanghai.aliyuncs.com/rest/v1/tts/async';
const appkey = process.env.NLS_APP_KEY;
const token = process.env.NLS_TOKEN;
if (!appkey || !token) throw new Error('Set NLS_APP_KEY and NLS_TOKEN');
async function call(address, options = {}) {
const response = await fetch(address, {
...options, signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}`);
const result = await response.json();
if (result.error_code !== 20000000) {
throw new Error(`NLS error ${result.error_code}; request_id=${result.request_id}`);
}
return result;
}
const body = {
header: { appkey, token },
payload: {
tts_request: {
text: 'Welcome to the text-to-speech service.',
voice: 'abby', format: 'wav', sample_rate: 16000,
enable_subtitle: true
},
enable_notify: false
}
};
let result = await call(url, {
method: 'POST', headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body)
});
const taskId = result.data.task_id;
console.log('task_id:', taskId);
const query = new URLSearchParams({ appkey, token, task_id: taskId });
while (true) {
result = await call(`${url}?${query}`);
if (['QUEUEING', 'RUNNING'].includes(result.error_message)) {
await new Promise(resolve => setTimeout(resolve, 10000));
continue;
}
if (!result.data?.audio_address) {
throw new Error(`No audio URL; request_id=${result.request_id}`);
}
console.log(result.data.audio_address);
break;
}
Go
This example uses only the Go standard library.
package main
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"net/url"
"os"
"time"
)
type Result struct {
ErrorCode int `json:"error_code"`
Message string `json:"error_message"`
RequestID string `json:"request_id"`
Data struct {
TaskID string `json:"task_id"`
AudioAddress string `json:"audio_address"`
} `json:"data"`
}
var client = &http.Client{Timeout: 30 * time.Second}
func call(method, address string, body []byte) (Result, error) {
var result Result
request, err := http.NewRequest(method, address, bytes.NewReader(body))
if err != nil { return result, err }
if body != nil { request.Header.Set("Content-Type", "application/json") }
response, err := client.Do(request)
if err != nil { return result, fmt.Errorf("HTTP request failed") }
defer response.Body.Close()
if response.StatusCode < 200 || response.StatusCode >= 300 {
return result, fmt.Errorf("HTTP %d", response.StatusCode)
}
if err := json.NewDecoder(response.Body).Decode(&result); err != nil {
return result, err
}
if result.ErrorCode != 20000000 {
return result, fmt.Errorf("NLS error %d; request_id=%s", result.ErrorCode, result.RequestID)
}
return result, nil
}
func run() error {
endpoint := "https://nls-gateway-cn-shanghai.aliyuncs.com/rest/v1/tts/async"
appkey, token := os.Getenv("NLS_APP_KEY"), os.Getenv("NLS_TOKEN")
if appkey == "" || token == "" { return fmt.Errorf("set NLS_APP_KEY and NLS_TOKEN") }
body, err := json.Marshal(map[string]interface{}{
"header": map[string]string{"appkey": appkey, "token": token},
"payload": map[string]interface{}{
"enable_notify": false,
"tts_request": map[string]interface{}{
"text": "Welcome to the text-to-speech service.",
"voice": "abby", "format": "wav", "sample_rate": 16000,
"enable_subtitle": true,
},
},
})
if err != nil { return err }
result, err := call("POST", endpoint, body)
if err != nil { return err }
fmt.Println("task_id:", result.Data.TaskID)
query := url.Values{"appkey": {appkey}, "token": {token}, "task_id": {result.Data.TaskID}}
for {
result, err = call("GET", endpoint + "?" + query.Encode(), nil)
if err != nil { return err }
if result.Message == "QUEUEING" || result.Message == "RUNNING" {
time.Sleep(10 * time.Second)
continue
}
if result.Data.AudioAddress == "" {
return fmt.Errorf("no audio URL; request_id=%s", result.RequestID)
}
fmt.Println(result.Data.AudioAddress)
return nil
}
}
func main() {
if err := run(); err != nil { fmt.Fprintln(os.Stderr, err); os.Exit(1) }
}
Submit a task
Request
Send a POST request to the endpoint with the Content-Type HTTP header set to application/json. Use a UTF-8-encoded JSON request body.
|
Field |
Type |
Description |
|
header |
Object |
Contains the project |
|
payload.tts_request |
Object |
Synthesis parameters, including the required |
|
payload.enable_notify |
Boolean |
Whether to receive the result through a callback. Default: |
|
payload.notify_url |
String |
The callback URL. Required when |
|
context |
Object |
Client context, such as |
For parameter values, defaults, and limits, see Long-text speech synthesis API reference.
{
"header": {
"appkey": "yourAppkey",
"token": "yourToken"
},
"payload": {
"tts_request": {
"text": "Welcome to the text-to-speech service.",
"voice": "abby",
"format": "wav",
"sample_rate": 16000,
"enable_subtitle": true
},
"enable_notify": false
},
"context": {
"device_id": "my_device_id"
}
}
Response
The response body is JSON. A successful request returns status: 200 and error_code: 20000000. The data.task_id field contains the task ID for subsequent queries. Successful submission does not mean that synthesis is complete.
|
Field |
Type |
Description |
|
status |
Integer |
Request status. |
|
error_code |
Integer |
Service status code. |
|
error_message |
String |
Status or error description. |
|
request_id |
String |
Request ID for troubleshooting. |
|
data.task_id |
String |
Synthesis task ID for querying the result. |
Example response for a successful submission:
{
"status": 200,
"error_code": 20000000,
"error_message": "SUCCESS",
"request_id": "a3370c49a29148e78b39978f98ba****",
"data": {
"task_id": "35d9f813e00b11e9a2ce9ba0d6a2****"
}
}
If a request fails, check the HTTP status code and the error_code and error_message response fields. For example, a request without an authentication token returns:
{
"status": 400,
"error_code": 40000001,
"error_message": "Gateway:ACCESS_DENIED:Missing authorization header!",
"request_id": "a3370c49a29148e78b39978f98ba****",
"url": "/rest/v1/tts/async"
}
Retrieve the result
Polling
Send a GET request to the same endpoint with the following query parameters.
|
Parameter |
Description |
|
appkey |
The Appkey of the project. |
|
token |
A valid NLS token. |
|
task_id |
The task ID returned when the task was submitted. |
https://nls-gateway-cn-shanghai.aliyuncs.com/rest/v1/tts/async?appkey={Appkey}&task_id={task_id}&token={Token}
If error_message is QUEUEING or RUNNING, the task is not complete. Query again later. Example response for a task in progress:
{
"status": 200,
"error_code": 20000000,
"error_message": "RUNNING",
"request_id": "a3370c49a29148e78b39978f98ba****",
"data": {
"task_id": "35d9f813e00b11e9a2ce9ba0d6a2****",
"audio_address": null,
"notify_custom": null
}
}
When synthesis is complete, data.audio_address contains the audio download URL. If the voice supports sentence-level timestamps and enable_subtitle is true, data.sentences contains the timestamps. The begin_time and end_time values are in milliseconds.
{
"status": 200,
"error_code": 20000000,
"error_message": "SUCCESS",
"request_id": "c541eae489af48d69dae2d2e203a****",
"data": {
"task_id": "35d9f813e00b11e9a2ce9ba0d6a2****",
"audio_address": "https://example.com/audio.wav",
"sentences": [
{"text": "Welcome to the text-to-speech service.", "begin_time": "0", "end_time": "2330"}
],
"notify_custom": ""
}
}
Callback
When submitting a task, set payload.enable_notify to true and specify the result receiver URL in payload.notify_url. The callback URL supports HTTP and HTTPS and must use a domain name rather than an IP address.
The service sends the result to the callback URL when synthesis is complete. Example callback payload:
{
"data": {
"audio_address": "https://example.com/audio.wav",
"task_id": "e3a06366d**************e1257693b"
},
"request_id": "edc6b5ab4***********223c8ace71",
"status": 20000000,
"error_code": 20000000,
"error_message": "SUCCESS"
}
The audio_address URL is valid for up to seven days. Download and save the audio file before it expires. The examples use https://example.com/audio.wav as a placeholder. Use the URL returned by the API.
Error codes
For error descriptions and solutions, see Error codes.