Text to speech API

Turn text into natural MP3, WAV or OGG audio from your own app, website or script. The API uses the same voices and languages as the site.

API access comes with Pro and Enterprise. Conversions count against your plan's characters, the same as conversions on the site: Pro allows 100,000 characters per request and 3,000,000 a month.

Create an account then upgrade to Pro for API access.

Authentication

Make a key under API keys on your account page and send it with every request:

Authorization: Bearer sk_your_key_here

If your tool can't set that header, send X-API-Key: sk_your_key_here instead.

Keep keys on your server. Don't put them in web pages or apps that other people can download, because anyone with the key can use your characters. If a key leaks, delete it on your account page and make a new one.

Convert text to speech

POST https://www.ttsmp3.ca/api/v1/speech
Content-Type: application/json

{
  "text": "Hello! This is a test of the TTSMP3 API.",
  "lang": "english",
  "voice": "af_heart",
  "format": "mp3"
}
FieldWhat it does
textRequired. The text to read.
langLanguage id from GET /languages. Default english.
voiceVoice id from GET /voices. Default: the language's first (best) voice.
formatmp3 (default), wav or ogg.
styleOne of neutral, happy, excited, calm, sad, angry, serious. Default neutral.
speed0.5 to 2.0. Default 1.0.
pitch0 to 99. Default 50.
pauseExtra pause between sentences in seconds, 0 to 2. Default 0.2.
waitOptional. For long text, wait up to this many seconds (at most 120) for the audio before answering.

Short text is converted right away and the answer has status done (HTTP 200):

{
  "id": "3f9c0d2e8a4b4c6f9e1d2a3b4c5d6e7f",
  "status": "done",
  "chars": 41,
  "audio_url": "https://www.ttsmp3.ca/audio/…mp3",
  "download_url": "https://www.ttsmp3.ca/audio/…mp3?download=1"
}

Long text is queued and the answer has status queued or working (HTTP 202). Check it every few seconds until it is done or failed:

GET https://www.ttsmp3.ca/api/v1/speech/{id}

Or fetch the audio file itself, which redirects to the file once it is ready:

GET https://www.ttsmp3.ca/api/v1/speech/{id}/audio

Audio files are kept for a few days. Download them if you need them longer.

Languages, voices and usage

GET https://www.ttsmp3.ca/api/v1/languages
GET https://www.ttsmp3.ca/api/v1/voices?lang=english
GET https://www.ttsmp3.ca/api/v1/usage

/usage shows your plan, its limits and the characters used this month.

Examples

curl

curl -X POST https://www.ttsmp3.ca/api/v1/speech \
  -H "Authorization: Bearer $TTSMP3_KEY" \
  -H "Content-Type: application/json" \
  -d '{"text":"Hello from the API","lang":"english","wait":60}'

PHP

$ch = curl_init('https://www.ttsmp3.ca/api/v1/speech');
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . getenv('TTSMP3_KEY'), 'Content-Type: application/json'],
    CURLOPT_POSTFIELDS => json_encode(['text' => 'Hello from PHP', 'lang' => 'english', 'wait' => 60]),
]);
$job = json_decode(curl_exec($ch), true);
if (($job['status'] ?? '') === 'done') {
    file_put_contents('hello.mp3', file_get_contents($job['audio_url']));
}

Python

import os, time, requests

API = "https://www.ttsmp3.ca/api/v1"
H = {"Authorization": "Bearer " + os.environ["TTSMP3_KEY"]}

job = requests.post(API + "/speech", headers=H, json={"text": "Hello from Python", "lang": "english"}).json()
while job.get("status") in ("queued", "working"):
    time.sleep(3)
    job = requests.get(API + "/speech/" + job["id"], headers=H).json()
if job.get("status") == "done":
    open("hello.mp3", "wb").write(requests.get(job["audio_url"]).content)
else:
    print(job.get("error"))

JavaScript (Node.js 18+)

const API = "https://www.ttsmp3.ca/api/v1";
const headers = { Authorization: `Bearer ${process.env.TTSMP3_KEY}`, "Content-Type": "application/json" };

const job = await (await fetch(`${API}/speech`, {
  method: "POST", headers,
  body: JSON.stringify({ text: "Hello from Node", lang: "english", wait: 60 }),
})).json();
console.log(job.audio_url ?? job);

Errors

Errors come back as JSON with an error message, for example {"error": "Choose a voice from the list."}.

StatusMeaning
401The API key is missing or wrong.
402The text is longer than your plan allows, or this month's characters are used up.
403The account isn't on Pro or Enterprise, or is suspended.
404Unknown endpoint, language or conversion id.
422Something in the request is wrong, such as an unknown voice or format.
429Too many requests (60 a minute per key), or a long conversion is already in progress.

Questions or need higher limits? Contact us about Enterprise.