Documentation
Introduction
The SPEECHMA API turns text into natural speech. It is a plain REST API: you send JSON over HTTPS and get MP3 audio back. It works with any language that can make a web request, from cURL and Python to Node.js, PHP, Go, Java and no-code tools.
There are three endpoints: /tts makes the audio, /voices lists every voice, and /usage shows how much of your monthly quota is left.
https://speechma.com/api/v1Authentication
Every request needs your API key. You find it in your dashboard. Send it in the X-API-Key header, or as a Bearer token if your tool prefers that.
X-API-Key: sm_your_api_key
Authorization: Bearer sm_your_api_key
Plans and limits
Limits reset with each monthly billing cycle. Every successful reply also tells you how many characters you have left (see /tts).
| Limit | Starter | Pro | Enterprise |
|---|---|---|---|
| Characters a month | 1,000,000 | 50,000,000 | Custom |
| Characters per request | 5,000 | 10,000 | Custom |
| Requests an hour | 100 | 500 | Custom |
| Requests a day | 500 | 5,000 | Custom |
| Requests at the same time | 3 | 10 | Custom |
| Price | $9 a month | $99 a month | Contact us |
POST /tts
Turns text into speech. On success the reply body is the MP3 file itself, so you can save it straight to disk or stream it to a player.
Headers
| Header | Value | |
|---|---|---|
X-API-Key | Required | Your API key |
Content-Type | Required | application/json |
Body (JSON)
| Field | Type | What it does | |
|---|---|---|---|
text | string | Required | The words to speak. Up to 5,000 characters on Starter or 10,000 on Pro. |
voice | string | Required | A voice_id from /voices, for example voice-1. |
pitch | integer | Optional | From -100 to 100. Default 0. |
rate | integer | Optional | Speed from -100 to 100. Default 0. |
style | string | Optional | A speaking style such as cheerful, sad or whispering. Only for voices that list it in their styles. Other values are ignored. |
[break 1s] or [break 500ms] anywhere in text to add silence (up to 10 seconds each, 50 per request). Pause tags are not counted as characters. HD voices support pauses but always use normal pitch, speed and style.Example request
curl -X POST https://speechma.com/api/v1/tts \ -H "X-API-Key: sm_your_api_key" \ -H "Content-Type: application/json" \ -d '{"text":"Hello world!","voice":"voice-1"}' \ --output speech.mp3
import requests response = requests.post( "https://speechma.com/api/v1/tts", headers={ "X-API-Key": "sm_your_api_key", "Content-Type": "application/json", }, json={"text": "Hello world!", "voice": "voice-1"}, ) if response.status_code == 200: with open("speech.mp3", "wb") as f: f.write(response.content) print("Saved speech.mp3") else: print("Error:", response.json()["error"])
import fs from "node:fs"; const response = await fetch("https://speechma.com/api/v1/tts", { method: "POST", headers: { "X-API-Key": "sm_your_api_key", "Content-Type": "application/json", }, body: JSON.stringify({ text: "Hello world!", voice: "voice-1" }), }); if (response.ok) { fs.writeFileSync("speech.mp3", Buffer.from(await response.arrayBuffer())); console.log("Saved speech.mp3"); } else { console.error("Error:", (await response.json()).error); }
$ch = curl_init('https://speechma.com/api/v1/tts'); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true, CURLOPT_POSTFIELDS => json_encode(['text' => 'Hello world!', 'voice' => 'voice-1']), CURLOPT_HTTPHEADER => ['X-API-Key: sm_your_api_key', 'Content-Type: application/json'], ]); $audio = curl_exec($ch); $code = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($code === 200) { file_put_contents('speech.mp3', $audio); }
Reply
# The body is the MP3 file. These headers show your quota: Content-Type: audio/mpeg X-Chars-Used: 250 X-Chars-Remaining: 999750 X-Plan: starter
{
"success": false,
"error": "Quota exceeded",
"code": 429
}GET /voices
Lists every voice you can use, including 456 premium HD voices ("hd": true). Use the voice_id in your /tts requests. Each voice is listed under the language it speaks, so ?language=Czech also returns HD and multilingual voices that speak Czech. A voice with "multilingual": true speaks the listed language when your text is written in it. styles lists the values you can send as style.
Query parameter
| Parameter | Example | |
|---|---|---|
language | Optional | English, Spanish, Hindi, Arabic |
Reply
{
"success": true,
"total": 1041,
"hd_total": 456,
"voices": [
{
"voice_id": "voice-1",
"name": "Adri",
"language": "Afrikaans",
"locale": "af-ZA",
"gender": "Female",
"hd": false,
"multilingual": false,
"styles": []
},
{
"voice_id": "voice-584",
"name": "Adam HD",
"language": "English",
"locale": "en-US",
"gender": "Male",
"hd": true,
"multilingual": false,
"styles": []
}
// ...and 1,025 more
]
}
GET /usage
Shows your plan, the characters you have used this cycle, what is left and when the cycle ends. Calling it does not use any characters.
{
"success": true,
"account": {
"plan": "pro",
"billing_cycle_end": "2026-12-10"
},
"quota": {
"chars_limit": 50000000,
"chars_used": 120450,
"chars_remaining": 49879550,
"usage_percent": 0.24
}
}
Error codes
Every error comes back as JSON with "success": false, a readable "error" message and the number in "code".
| Code | Meaning | How to fix it |
|---|---|---|
400 | Bad request | text or voice is missing or wrong, or the text is longer than your plan allows per request. |
401 | Unauthorized | The API key is missing or wrong. Check the X-API-Key header. |
402 | Payment required | Your plan is not active. Renew it in your dashboard. |
403 | Forbidden | The account is paused. Write to contact@speechma.com. |
405 | Method not allowed | Use POST for /tts and GET for /voices and /usage. |
429 | Too many requests | You hit the hourly, daily or monthly limit. Wait a little, or move to a bigger plan. |
500 | Server error | A short problem on our side. Wait a moment and try again. |
429 and 500, retry after a short wait (for example 2, then 4, then 8 seconds). Most apps never notice a hiccup this way.More examples
Slower, deeper voice with a pause
{
"text": "Welcome to the show. [break 1s] Let us begin.",
"voice": "voice-1",
"pitch": -20,
"rate": -30
}
Check your quota before a long job
import requests headers = {"X-API-Key": "sm_your_api_key"} text = "Hello from my app!" usage = requests.get("https://speechma.com/api/v1/usage", headers=headers).json() if len(text) <= usage["quota"]["chars_remaining"]: audio = requests.post("https://speechma.com/api/v1/tts", headers=headers, json={"text": text, "voice": "voice-1"}) open("output.mp3", "wb").write(audio.content) else: print("Not enough characters left this month")
Try every endpoint in your browser.
Paste your key, pick a voice and hear the result. No code needed.