RESTful API

Updated at:

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.

Note

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.

https://nls-gateway-cn-shanghai.aliyuncs.com/rest/v1/tts/async

VPC access

An Alibaba Cloud ECS instance in China (Shanghai).

http://nls-gateway-cn-shanghai-internal.aliyuncs.com/rest/v1/tts/async

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 appkey and authentication token. Both are required.

payload.tts_request

Object

Synthesis parameters, including the required text and optional parameters such as voice, audio format, and sample rate.

payload.enable_notify

Boolean

Whether to receive the result through a callback. Default: false.

payload.notify_url

String

The callback URL. Required when enable_notify is true.

context

Object

Client context, such as device_id.

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. 20000000 indicates a successful request.

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"
}
Note

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.