Skip to main content
A page or an app that talks to the Streaming TTS API directly must not carry your API key: the key would be in the shipped code, in URLs, and in whatever logs those URLs reach. Mint a temporary token on your server instead, hand it to the client, and let the client open its session with that. The token is good for one short window, usage through it is attributed to the key that minted it, and a token cannot mint another token. Browsers cannot set headers on a WebSocket handshake, which is why the API also accepts the credential as a token query parameter. That parameter takes an API key too, but never send one from a device you do not control.
1

Mint a token on your server

GET https://token.us.assemblyai.com/v1/token with your API key in the Authorization header and two query parameters, both required: product=tts, and expires_in_seconds, how long the token can wait before it opens a session.
The Python SDK has no minting helper for Streaming TTS; the request above is all it takes.
The answer is JSON with the token in its token field, a long opaque string:
  • expires_in_seconds, 1 to 600, is the window in which the token can open a session. Past it the connection is refused with 1008 and Unauthorized: Signature has expired. A session that is already open is not affected when its token expires.
  • max_session_duration_seconds, optional and at least 60, caps the session opened with the token. The cap shows up as expires_at on the Begin frame, and the session closes with code 3008 when it is reached. A session never lasts longer than an hour either way.
  • Zones. token.us.assemblyai.com and token.eu.assemblyai.com serve the same route within the US and EU data zones, and a token from either opens sessions on both TTS hosts. token.assemblyai.com serves it too, from the nearest region of either zone. Mint on the token host of the zone your sessions connect to, so that the minting request is processed in that zone too; see Cloud endpoints and data residency.
  • Errors. A key the route refuses, or a request it cannot accept, gets a 4xx answer with the reason in the JSON detail field. A 500 or 502 means the route could not issue a token just then: retry with backoff.
  • Latency. Minting adds a request to your server and one to the token host before the session can open, and a mint over a new connection also waits for the TCP and TLS handshakes. Mint before the client needs the token, for example when the page loads or while the user is still speaking, within the expires_in_seconds window, and reuse one HTTP connection on your server for every mint.
Mint a new token for every connection, reconnects included, and keep expires_in_seconds short. A token is not single-use: until it expires, it opens as many sessions and HTTP requests as it is given to, so a token that leaks from a page can be used by someone else. Never put your API key in the token parameter of a page you ship. The query string is part of the URL, and URLs are written to server access logs, browser history and developer tools.
2

Open the session with the token

The client fetches the token from your server, then passes it in place of the API key. On the WebSocket API a browser sends it in the token query parameter, and a client that can set headers may send it in the Authorization header instead, raw or after Bearer . Send it in one place only: a credential in the header wins over token, and a header that is empty or only Bearer carries none; see Authentication. In the fragments below, fetchToken() and fetch_token() are requests to your own server, and play() is whatever takes a chunk of audio in your app.
Give each connection a token of its own: to reconnect, mint a new token and build a new session with it. The session opens on the US host unless aai.settings.tts_base_url names the EU one; see Cloud endpoints and data residency.
A token that was refused, because it expired or is not a valid token, closes the session with 1008 and error_type unauthorized before Begin. See Error handling.

Where else a token works

A temporary token works wherever an API key does, in the Authorization header of the HTTP API, so a page can call it directly too. Under a token, max_session_duration_seconds also caps how long an HTTP request may run. See HTTP API basics.

Speak a whole text over HTTP from a page

A page with a whole text to speak and no need for barge-in can call the HTTP API directly, one fetch per text, played as it streams in. Every route answers cross-origin requests from any origin. Send the token in the Authorization header like a key; the HTTP API reads the credential from that header only. A token stays valid for any number of requests until it expires, so the page can retry a failed request with the same token. The client below schedules each piece of audio on one AudioContext as it arrives, so playback starts with the first bytes. Create and resume the context in a user gesture, and reuse it for every request. /tts-token is a route on your server that returns a freshly minted token as JSON.
Every response header can be read from a page, errors included, so X-Request-Id and the X-TTS-* headers are available to your error reporting.