API Documentation
One unified API. 24+ TTS models, speech-to-text, voice cloning, and audio tools routed through a single endpoint.
Authentication
All authenticated requests require an API key passed in the Authorization header as a Bearer token. API keys are prefixed with sk-tts- and can be generated from your account page or via the API.
API Key Authentication
Include your API key in every request:
curl https://whatistts.com/api/v1/tts/ \
-H "Authorization: Bearer sk-tts-your-api-key" \
-H "Content-Type: application/json" \
-d '{"text": "Hello world", "model": "openai-tts-1"}'import requests
API_KEY = "sk-tts-your-api-key"
BASE_URL = "https://whatistts.com/api"
headers = {
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json"
}
response = requests.post(f"{BASE_URL}/v1/tts/", headers=headers, json={
"text": "Hello world",
"model": "openai-tts-1"
})const API_KEY = "sk-tts-your-api-key";
const BASE_URL = "https://whatistts.com/api";
const response = await fetch(`${BASE_URL}/v1/tts/`, {
method: "POST",
headers: {
"Authorization": `Bearer ${API_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
text: "Hello world",
model: "openai-tts-1"
})
});package main
import (
"bytes"
"encoding/json"
"net/http"
)
const (
apiKey = "sk-tts-your-api-key"
baseURL = "https://whatistts.com/api"
)
func main() {
payload, _ := json.Marshal(map[string]string{
"text": "Hello world",
"model": "openai-tts-1",
})
req, _ := http.NewRequest("POST", baseURL+"/v1/tts/", bytes.NewBuffer(payload))
req.Header.Set("Authorization", "Bearer "+apiKey)
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, _ := client.Do(req)
defer resp.Body.Close()
}Session Tokens (Frontend)
For browser-based applications, request a temporary session token using your user session cookie. This is used internally by the WhatIsTTS frontend.
# Requires an active session cookie
curl -X POST https://whatistts.com/api/session-token/ \
-H "Cookie: sessionid=your-session-cookie" \
-H "X-CSRFToken: your-csrf-token"
# Response:
# {
# "authenticated": true,
# "api_key": "sk-tts-...",
# "credits": 500,
# "plan": "pro",
# "is_plan_active": true
# }import requests
session = requests.Session()
# Log in first to get session cookie
session.post("https://whatistts.com/login/", data={
"email": "user@example.com",
"password": "your-password"
})
# Then get session token
response = session.post("https://whatistts.com/api/session-token/")
data = response.json()
api_key = data["api_key"]
credits = data["credits"]// In-browser: session cookie is sent automatically
const response = await fetch("/api/session-token/", {
method: "POST",
headers: {
"X-CSRFToken": window.TTS.csrfToken
}
});
const data = await response.json();
// data.api_key, data.credits, data.plan// Session tokens are primarily for browser-based usage.
// For server-to-server integrations, use API key auth instead.
req, _ := http.NewRequest("POST", "https://whatistts.com/api/session-token/", nil)
req.Header.Set("Cookie", "sessionid=your-session-cookie")
req.Header.Set("X-CSRFToken", "your-csrf-token")
client := &http.Client{}
resp, _ := client.Do(req)Generate API Keys
Create new API keys programmatically:
curl -X POST https://whatistts.com/api/generate-key/ \
-H "Authorization: Bearer sk-tts-your-api-key" \
-H "Content-Type: application/json" \
-d '{"name": "My Production Key"}'
# Response:
# {"key": "sk-tts-abc123...", "name": "My Production Key", "id": 42}response = requests.post(
"https://whatistts.com/api/generate-key/",
headers=headers,
json={"name": "My Production Key"}
)
new_key = response.json()["key"]
print(f"New API key: {new_key}")const response = await fetch("https://whatistts.com/api/generate-key/", {
method: "POST",
headers: {
"Authorization": `Bearer ${API_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({ name: "My Production Key" })
});
const { key, name, id } = await response.json();payload, _ := json.Marshal(map[string]string{
"name": "My Production Key",
})
req, _ := http.NewRequest("POST", baseURL+"/generate-key/", bytes.NewBuffer(payload))
req.Header.Set("Authorization", "Bearer "+apiKey)
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, _ := client.Do(req)
defer resp.Body.Close()Text to Speech
Convert text to speech using any of 24+ available models. The response is an audio file (binary) or a JSON object containing a URL to the generated audio, depending on the model and configuration.
Request Parameters
| Parameter | Type | Description |
|---|---|---|
text required |
string | The text to synthesize. Free tier is limited to 500 characters. Authenticated users can send up to 5,000 characters per request. |
model optional |
string | TTS model to use. Defaults to google-standard. See Models for available options. Model determines the credit cost per request. |
voice optional |
string | Voice ID to use. Defaults vary by model. See Voices for available voices per model. Example: alloy (OpenAI), en-US-Standard-A (Google Cloud), Joanna (Amazon Polly). |
format optional |
string | Output audio format. One of mp3, wav, ogg, flac. Defaults to mp3. |
speed optional |
float | Speaking speed multiplier. Range: 0.5 to 2.0. Defaults to 1.0. Not all models support speed adjustment. |
Model Tiers & Pricing
| Tier | Cost | Models |
|---|---|---|
| Standard | 2 credits / 1K chars | OpenAI TTS-1, Google Cloud Standard, Azure Neural, Amazon Polly Neural, ElevenLabs Flash, ElevenLabs Turbo v2, Cartesia Sonic Turbo, Deepgram Aura, and more |
| Premium | 5 credits / 1K chars | ElevenLabs Multilingual v2, OpenAI TTS-1-HD, OpenAI GPT-4o-mini TTS, Google Cloud Studio, Azure HD, Cartesia Sonic 2, Cartesia Sonic 3, Deepgram Aura HD, and more |
Example Request
curl -X POST https://whatistts.com/api/v1/tts/ \
-H "Authorization: Bearer sk-tts-your-api-key" \
-H "Content-Type: application/json" \
-d '{
"text": "Welcome to the future of text to speech.",
"model": "openai-tts-1",
"voice": "alloy",
"format": "mp3",
"speed": 1.0
}' \
--output speech.mp3import requests
API_KEY = "sk-tts-your-api-key"
response = requests.post(
"https://whatistts.com/api/v1/tts/",
headers={
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json"
},
json={
"text": "Welcome to the future of text to speech.",
"model": "openai-tts-1",
"voice": "alloy",
"format": "mp3",
"speed": 1.0
}
)
if response.status_code == 200:
content_type = response.headers.get("Content-Type", "")
if "audio/" in content_type:
# Binary audio response
with open("speech.mp3", "wb") as f:
f.write(response.content)
print("Saved to speech.mp3")
else:
# JSON response with URL
data = response.json()
print(f"Audio URL: {data.get('url')}")
else:
print(f"Error {response.status_code}: {response.text}")const API_KEY = "sk-tts-your-api-key";
const response = await fetch("https://whatistts.com/api/v1/tts/", {
method: "POST",
headers: {
"Authorization": `Bearer ${API_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
text: "Welcome to the future of text to speech.",
model: "openai-tts-1",
voice: "alloy",
format: "mp3",
speed: 1.0
})
});
if (response.ok) {
const contentType = response.headers.get("Content-Type") || "";
if (contentType.includes("audio/")) {
const blob = await response.blob();
const url = URL.createObjectURL(blob);
// Play it
const audio = new Audio(url);
audio.play();
} else {
const data = await response.json();
console.log("Audio URL:", data.url);
}
}package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
)
func main() {
payload, _ := json.Marshal(map[string]interface{}{
"text": "Welcome to the future of text to speech.",
"model": "openai-tts-1",
"voice": "alloy",
"format": "mp3",
"speed": 1.0,
})
req, _ := http.NewRequest("POST", "https://whatistts.com/api/v1/tts/", bytes.NewBuffer(payload))
req.Header.Set("Authorization", "Bearer sk-tts-your-api-key")
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
fmt.Println("Error:", err)
return
}
defer resp.Body.Close()
if resp.StatusCode == 200 {
file, _ := os.Create("speech.mp3")
defer file.Close()
io.Copy(file, resp.Body)
fmt.Println("Saved to speech.mp3")
} else {
body, _ := io.ReadAll(resp.Body)
fmt.Printf("Error %d: %s\n", resp.StatusCode, string(body))
}
}Response
On success, the API returns either:
- Binary audio with
Content-Type: audio/mpeg(oraudio/wav,audio/ogg,audio/flac) -- stream directly or save to file. - JSON with a
urlfield pointing to the generated audio file.
{
"url": "https://whatistts.com/media/output/abc123.mp3",
"model": "openai-tts-1",
"voice": "alloy",
"duration": 3.2,
"credits_charged": 2
}Try It
Try It
Speech to Text
Transcribe audio files to text. Supports Whisper, Faster Whisper, and SenseVoice models. The request must use multipart/form-data encoding to upload the audio file.
Request Parameters
| Parameter | Type | Description |
|---|---|---|
file required |
file | Audio file to transcribe. Supported formats: mp3, wav, ogg, flac, m4a, webm. Max size: 50MB. |
model optional |
string | STT model to use. One of whisper, faster-whisper, sensevoice. Defaults to faster-whisper. |
language optional |
string | ISO 639-1 language code (e.g., en, es, fr). Auto-detected if not specified. |
Cost: 2 credits per minute of audio (rounded up).
Example Request
curl -X POST https://whatistts.com/api/v1/stt/ \
-H "Authorization: Bearer sk-tts-your-api-key" \
-F "file=@recording.mp3" \
-F "model=faster-whisper" \
-F "language=en"import requests
API_KEY = "sk-tts-your-api-key"
with open("recording.mp3", "rb") as f:
response = requests.post(
"https://whatistts.com/api/v1/stt/",
headers={"Authorization": f"Bearer {API_KEY}"},
files={"file": ("recording.mp3", f, "audio/mpeg")},
data={
"model": "faster-whisper",
"language": "en"
}
)
data = response.json()
print(data["text"])
print(f"Duration: {data['duration']}s")const API_KEY = "sk-tts-your-api-key";
const formData = new FormData();
formData.append("file", audioFile); // File or Blob object
formData.append("model", "faster-whisper");
formData.append("language", "en");
const response = await fetch("https://whatistts.com/api/v1/stt/", {
method: "POST",
headers: {
"Authorization": `Bearer ${API_KEY}`
// Do NOT set Content-Type -- browser sets it with boundary
},
body: formData
});
const data = await response.json();
console.log(data.text);package main
import (
"bytes"
"fmt"
"io"
"mime/multipart"
"net/http"
"os"
)
func main() {
file, _ := os.Open("recording.mp3")
defer file.Close()
var body bytes.Buffer
writer := multipart.NewWriter(&body)
part, _ := writer.CreateFormFile("file", "recording.mp3")
io.Copy(part, file)
writer.WriteField("model", "faster-whisper")
writer.WriteField("language", "en")
writer.Close()
req, _ := http.NewRequest("POST", "https://whatistts.com/api/v1/stt/", &body)
req.Header.Set("Authorization", "Bearer sk-tts-your-api-key")
req.Header.Set("Content-Type", writer.FormDataContentType())
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
fmt.Println("Error:", err)
return
}
defer resp.Body.Close()
result, _ := io.ReadAll(resp.Body)
fmt.Println(string(result))
}Response
{
"text": "Hello, this is a transcription of the uploaded audio file.",
"language": "en",
"duration": 12.5,
"segments": [
{
"start": 0.0,
"end": 3.2,
"text": "Hello, this is a transcription"
},
{
"start": 3.2,
"end": 5.8,
"text": "of the uploaded audio file."
}
],
"credits_charged": 2
}Voice Cloning
Clone a voice from a reference audio sample and generate speech in that voice. Upload a short audio clip (5-30 seconds recommended) as the reference, along with the text you want spoken. Requires a model that supports cloning (ElevenLabs, OpenAI, Cartesia, and others).
Cost: 20 credits flat per clone operation.
Request Parameters
| Parameter | Type | Description |
|---|---|---|
reference_audio required |
file | Audio file of the voice to clone. 5-30 seconds recommended. Formats: mp3, wav, ogg, flac. Max 50MB. |
text required |
string | Text to synthesize in the cloned voice. |
model optional |
string | Model to use for cloning. Must support voice cloning. Defaults to elevenlabs-multilingual-v2. |
language optional |
string | Target language for synthesis. ISO 639-1 code. Defaults to en. |
Example Request
curl -X POST https://whatistts.com/api/v1/voice-clone/ \
-H "Authorization: Bearer sk-tts-your-api-key" \
-F "reference_audio=@my_voice.wav" \
-F "text=This is my cloned voice speaking." \
-F "model=elevenlabs-multilingual-v2" \
-F "language=en"import requests
API_KEY = "sk-tts-your-api-key"
with open("my_voice.wav", "rb") as ref:
response = requests.post(
"https://whatistts.com/api/v1/voice-clone/",
headers={"Authorization": f"Bearer {API_KEY}"},
files={"reference_audio": ("my_voice.wav", ref, "audio/wav")},
data={
"text": "This is my cloned voice speaking.",
"model": "elevenlabs-multilingual-v2",
"language": "en"
}
)
if response.status_code == 200:
with open("cloned_output.mp3", "wb") as f:
f.write(response.content)
print("Saved cloned speech to cloned_output.mp3")const API_KEY = "sk-tts-your-api-key";
const formData = new FormData();
formData.append("reference_audio", referenceFile); // File object
formData.append("text", "This is my cloned voice speaking.");
formData.append("model", "elevenlabs-multilingual-v2");
formData.append("language", "en");
const response = await fetch("https://whatistts.com/api/v1/voice-clone/", {
method: "POST",
headers: {
"Authorization": `Bearer ${API_KEY}`
},
body: formData
});
if (response.ok) {
const blob = await response.blob();
const url = URL.createObjectURL(blob);
const audio = new Audio(url);
audio.play();
}package main
import (
"bytes"
"fmt"
"io"
"mime/multipart"
"net/http"
"os"
)
func main() {
file, _ := os.Open("my_voice.wav")
defer file.Close()
var body bytes.Buffer
writer := multipart.NewWriter(&body)
part, _ := writer.CreateFormFile("reference_audio", "my_voice.wav")
io.Copy(part, file)
writer.WriteField("text", "This is my cloned voice speaking.")
writer.WriteField("model", "elevenlabs-multilingual-v2")
writer.WriteField("language", "en")
writer.Close()
req, _ := http.NewRequest("POST", "https://whatistts.com/api/v1/voice-clone/", &body)
req.Header.Set("Authorization", "Bearer sk-tts-your-api-key")
req.Header.Set("Content-Type", writer.FormDataContentType())
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
fmt.Println("Error:", err)
return
}
defer resp.Body.Close()
output, _ := os.Create("cloned_output.mp3")
defer output.Close()
io.Copy(output, resp.Body)
fmt.Println("Saved to cloned_output.mp3")
}Response
Returns binary audio of the synthesized cloned voice, or a JSON object with a URL.
{
"url": "https://whatistts.com/media/output/clone_xyz789.mp3",
"model": "elevenlabs-multilingual-v2",
"duration": 4.1,
"credits_charged": 20
}Models Supporting Voice Cloning
Voices
Retrieve the full voice catalog. Filter by model or language using query parameters. This endpoint does not require authentication.
Query Parameters
| Parameter | Type | Description |
|---|---|---|
model optional |
string | Filter voices by model name. Example: openai-tts-1, elevenlabs-multilingual-v2. |
language optional |
string | Filter voices by language code. Example: en, es, fr. |
Example Request
# Get all voices
curl https://whatistts.com/api/v1/voices/
# Filter by model
curl "https://whatistts.com/api/v1/voices/?model=openai-tts-1"
# Filter by language
curl "https://whatistts.com/api/v1/voices/?language=en"
# Filter by both
curl "https://whatistts.com/api/v1/voices/?model=openai-tts-1&language=en"import requests
# Get all voices
response = requests.get("https://whatistts.com/api/v1/voices/")
voices = response.json()["voices"]
print(f"Total voices: {len(voices)}")
# Filter by model
response = requests.get("https://whatistts.com/api/v1/voices/", params={
"model": "openai-tts-1"
})
openai_voices = response.json()["voices"]
for voice in openai_voices:
print(f" {voice['voice_id']} - {voice['name']} ({voice['language']})")// Get all voices
const response = await fetch("https://whatistts.com/api/v1/voices/");
const { voices } = await response.json();
console.log(`Total voices: ${voices.length}`);
// Filter by model
const filtered = await fetch("https://whatistts.com/api/v1/voices/?model=openai-tts-1");
const { voices: openaiVoices } = await filtered.json();
openaiVoices.forEach(v => {
console.log(`${v.voice_id} - ${v.name} (${v.language})`);
});package main
import (
"encoding/json"
"fmt"
"io"
"net/http"
)
type VoicesResponse struct {
Voices []struct {
ModelName string `json:"model_name"`
VoiceID string `json:"voice_id"`
Name string `json:"name"`
Language string `json:"language"`
Gender string `json:"gender"`
PreviewURL string `json:"preview_url"`
IsPremium bool `json:"is_premium"`
} `json:"voices"`
}
func main() {
resp, _ := http.Get("https://whatistts.com/api/v1/voices/?model=openai-tts-1")
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
var result VoicesResponse
json.Unmarshal(body, &result)
for _, v := range result.Voices {
fmt.Printf("%s - %s (%s)\n", v.VoiceID, v.Name, v.Language)
}
}Response
{
"voices": [
{
"model_name": "openai-tts-1",
"voice_id": "alloy",
"name": "Alloy",
"language": "en",
"gender": "neutral",
"preview_url": "https://whatistts.com/media/previews/openai-tts-1_alloy.mp3",
"is_premium": false,
"tags": ["natural", "warm"]
},
{
"model_name": "openai-tts-1",
"voice_id": "nova",
"name": "Nova",
"language": "en",
"gender": "female",
"preview_url": "https://whatistts.com/media/previews/openai-tts-1_nova.mp3",
"is_premium": false,
"tags": ["clear", "professional"]
}
]
}Models
Retrieve the list of all available TTS models with their tier and credit cost. This endpoint does not require authentication.
Example Request
curl https://whatistts.com/api/v1/models/import requests
response = requests.get("https://whatistts.com/api/v1/models/")
models = response.json()["models"]
for model in models:
print(f"{model['name']:20s} tier={model['tier']:10s} cost={model['credits_per_1k']} credits/1K chars")const response = await fetch("https://whatistts.com/api/v1/models/");
const { models } = await response.json();
models.forEach(m => {
console.log(`${m.name} (${m.tier}) - ${m.credits_per_1k} credits/1K chars`);
});package main
import (
"encoding/json"
"fmt"
"io"
"net/http"
)
type ModelsResponse struct {
Models []struct {
Name string `json:"name"`
Tier string `json:"tier"`
CreditsPer1K int `json:"credits_per_1k"`
} `json:"models"`
}
func main() {
resp, _ := http.Get("https://whatistts.com/api/v1/models/")
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
var result ModelsResponse
json.Unmarshal(body, &result)
for _, m := range result.Models {
fmt.Printf("%-20s tier=%-10s cost=%d credits/1K\n", m.Name, m.Tier, m.CreditsPer1K)
}
}Response
{
"models": [
{"name": "google-standard", "tier": "standard", "credits_per_1k": 2},
{"name": "openai-tts-1", "tier": "standard", "credits_per_1k": 2},
{"name": "google-neural2", "tier": "standard", "credits_per_1k": 2},
{"name": "azure-neural", "tier": "standard", "credits_per_1k": 2},
{"name": "polly-neural", "tier": "standard", "credits_per_1k": 2},
{"name": "elevenlabs-flash", "tier": "standard", "credits_per_1k": 2},
{"name": "deepgram-aura", "tier": "standard", "credits_per_1k": 2},
{"name": "elevenlabs-multilingual-v2", "tier": "premium", "credits_per_1k": 5},
{"name": "openai-tts-1-hd", "tier": "premium", "credits_per_1k": 5},
{"name": "cartesia-sonic", "tier": "premium", "credits_per_1k": 5}
]
}Available Models
| Model | Tier | Speed | Quality | Cloning | Developer |
|---|---|---|---|---|---|
elevenlabs-multilingual-v2 |
premium | fast | ElevenLabs | ||
elevenlabs-flash-v2 |
standard | fast | ElevenLabs | ||
elevenlabs-turbo-v2 |
standard | fast | ElevenLabs | ||
openai-tts-1 |
standard | fast | -- | OpenAI | |
openai-tts-1-hd |
premium | medium | -- | OpenAI | |
openai-gpt-4o-mini-tts |
premium | medium | -- | OpenAI | |
google-standard |
standard | fast | -- | ||
google-wavenet |
standard | fast | -- | ||
google-neural2 |
standard | fast | -- | ||
google-studio |
premium | medium | -- | ||
azure-neural |
standard | fast | Microsoft | ||
azure-neural-hd |
premium | medium | Microsoft | ||
polly-standard |
standard | fast | -- | Amazon Web Services | |
polly-neural |
standard | fast | -- | Amazon Web Services | |
polly-generative |
premium | medium | -- | Amazon Web Services | |
polly-long-form |
premium | medium | -- | Amazon Web Services | |
deepgram-aura-2 |
premium | fast | -- | Deepgram | |
cartesia-sonic-2 |
premium | fast | Cartesia | ||
cartesia-sonic-turbo |
standard | very fast | Cartesia | ||
cartesia-sonic-3 |
premium | fast | Cartesia |
STT Models
| Model | Languages | Developer | Description |
|---|---|---|---|
openai-whisper-1 |
57+ | OpenAI | OpenAI's robust speech recognition API supporting 57 languages. |
openai-gpt-4o-transcribe |
57+ | OpenAI | OpenAI's latest transcription model with improved accuracy and structured output. |
deepgram-nova-3 |
36+ | Deepgram | Deepgram's fastest and most accurate STT with real-time streaming and diarization. |
google-chirp-3 |
100+ | Google's latest speech recognition with 100+ language support and auto-punctuation. | |
azure-stt |
100+ | Microsoft | Azure speech recognition with 100+ languages, custom models, and real-time streaming. |
elevenlabs-scribe |
99+ | ElevenLabs | ElevenLabs' speech-to-text with speaker diarization and 99 language support. |
Rate Limits
WhatIsTTS enforces rate limits to ensure fair usage across all users. Limits differ based on authentication status and plan.
| Tier | Rate Limit | Character Limit | Models | Auth Required |
|---|---|---|---|---|
| Free (No Account) | 3 requests / hour | 500 characters / request | Google Cloud Standard and Amazon Polly only | No |
| Free Account | 10 requests / hour | 5,000 characters / request | All free + standard models | Yes |
| Paid Plan | Per plan allocation | 5,000 characters / request | All models | Yes |
Rate Limit Headers
When you exceed rate limits, the API returns a 429 Too Many Requests response:
{
"error": "rate_limit",
"message": "Free tier limit reached. Sign up for more."
}Handling Rate Limits
# Check the HTTP status code
response=$(curl -s -w "\n%{http_code}" -X POST https://whatistts.com/api/v1/tts/ \
-H "Authorization: Bearer sk-tts-your-api-key" \
-H "Content-Type: application/json" \
-d '{"text": "Hello", "model": "openai-tts-1"}')
status=$(echo "$response" | tail -1)
body=$(echo "$response" | head -1)
if [ "$status" = "429" ]; then
echo "Rate limited. Wait and retry."
fiimport time
import requests
API_KEY = "sk-tts-your-api-key"
def generate_speech(text, model="openai-tts-1", retries=3):
for attempt in range(retries):
response = requests.post(
"https://whatistts.com/api/v1/tts/",
headers={
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json"
},
json={"text": text, "model": model}
)
if response.status_code == 200:
return response.content
elif response.status_code == 429:
wait = 2 ** attempt # Exponential backoff
print(f"Rate limited. Retrying in {wait}s...")
time.sleep(wait)
else:
response.raise_for_status()
raise Exception("Max retries exceeded")async function generateSpeech(text, model = "openai-tts-1", retries = 3) {
for (let attempt = 0; attempt < retries; attempt++) {
const response = await fetch("https://whatistts.com/api/v1/tts/", {
method: "POST",
headers: {
"Authorization": `Bearer ${API_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({ text, model })
});
if (response.ok) return await response.blob();
if (response.status === 429) {
const wait = Math.pow(2, attempt) * 1000;
console.log(`Rate limited. Retrying in ${wait}ms...`);
await new Promise(r => setTimeout(r, wait));
continue;
}
throw new Error(`HTTP ${response.status}: ${await response.text()}`);
}
throw new Error("Max retries exceeded");
}package main
import (
"bytes"
"encoding/json"
"fmt"
"math"
"net/http"
"time"
)
func generateSpeech(text, model string) (*http.Response, error) {
maxRetries := 3
for attempt := 0; attempt < maxRetries; attempt++ {
payload, _ := json.Marshal(map[string]string{
"text": text,
"model": model,
})
req, _ := http.NewRequest("POST", "https://whatistts.com/api/v1/tts/", bytes.NewBuffer(payload))
req.Header.Set("Authorization", "Bearer sk-tts-your-api-key")
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
if resp.StatusCode == 200 {
return resp, nil
}
resp.Body.Close()
if resp.StatusCode == 429 {
wait := time.Duration(math.Pow(2, float64(attempt))) * time.Second
fmt.Printf("Rate limited. Retrying in %v...\n", wait)
time.Sleep(wait)
continue
}
return nil, fmt.Errorf("HTTP %d", resp.StatusCode)
}
return nil, fmt.Errorf("max retries exceeded")
}Credit Costs Reference
| Operation | Cost | Unit |
|---|---|---|
| TTS (trial: Google Standard, Polly Standard) | 0 credits | per 1K chars |
| TTS (standard models) | 2 credits | per 1K chars |
| TTS (premium models) | 5 credits | per 1K chars |
| Speech to Text | 2 credits | per minute |
| Voice Cloning | 20 credits | flat |
| Voice Conversion | 5 credits | per minute |
| Speech Translation | 5 credits | per minute |
| Audio Enhancement | 3 credits | per minute |
| Echo/Reverb Removal | 3 credits | per minute |
| Stem Splitting | 5 credits | flat |
| Voice Chat | 3 credits | per minute |
| Audio Converter / Key-BPM / Recorder | 0 credits | free |
Error Codes
WhatIsTTS uses standard HTTP status codes. All error responses return JSON with an error field and an optional message field.
| Code | Status | Description |
|---|---|---|
| 200 | OK | Request succeeded. Response contains the generated audio or requested data. |
| 400 | Bad Request | The request was malformed. Check that required parameters are present and valid. Common causes: missing text field, invalid model name, unsupported format. |
| 401 | Unauthorized | Authentication failed. The API key is missing, invalid, or revoked. Ensure your Authorization header contains a valid Bearer sk-tts-... token. |
| 402 | Payment Required | Insufficient credits for this operation. The response includes credits_remaining and credits_needed fields. Purchase more credits. |
| 403 | Forbidden | You do not have permission for this action. Common causes: trying to use a paid model without an account, email not verified, or API key lacks required scope. |
| 429 | Too Many Requests | Rate limit exceeded. Free tier: 3 requests per hour. Implement exponential backoff and retry after a delay. See Rate Limits for details. |
| 500 | Internal Server Error | Something went wrong on our end. This may indicate a temporary issue with an upstream provider. Retry after a short delay. If the problem persists, contact support. |
Error Response Format
{
"authorized": false,
"error": "invalid_api_key"
}{
"authorized": false,
"error": "insufficient_credits",
"credits_remaining": 1,
"credits_needed": 5
}{
"error": "rate_limit",
"message": "Free tier limit reached. Sign up for more."
}{
"error": "upstream_provider_error",
"message": "The TTS provider returned an unexpected error. Please try again."
}Best Practices
- Always check the status code before processing the response body.
- Implement exponential backoff for 429 and 500 errors. Start at 1 second and double on each retry, up to 3 attempts.
- Monitor your credit balance using the
credits_remainingfield in verify and error responses to avoid unexpected 402 errors. - Validate input client-side before sending to reduce unnecessary API calls. Check text length, model names, and format values.
- Keep API keys secret. Never expose
sk-tts-keys in client-side code or public repositories. Use session tokens for browser-based apps.