API Reference
Integrate Sooddy text-to-speech into your own apps and scripts. A Pro subscription is required.
https://sooddy.com/api
Authentication
All protected endpoints use Bearer token authentication via Laravel Sanctum. Generate a personal access token from Profile → API access (Pro plan required).
Authorization: Bearer YOUR_API_TOKEN
✓ Pro subscribers
Full API access
✗ Free users
Requests return 403
✓ Public endpoints
No auth needed
Error codes
All errors return JSON with a message field and an optional code field.
| HTTP | Code | Meaning |
|---|---|---|
| 401 | unauthenticated | Missing or invalid token |
| 403 | pro_required | Token owner is not a Pro subscriber |
| 402 | insufficient_balance | Wallet balance too low |
| 422 | — | Validation error — check request body |
| 429 | quota_exceeded | Daily character quota exhausted |
| 429 | — | Rate limit hit (40 req / min on /tts) |
| 500 | — | Server error — contact support |
/api/voices
List voices
Returns all active voices. No authentication required.
curl https://sooddy.com/api/voices
{
"data": [
{
"id": 42,
"name": "Amy",
"slug": "en_amy",
"language": "en",
"engine": "piper",
"gender": "female",
"preview_url": "https:\/\/sooddy.com\/audio\/example\/stream"
},
{
"...": "more voices"
}
]
}
/api/languages
List languages
Returns all language codes with their display names.
curl https://sooddy.com/api/languages
{
"data": [
{
"code": "en",
"name": "English",
"voice_count": 48
},
{
"code": "de",
"name": "German",
"voice_count": 12
},
{
"...": "more languages"
}
]
}
/api/quota
Get quota
Returns your current daily character quota and usage.
curl https://sooddy.com/api/quota \
-H "Authorization: Bearer YOUR_API_TOKEN"
{
"data": {
"limit": 100000,
"used": 12450,
"remaining": 87550,
"resets_at": "2026-07-16T00:00:00Z"
}
}
/api/history
List history
Returns a paginated list of your recent TTS generations.
| Query param | Type | Default | Description |
|---|---|---|---|
| page | integer | 1 | Page number |
| per_page | integer | 15 | Items per page (max 100) |
curl "https://sooddy.com/api/history?page=1&per_page=15" \
-H "Authorization: Bearer YOUR_API_TOKEN"
{
"data": [
{
"uuid": "a1b2c3d4-...",
"status": "completed",
"text": "Hello world",
"voice": "en_amy",
"duration_s": 1.24,
"chars": 11,
"audio_url": "https:\/\/sooddy.com\/audio\/a1b2c3d4-...\/stream",
"created_at": "2026-07-15T10:30:00Z"
}
],
"meta": {
"page": 1,
"per_page": 15,
"total": 87
}
}
/api/tts
Create speech
Submits a TTS job and returns 202 Accepted immediately with a
poll_url. Use
Poll status to track completion.
| Field | Type | Required | Description |
|---|---|---|---|
| text* | string | Yes | Text to synthesise. Max 15,000 chars (Pro). |
| voice* | string | Yes | Voice slug — e.g. en_amy. See /api/voices. |
| speed | float | No | Playback speed 0.5–2.0. Default: 1.0. |
| format | string | No | Output format: mp3 or wav. Default: mp3. |
curl -X POST https://sooddy.com/api/tts \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"text": "Hello from the API!",
"voice": "en_amy",
"speed": 1.0,
"format": "mp3"
}'
{
"data": {
"uuid": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"status": "queued",
"created_at": "2026-07-15T10:30:00Z"
},
"poll_url": "https:\/\/sooddy.com\/api\/tts\/a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}
/api/tts/{uuid}
Poll status
Poll after submitting a job. Status transitions:
queued →
processing →
completed (or failed).
When completed, the audio_url is ready.
curl https://sooddy.com/api/tts/{uuid} \
-H "Authorization: Bearer YOUR_API_TOKEN"
{
"data": {
"uuid": "a1b2c3d4-...",
"status": "processing"
}
}
{
"data": {
"uuid": "a1b2c3d4-...",
"status": "completed",
"duration_s": 1.24,
"chars": 20,
"audio_url": "https:\/\/sooddy.com\/audio\/a1b2c3d4-...\/stream",
"download_url": "https:\/\/sooddy.com\/audio\/a1b2c3d4-...\/download"
}
}
async function generateSpeech(text, voice, token) {
const BASE = 'https://sooddy.com/api';
// 1. Submit the job
const res = await fetch(`${BASE}/tts`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ text, voice }),
});
const { poll_url } = await res.json();
// 2. Poll until done (max 60 s)
for (let i = 0; i < 30; i++) {
await new Promise(r => setTimeout(r, 2000));
const poll = await fetch(poll_url, {
headers: { 'Authorization': `Bearer ${token}` },
});
const { data } = await poll.json();
if (data.status === 'completed') return data.audio_url;
if (data.status === 'failed') throw new Error('TTS job failed');
}
throw new Error('Timed out waiting for TTS job');
}