API reference
Coming soonA single endpoint that turns text into audio. This is the shape the public API will take — it is documented now so you can plan against it, but keys are not being issued yet.
Status#
API support: coming soon
Bot and server access is closed during beta. Every generation currently goes through a signed-in browser session so we can keep a two-core box from being scraped. Everything below is implemented behind that session and will be exposed as keyed endpoints once capacity allows.
Quickstart#
One POST returns a complete WAV. Set stream to true and you get audio frames as each sentence finishes instead.
curl https://api.strophic.in/v1/speech \
-H "Authorization: Bearer $VOICER_KEY" \
-H "Content-Type: application/json" \
-d '{
"text": "The deploy finished in ninety seconds.",
"voice": "af_heart",
"speed": 1.0,
"format": "wav"
}' --output speech.wavAuthentication#
Keys are bearer tokens scoped to one account. They carry the same credit balance as the Studio — there is no separate API quota.
Authorization: Bearer vk_live_a1b2c3…Keys are shown once at creation and stored only as a hash. Send them from a server — a key in browser code is a key someone else is using.
Create speech#
/v1/speech| Parameter | Type | Notes |
|---|---|---|
| text* | string | Up to 5,000 characters. |
| voice* | string | One of the 51 voice ids. |
| speed | number | 0.5 to 2.0. Defaults to 1.0. |
| format | string | "wav" or "pcm". Defaults to "wav". |
| stream | boolean | Emit chunks as they render. Defaults to false. |
A successful call returns audio/wav at 24 kHz mono, 16-bit. With format: "pcm" you get raw little-endian int16 with no header.
Streaming format#
With stream: true the response is a sequence of length-prefixed binary frames. Each frame is a one-byte type, a four-byte little-endian payload length, then the payload.
| Type | Name | Payload |
|---|---|---|
| 1 | meta | JSON — sample rate, chunk count, character count |
| 2 | audio | int16 little-endian PCM for one chunk |
| 3 | progress | JSON — chunk index, characters rendered, audio ms |
| 4 | done | JSON — totals and timing |
| 5 | error | JSON — code and message |
const response = await fetch("https://api.strophic.in/v1/speech", {
method: "POST",
headers: {
Authorization: `Bearer ${key}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ text, voice: "af_heart", stream: true }),
});
const reader = response.body!.getReader();
let buffer = new Uint8Array(0);
while (true) {
const { done, value } = await reader.read();
if (done) break;
buffer = concat(buffer, value);
// 1 byte frame type, 4 bytes little-endian length, then the payload.
while (buffer.length >= 5) {
const view = new DataView(buffer.buffer, buffer.byteOffset);
const type = view.getUint8(0);
const length = view.getUint32(1, true);
if (buffer.length < 5 + length) break;
const payload = buffer.subarray(5, 5 + length);
if (type === 2) playPcm16(payload); // audio
buffer = buffer.slice(5 + length);
}
}List voices#
/v1/voicesReturns all 51 voices across 9 languages with their gender and quality grade. The catalogue is static — cache it.
{
"sample_rate": 24000,
"languages": [
{ "code": "en-us", "label": "English (US)", "prefix": "a" }
],
"voices": [
{ "id": "af_heart", "name": "Heart", "gender": "female",
"grade": "A", "lang": "en-us" }
]
}Credits#
One credit is one character of input text. Credits are held when the request is accepted and settled against the audio that actually rendered — if a stream is cut short, the unused balance returns automatically.
The response carries X-Voicer-Credits-Charged and X-Voicer-Credits-Remaining so you can track spend without a second call.
Errors#
Errors are JSON with an error code and a human-readable message. Codes are stable; messages are not.
| Status | Code | Meaning |
|---|---|---|
| 400 | invalid_request | A parameter is missing or out of range. |
| 401 | invalid_key | The API key is missing, malformed or revoked. |
| 402 | insufficient_credits | Fewer credits than the request needs. |
| 403 | not_active | The account is pending approval or suspended. |
| 413 | text_too_long | Text exceeds 5,000 characters. |
| 429 | rate_limited | Too many requests. Back off and retry. |
| 503 | busy | The render queue is saturated. Retry in a few seconds. |
Rate limits#
Beta accounts are capped at two concurrent generations and thirty requests a minute. The render queue holds eight jobs; past that you get a 503 busy and should retry with backoff.
These numbers exist because Voicer runs on shared hardware, not because we want them to. They will move up as capacity does.
Want a key when they open?
Tell us what you'd build and we'll put you at the front of the queue.