API Reference

v1 https://api.secretvoice.dev Get API Key

Overview

Expressive speech, one request away.

SecretVoice is a low-latency text-to-speech API tuned for closeness — breathy whispers, intimate narration, and natural conversational turns. Twenty-nine voices across 21 languages, with synchronous, streaming, and realtime WebSocket delivery.

  • 29 premade voices — 13 female, 16 male, each with a distinct persona.
  • 21 languages — one voice keeps its character in every language.
  • Three delivery modes — synchronous audio, HTTP streaming, and realtime WebSocket.
  • Drop-in compatibility — point any ElevenLabs client at this API and it works. See Compatibility →

Billing is simple and transparent: you pay for the audio-seconds you produce, tracked live on your balance.

Quickstart

Your first voice in 30 seconds.

Create an account, grab your API key, and synthesize your first line. One endpoint, one body.

POST /v1/text-to-speech/velvet Returns the generated audio file.
request · application/json
{
  "text": "Come closer. Let me tell you a secret. Now, just listen to my voice.",
  "model_id": "secret-v1"
}
curl
curl -X POST https://api.secretvoice.dev/v1/text-to-speech/velvet \
  -H "xi-api-key: $SECRETVOICE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "text": "Come closer. Let me tell you a secret.", "model_id": "secret-v1" }' \
  --output voice.mp3
python
import requests

r = requests.post(
    "https://api.secretvoice.dev/v1/text-to-speech/velvet",
    headers={"xi-api-key": "YOUR_KEY", "Content-Type": "application/json"},
    json={"text": "Come closer. Let me tell you a secret.", "model_id": "secret-v1"},
)
open("voice.mp3", "wb").write(r.content)
javascript
const res = await fetch("https://api.secretvoice.dev/v1/text-to-speech/velvet", {
  method: "POST",
  headers: { "xi-api-key": "YOUR_KEY", "Content-Type": "application/json" },
  body: JSON.stringify({ text: "Come closer. Let me tell you a secret.", model_id: "secret-v1" }),
});
const blob = await res.blob();
const url = URL.createObjectURL(blob); // play or save it

Listen to this exact request. The audio below is the real output of the request above.

Authentication

One header, every request.

All endpoints require your API key, sent as the xi-api-key header. The key is tied to your account and is never shared.

header
xi-api-key: $SECRETVOICE_API_KEY
  • All requests must use HTTPS.
  • API keys are scoped to your account and cannot be shared across accounts.
  • Requests made without a valid key return 401 with {"detail": "Invalid API key"}.
  • Rotating a key invalidates the old one immediately.

Models

One model, tuned for closeness.

SecretVoice exposes a single model, secret-v1, engineered for intimate, expressive delivery. Common model identifiers from compatible clients are accepted as aliases.

GET /v1/models Lists available models.

Response · 200

200 · application/json
{
  "models": [{
    "model_id": "secret-v1",
    "name": "SecretVoice v1",
    "can_do_text_to_speech": true,
    "can_do_voice_conversion": false,
    "languages": ["en", "es", "ja", "fr", "de", "it", "pt", "ru", "zh", "ko", "th", "id", "vi", "ms", "nl", "pl", "et", "fi", "hi", "ta", "cs"]
  }]
}

Model aliases

The following identifiers are silently mapped to secret-v1 for compatibility: eleven_multilingual_v2, eleven_flash_v2_5, eleven_turbo_v2_5.

Voices

Twenty-nine voices, tuned for closeness.

Every voice has a distinct persona and keeps its character across all 21 languages. Voice ids are lowercase shorthands.

GET /v1/voices Lists all available voices.

Response · 200

200 · application/json
{
  "voices": [{
    "voice_id": "velvet",
    "name": "Velvet",
    "category": "premade",
    "labels": { "gender": "female", "style": "rich and luxurious" },
    "preview_url": "https://api.secretvoice.dev/v1/voices/velvet/preview.mp3"
  }, /* …28 more */]
}
GET /v1/voices/{voice_id} Returns a single voice. 404 if the voice is not in the public roster.

Response · 200

200 · application/json
{
  "voice_id": "velvet",
  "name": "Velvet",
  "category": "premade",
  "labels": { "gender": "female", "style": "rich and luxurious" },
  "preview_url": "https://api.secretvoice.dev/v1/voices/velvet/preview.mp3"
}
GET /v1/voices/{voice_id}/preview.mp3 Redirects (301) to the voice's preview audio. This is what preview_url points to.
GET /v1/voices/{voice_id}/settings Returns the account-level settings override for a voice.

Response · 200

200 · application/json
{
  "stability": 0.5,
  "similarity_boost": 0.75,
  "style": 0.0,
  "speed": 1.0,
  "use_speaker_boost": true
}
PATCH /v1/voices/{voice_id}/settings Updates voice settings for your account. Partial bodies accepted; omitted fields keep current values.
FieldTypeRequiredDescription
stabilitynumber0 – 1. Lower = broader emotional range.
similarity_boostnumber0 – 1. Adherence to the voice's native character.
stylenumber0 – 1. Style exaggeration. Accepted; the signature style is always present.
speednumber0.5 – 2.0. 1.0 is default.
use_speaker_boostbooleanAccepted for compatibility.
request
PATCH /v1/voices/velvet/settings
{
  "stability": 0.4,
  "speed": 0.9
}

The roster

Female

Male

Text to Speech

Convert text to speech.

The core endpoint. Converts text into speech using a voice of your choice and returns the audio file directly.

POST /v1/text-to-speech/{voice_id} Returns the generated audio. voice_id is a roster shorthand (e.g. velvet).

Query parameters

ParameterTypeRequiredDescription
output_formatstringAudio format as codec_sample_rate_bitrate. Default mp3_44100_128.
enable_loggingbooleanDefault true. Set false for zero retention (no history).

Request body

FieldTypeRequiredDescription
textstringyesThe text to synthesize. Cannot be empty.
model_idstringModel to use. Default secret-v1.
language_codestringISO 639-1 code to enforce a language. Default en.
voice_settingsobjectPer-request voice overrides. See table below.
previous_textstringText before this request, for continuity.
next_textstringText after this request, for continuity.
apply_text_normalizationenumauto (default), on, off.
seedintegerBest-effort deterministic sampling. Accepted; not guaranteed.
pronunciation_dictionary_locatorsarrayAccepted for compatibility. Reserved for a future release.

voice_settings

FieldTypeRequiredDescription
stabilitynumber0 – 1. Lower = broader emotional range.
similarity_boostnumber0 – 1. Adherence to the voice's native character.
stylenumber0 – 1. Accepted; the signature style is always present.
speednumber0.5 – 2.0. 1.0 is default.
use_speaker_boostbooleanAccepted for compatibility.

Response · 200

Binary audio. Content-Type matches the requested format. Response headers: X-Request-Id (trace id), X-Duration (exact audio-seconds billed).

example
curl -X POST "https://api.secretvoice.dev/v1/text-to-speech/whisper?output_format=mp3_44100_128" \
  -H "xi-api-key: $SECRETVOICE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "text": "Lean in. I have something to tell you.",
  "model_id": "secret-v1",
  "language_code": "en",
  "voice_settings": { "stability": 0.6, "speed": 0.95 }
}' \
  --output whisper.mp3

Output formats

FormatSample ratesNotes
mp3_*22050 · 24000 · 44100Bitrates 32 – 192 kbps. Default mp3_44100_128.
wav_*8000 – 44100Uncompressed, highest fidelity.
pcm_*8000 – 48000Raw 16-bit PCM, for realtime pipelines.
opus_48000_*48000Efficient streaming codec.
ulaw_8000 / alaw_80008000Telephony codecs, e.g. Twilio.

Streaming

Audio as it's synthesized.

Identical request to the synchronous endpoint, but the audio streams back as it is produced — ideal for agents and long-form narration that want the first byte fast.

POST /v1/text-to-speech/{voice_id}/stream Streaming audio response (chunked). Same body and query as the synchronous endpoint.
ParameterTypeRequiredDescription
output_formatstringDefault mp3_44100_128.

Request body

Identical to Text to Speech — text, model_id, language_code, voice_settings.

example
curl -X POST "https://api.secretvoice.dev/v1/text-to-speech/velvet/stream" \
  -H "xi-api-key: $SECRETVOICE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "text": "There you are. I was starting to miss you.", "model_id": "secret-v1" }' \
  --output live.mp3

Realtime

A voice agent that responds immediately.

Realtime speech over WebSocket — the protocol voice agents and AI companions use. Send text chunks as they arrive; audio comes back as it's spoken, with word timing preserved for captions.

WS wss://ws.secretvoice.dev/v1/text-to-speech/{voice_id}/stream-input Realtime TTS over WebSocket. Authenticate with the xi-api-key header on the handshake. Query: output_format (default mp3_44100_128).

Send

MessagePurpose
{ "text": " " }Initialize the connection (blank space). May carry voice_settings.
{ "text": "..." }Stream text chunks as they become available.
{ "flush": true }Force generation of remaining buffered text.
{ "text": "" }Close the stream cleanly.

Receive

MessageMeaning
{ "audio": "<base64>", "isFinal": false }One chunk of generated audio.
{ "isFinal": true }The stream is complete.
{ "error": "..." }Synthesis failed; the server closes.
websocket
const ws = new WebSocket(
  "wss://ws.secretvoice.dev/v1/text-to-speech/velvet/stream-input?output_format=mp3_44100_128",
  { headers: { "xi-api-key": "YOUR_KEY" } }
);

ws.onopen = () => {
  ws.send(JSON.stringify({ text: " " }));
  ws.send(JSON.stringify({ text: "There you are. I was starting to miss you." }));
  ws.send(JSON.stringify({ flush: true }));
  ws.send(JSON.stringify({ text: "" }));
};

ws.onmessage = (e) => {
  const msg = JSON.parse(e.data);
  if (msg.audio) playBase64Audio(msg.audio); // decode + queue
  if (msg.isFinal) ws.close();
};

Hear it. The sample below is a real streamed reply generated through the realtime path.

History

Every generation, retrievable.

Generations are stored on your account so you can re-fetch, review, or delete them. Pass ?enable_logging=false on any synthesis call to opt out (zero retention).

GET /v1/history Lists recent generations, newest first.

Response · 200

200 · application/json
{
  "history": [{
    "history_item_id": "req_01HZxK…",
    "voice_id": "velvet",
    "text": "Come closer. Let me tell you a secret.",
    "date_unix": 1770000000,
    "seconds": 8.3
  }]
}
GET /v1/history/{history_item_id} Downloads the generated audio for a history item. 404 if the item doesn't exist or belongs to another account.
DELETE /v1/history/{history_item_id} Deletes a history item and its audio. Returns 200, or 404 if not found.

Account & Billing

Pay for what you produce.

You are billed per audio-second of produced content. Your balance is deducted progressively during generation, and you can monitor it at any time.

GET /v1/user Returns your account.

Response · 200

200 · application/json
{
  "id": "usr_01HZxK…",
  "email": "you@example.com",
  "is_new_user": false,
  "subscription": { "tier": "pay_as_you_go" }
}
GET /v1/user/subscription Current plan, usage, and reset information.

Response · 200

200 · application/json
{
  "tier": "pay_as_you_go",
  "character_count": 4123,    // audio-seconds used (field name kept for compat)
  "character_limit": -1,
  "next_character_count_reset_unix": 0
}

Multilingual

One voice, twenty-one languages.

Pass a language_code and the same voice keeps its warmth and character in that language. Supported codes:

en · es · de · fr · it · pt · ru · zh · ja · ko · th · id · vi · ms · nl · pl · et · fi · hi · ta · cs

Español · es "Acércate. Quiero contarte un secreto."
日本語 · ja "もっと近くに来て。秘密を教えてあげる。"
Français · fr "Approche-toi. Je vais te murmurer un secret."
example
curl -X POST "https://api.secretvoice.dev/v1/text-to-speech/whisper" \
  -H "xi-api-key: $SECRETVOICE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "text": "もっと近くに来て。秘密を教えてあげる。",
  "language_code": "ja",
  "model_id": "secret-v1"
}' \
  --output whisper-ja.mp3

Rate Limits

Fair use, for everyone.

Limits protect the reliability of the Service for all users. They scale with your plan.

LimitDefaultNotes
Requests per minute60Per API key, per endpoint.
Concurrent streams5Across the streaming and realtime endpoints.
Max text length5,000 charsPer synthesis request. Split longer text into consecutive calls with previous_text.
Max audio duration90 minPer generation.

When a limit is exceeded, the API returns 429 with a Retry-After header. Back off and retry after the indicated delay; exponential backoff is recommended.

Errors

Errors you can read.

All errors use a consistent, machine-readable body: {"detail": "..."}.

StatusMeaningExample detail
400Invalid request"text is required"
401Missing or invalid key"Invalid API key"
402Insufficient balance"No minutes available"
404Voice or model not found"Voice not found"
422Validation error{"detail": [...]}
429Rate limit exceeded"Rate limit exceeded"
500Internal error"TTS generation failed"
501Not implemented"Voice conversion is not available"

Compatibility

Drop-in for the ElevenLabs protocol.

SecretVoice speaks the ElevenLabs API contract. If your code already talks to ElevenLabs, you can point it at SecretVoice by changing the base URL and using a SecretVoice API key — no code changes.

  • Same surface — paths, methods, request bodies, and response shapes follow the same contract.
  • Same auth header — xi-api-key is accepted natively.
  • Same realtime protocol — the WebSocket stream-input flow matches message-for-message.
  • Model aliases — common model identifiers map to secret-v1 automatically.
  • Safe over-acceptance — optional fields with no equivalent here are accepted and safely ignored, so clients never break.
  • Same error shape — {"detail": "..."} bodies and matching status codes.

Honesty in compatibility. The contract is compatible; the model is ours. A handful of optional parameters (e.g. seed, request stitching ids) are accepted and safely ignored. Features we don't offer (voice conversion, custom voice cloning in v1) return a clear 501 — never a silent success.

Machine-readable

OpenAPI & llms.txt

The API is fully described as machine-readable artifacts, kept in sync with these docs.