API Documentation
The eidosSpeech REST API converts text to speech using Microsoft Edge TTS neural voices. Responses are audio (mp3, wav, ogg, flac) or JSON.
Need a quick start? See free text to speech API guide.
https://eidosspeech.xyz/api/v1
audio/mpeg|audio/wav|audio/ogg|audio/flac
Errors:
application/json/api/v1/Authentication
eidosSpeech supports two authentication methods. Use your API key for server-side integrations, or JWT tokens for user-facing apps.
curl -H "X-API-Key: esk_your_api_key_here" ...
curl -H "Authorization: Bearer <access_token>" ...
Anonymous access (from eidosspeech.xyz origin) gets 5 req/day, 500 char limit. No API key needed to use the web UI.
Rate Limits
| Tier | Requests/Day | Chars/Request | Requests/Min |
|---|---|---|---|
| Anonymous (Web UI) | 5 | 500 | 1 |
| Registered (Free) | 30 | 2,000 | 3 |
Rate Limit Response Headers
X-RateLimit-Tier: registered X-RateLimit-Plan: free X-RateLimit-Limit-Day: 30 X-RateLimit-Remaining-Day: 24 X-RateLimit-Limit-Min: 3 X-RateLimit-Char-Limit: 2000 X-Cache-Hit: false X-Cache-Status: MISS X-Cache-Key: 9f0ab5c31c5d1b11
Daily limits reset at 00:00 UTC. Requests for the same identity are serialized and may briefly queue when server slots are full.
/tts
Generate speech from text using 1,200+ neural voices. Supports format selection, style controls, and optional completion webhook callbacks.
Request Body
{
"text": "Your text here", // required, max 2000 chars (registered)
"voice": "id-ID-GadisNeural", // optional, default: id-ID-GadisNeural
"rate": "+0%", // optional, range: -50% to +100%
"pitch": "+0Hz", // optional, range: -50Hz to +50Hz
"volume": "+0%", // optional, range: -50% to +50%
"format": "mp3", // optional: mp3, wav, ogg, flac
"bitrate": "128k", // optional: 64k, 96k, 128k, 192k, 256k, 320k
"webhook_url": "https://example.com/hook", // optional completion callback
"webhook_secret": "your-signing-secret", // optional HMAC signature
"style": "cheerful", // optional, emotion/style (voice-specific)
"style_degree": 1.0 // optional, style intensity (0.01-2.0)
}
cURL Example
curl -X POST https://eidosspeech.xyz/api/v1/tts \ -H "X-API-Key: esk_your_api_key_here" \ -H "Content-Type: application/json" \ -d '{ "text": "Hello, this is a test of the text to speech API.", "voice": "en-US-JennyNeural", "format": "wav", "bitrate": "192k", "rate": "+10%", "pitch": "+5Hz" }' \ --output audio.wav
Python Example
import requests response = requests.post( "https://eidosspeech.xyz/api/v1/tts", headers={"X-API-Key": "esk_your_api_key"}, json={ "text": "Halo, selamat datang di eidosSpeech!", "voice": "id-ID-GadisNeural", "format": "mp3", "bitrate": "128k", "rate": "+0%" } ) if response.status_code == 200: with open("output.mp3", "wb") as f: f.write(response.content) print(f"Remaining: {response.headers.get('X-RateLimit-Remaining-Day')}") else: print(response.json())
JavaScript Example
const response = await fetch('https://eidosspeech.xyz/api/v1/tts', { method: 'POST', headers: { 'X-API-Key': 'esk_your_api_key', 'Content-Type': 'application/json' }, body: JSON.stringify({ text: 'Welcome to eidosSpeech API!', voice: 'en-US-AriaNeural', rate: '+0%', pitch: '+0Hz' }) }); const audioBlob = await response.blob(); const audioUrl = URL.createObjectURL(audioBlob); const audio = new Audio(audioUrl); audio.play();
Response & Errors
Content-Type: audio/mpeg X-RateLimit-Remaining-Day: 29 X-Cache-Hit: false X-Cache-Status: MISS X-Cache-Key: 9f0ab5c31c5d1b11 (Audio bytes by requested format)
{
"error": "ValidationError",
"message": "Text exceeds 2000 characters"
}
{
"error": "AuthenticationError",
"message": "Invalid or expired API key"
}
{
"error": "RateLimitError",
"message": "Daily limit reached",
"detail": { "limit": 30, "used": 30 }
}
Retry-After: 86400
/tts/script
NEW v2.1Generate multi-voice dialog audio from script. Perfect for podcasts, audiobooks, and conversational content. Registered users only. Returns merged MP3 audio with natural pauses between speakers.
Request Body
{
"script": "[John]: Hello!\\n[Mary]: Hi there!", // required, format: [Speaker]: Text
"voice_map": { // required, speaker → voice mapping
"John": "en-US-GuyNeural",
"Mary": "en-US-JennyNeural"
},
"pause_ms": 500, // optional, pause between lines (0-2000ms, default: 500)
"rate": "+0%", // optional, speech rate (-50% to +100%)
"pitch": "+0Hz", // optional, pitch adjustment (-50Hz to +50Hz)
"volume": "+0%" // optional, volume level (-50% to +50%)
}
Script Format
Each line must follow the format: [SpeakerName]: Dialog text
[John]: Hello, how are you today? [Mary]: I'm doing great, thanks for asking! [John]: That's wonderful to hear. [Mary]: How about you?
- Speaker names are case-sensitive and must match voice_map keys
- Each speaker must have a corresponding voice in voice_map
- Empty lines are ignored
- Lines without [Speaker]: format will cause validation error
cURL Example
curl -X POST https://eidosspeech.xyz/api/v1/tts/script \ -H "X-API-Key: esk_your_api_key_here" \ -H "Content-Type: application/json" \ -d '{ "script": "[Host]: Welcome to our podcast!\\n[Guest]: Thanks for having me!", "voice_map": { "Host": "en-US-GuyNeural", "Guest": "en-US-JennyNeural" }, "pause_ms": 800 }' \ --output podcast.mp3
Python Example
import requests script = """[Narrator]: Once upon a time... [Character1]: Hello there! [Character2]: Hi! How are you?""" response = requests.post( "https://eidosspeech.xyz/api/v1/tts/script", headers={"X-API-Key": "esk_your_api_key"}, json={ "script": script, "voice_map": { "Narrator": "en-US-AriaNeural", "Character1": "en-US-GuyNeural", "Character2": "en-US-JennyNeural" }, "pause_ms": 600 } ) if response.status_code == 200: with open("output.mp3", "wb") as f: f.write(response.content) else: print(response.json())
JavaScript Example
const script = `[Host]: Welcome to the show! [Guest]: Thank you for having me! [Host]: Let's dive right in.`; const response = await fetch('https://eidosspeech.xyz/api/v1/tts/script', { method: 'POST', headers: { 'X-API-Key': 'esk_your_api_key', 'Content-Type': 'application/json' }, body: JSON.stringify({ script: script, voice_map: { 'Host': 'en-US-GuyNeural', 'Guest': 'en-US-JennyNeural' }, pause_ms: 700 }) }); const audioBlob = await response.blob(); const audioUrl = URL.createObjectURL(audioBlob); const audio = new Audio(audioUrl); audio.play();
Response & Errors
Content-Type: audio/mpeg X-RateLimit-Remaining-Day: 9 (Merged MP3 audio bytes)
{
"error": "ValidationError",
"message": "Speaker 'John' not found in voice_map"
}
{
"error": "ForbiddenError",
"message": "Multi-voice requires registration"
}
{
"error": "RateLimitError",
"message": "Daily limit reached (10/day)"
}
Retry-After: 43200
Rate Limits & Best Practices
- Registered users: 10 requests/day, 2 requests/minute
- Character count includes all dialog text (excluding speaker names)
- Recommended pause_ms: 500-800ms for natural conversation
- Use consistent voice genders for better audio quality
- Test with short scripts first before generating long content
/tts/subtitle
NEW v2.1Generate speech with subtitle file (.srt). Returns JSON with audio URL and SRT content.
Request Body
{
"text": "Your text here",
"voice": "id-ID-GadisNeural" // optional
}
Response
{
"audio_url": "/cache/abc123.mp3",
"subtitle": "1\\n00:00:00,000 --> 00:00:02,500\\nYour text here\\n"
}
/tts/translate
NEWTranslate source text then generate speech in the target language. Supports all standard TTS fields.
{
"text": "Hello world",
"source_lang": "en",
"target_lang": "id",
"voice": "id-ID-GadisNeural",
"format": "mp3"
}
/tts/ssml
v3Render a safe subset of SSML to a single MP3. Supported tags: <break> (real silence), <prosody> (rate/pitch/volume), <emphasis>, and <sub> (pronunciation). Unsupported tags (incl. Azure mstts:express-as) are ignored — markup is never read aloud. Billed by spoken character count.
Request Body
{
"ssml": "<speak>Hi<break time=\"500ms\"/> welcome to <sub alias=\"eye-doss speech\">eidosSpeech</sub>.</speak>",
"voice": "en-US-JennyNeural"
}
Response
Content-Type: audio/mpeg → single stitched MP3
/tts/long
v3Synthesize text that exceeds the single-request character limit. The text is split at sentence boundaries (Latin & CJK aware), each chunk is synthesized, and the audio is stitched into one MP3. Counts as one request; total characters must fit char_limit × chunk_count (max 12 chunks). Response header X-Chunks reports the chunk count.
Request Body
{
"text": "A very long article… multiple paragraphs…",
"voice": "en-US-JennyNeural",
"rate": "+0%",
"pitch": "+0Hz",
"volume": "+0%"
}
Response
Content-Type: audio/mpeg X-Chunks: 5 → single stitched MP3
/tts/from-srt
NEWUpload an SRT file and return merged timeline audio. Request uses multipart/form-data.
curl -X POST https://eidosspeech.xyz/api/v1/tts/from-srt \ -H "X-API-Key: esk_your_api_key_here" \ -F "srt_file=@subtitle.srt" \ -F "voice=id-ID-GadisNeural" \ -F "format=mp3" \ -o dubbed.mp3
/batch/tts
NEWProcess multiple TTS requests in one call. Response is a ZIP archive with generated audio files and a manifest.json that includes per-item status.
Request Body
{
"requests": [
{ "text": "Halo", "voice": "id-ID-GadisNeural", "format": "mp3" },
{ "text": "Hello", "voice": "en-US-JennyNeural", "format": "wav", "bitrate": "192k" }
]
}
Response
Content-Type: application/zip X-Batch-Total: 2 X-Batch-Success: 2 X-Batch-Failed: 0 tts_batch.zip ├── manifest.json ├── 001_id-ID-GadisNeural.mp3 └── 002_en-US-JennyNeural.wav
/queue/status
NEWView current processing load and estimated wait time for TTS queue.
{
"available_slots": 1,
"max_slots": 3,
"queued_requests": 2,
"current_load": "67%",
"estimated_wait": "5-15 seconds",
"details": {
"tts": {
"available_slots": 1,
"max_slots": 3,
"in_use": 2,
"queued_requests": 2,
"current_load_pct": 66.7,
"priority_lane": { "available_slots": 1, "max_slots": 1, "in_use": 0 }
}
}
}
/voices
List all available TTS voices. Public endpoint — no authentication.
Query Parameters
| language | Filter by language code: id-ID, en-US |
| gender | Filter by gender: Male or Female |
| search | Search voice name or language |
GET /api/v1/voices?language=id-ID&gender=Female
{
"voices": [
{ "id": "id-ID-GadisNeural", "name": "Gadis", "language": "Bahasa Indonesia", "gender": "Female" },
{ "id": "id-ID-ArdiNeural", "name": "Ardi", "language": "Bahasa Indonesia", "gender": "Male" }
],
"total": 2
}
/health
Returns service health including database status, cache stats, queue load, and proxy state.
{
"status": "ok",
"db": "ok",
"cache": { "files": 340, "hit_rate_pct": 64.2 },
"queue": { "tts": { "available_slots": 2, "max_slots": 3 } }
}
Auth Endpoints
Main authentication endpoints are available under /api/v1/auth:
POST /auth/register POST /auth/login POST /auth/refresh POST /auth/logout GET /auth/me
/analytics
NEWUser analytics dashboard endpoint. Requires authenticated user token.
GET /api/v1/analytics?period=30d
{
"period": "30d",
"plan": "free",
"requests_chart": [{ "date": "2026-03-01", "requests": 3, "chars": 420 }],
"most_used_voices": [{ "voice": "id-ID-GadisNeural", "count": 12 }],
"peak_hours": [{ "hour": "14", "activity": 8 }],
"avg_chars_per_request": 390.5,
"cache_hit_rate": "62.4%",
"cost_projection": "$0 (free/included tier)"
}
Team Collaboration Endpoints
POST /api/v1/teams
GET /api/v1/teams
GET /api/v1/teams/{team_id}
POST /api/v1/teams/{team_id}/members
DELETE /api/v1/teams/{team_id}/members/{member_user_id}
Voice Clone Endpoints Experimental
Voice cloning is under active development and currently disabled; these endpoints return 503 Service Unavailable until the training backend ships.
POST /api/v1/voice/clone (multipart form upload)
GET /api/v1/voice/clone/jobs
GET /api/v1/voice/clone/jobs/{job_id}
Billing & Tier Endpoints
GET /api/v1/billing/me POST /api/v1/billing/byok DELETE /api/v1/billing/byok POST /api/v1/billing/admin/set-tier (admin key required) POST /api/v1/billing/admin/add-credit (admin key required)
Integration Ecosystem Endpoints
GET /api/v1/integrations
GET /api/v1/integrations/{integration_id}
Developer Experience Endpoints
GET /api/v1/developer/sdk GET /api/v1/developer/changelog
Error Format
All errors return consistent JSON with error, message, and optional detail.
{
"error": "RateLimitError",
"message": "Daily limit reached (30 requests/day for registered tier).",
"detail": { "limit": 30, "used": 30, "retry_after": 86400 }
}
| Status | Error Type | Description |
|---|---|---|
| 400 | ValidationError | Bad request data |
| 401 | AuthenticationError | Invalid or expired token |
| 403 | ForbiddenError | No auth from external origin |
| 402 | PaymentRequired | PAYG balance not sufficient for request |
| 409 | ConflictError | Email already registered |
| 429 | RateLimitError | Rate limit hit — check Retry-After |
| 503 | ServiceUnavailable | TTS engine temporarily unavailable |
Available Voices by Language
Complete list of 1,200+ voice IDs you can use in the voice parameter. Select a language to see all available voices including multilingual ones.
Note: All voices are powered by Microsoft Edge TTS Neural technology. Multilingual voices (marked with 🌍) can speak multiple languages with native-like pronunciation. Use the GET /api/v1/voices endpoint to fetch this list programmatically.