POST /v1/infer/audio/speech
The OpenAI speech API: synthesizes `input` with a voice and returns the audio bytes. B3OS extension: `timestamps: "word"` returns JSON instead, with the audio base64-encoded and the start and end time of every word, which render uses to place clicks on words and cut captions. Every `response_format` is produced: `mp3`, `opus` (Ogg), `aac` (ADTS), `flac`, `wav` and `pcm` (16-bit mono at `sample_rate`, 24 kHz for `kokoro-82m`). `kokoro-82m` speaks at `speed` 0.5 to 2 and takes no `instructions`; other values return `400 unsupported_parameter`. While the node's voice service is down the answer is `503 infer_backend_unavailable` with `Retry-After`. `Server-Timing` carries the voice service's own `voice_queue` and `voice_run` beside b3osd's `synthesize` and `encode`. Bills `infer.audio_chars` (characters of `input`).

Authentication

Bearer Token (b3os_sk_ API key or b3os_pat_ token) API Key (cookie: __Host-b3os_session)

Request Body required

application/json
model string REQUIRED
A speech model, such as `kokoro-82m`.
input string REQUIRED
voice string REQUIRED
A voice ID from `GET /v1/infer/audio/voices`.
response_format string | null
Enum: mp3, opus, aac, flac, wav, pcm, null
speed number | null
OpenAI's range. `kokoro-82m` speaks at 0.5 to 2; a speed outside a model's range is `400 unsupported_parameter` (never clamped).
instructions string | null
Style guidance, for models that support it. `kokoro-82m` does not: non-blank instructions are `400 unsupported_parameter`.
timestamps string | null
B3OS extension. `word` returns JSON with base64 audio and per-word start and end times.
Enum: none, word, null

Responses

200 The audio, or JSON with word timings when `timestamps` is `word`.
application/json
object string REQUIRED
Enum: speech
model string REQUIRED
voice string REQUIRED
format string REQUIRED
Enum: mp3, opus, aac, flac, wav, pcm
sample_rate integer REQUIRED
duration_ms integer REQUIRED
audio_base64 string (byte) REQUIRED
words object[] REQUIRED
Array of:
text string REQUIRED
start_ms integer REQUIRED
end_ms integer REQUIRED
400 The request is invalid (`invalid_request`, `unsupported_parameter`, `context_length_exceeded`).
401 Missing or invalid credentials (`unauthorized`).
403 The key lacks `infer:run` or `infer:read` (`forbidden`), or the egress guard refused an image URL (`egress_denied`).
404 The model or voice is not available (`model_not_available`, `voice_not_found`).
413 The request body exceeds the model's `max_request_bytes` (`payload_too_large`).
415 The body is not JSON (`unsupported_media_type`): send `Content-Type: application/json`.
429 A limit applies (`rate_limited` for requests, tokens or remote image bytes per minute, `concurrency_limited` for requests in flight for the organization, `quota_exceeded`; `credits_exhausted` arrives as 402).
500 Something failed inside B3OS (`internal`).
503 The model runtime, the voice service or the B3IQ gateway route is restarting, overloaded or not set up on this node (`infer_backend_unavailable`, `capacity_unavailable`), or the node is unavailable (`node_unavailable`).
504 Speech synthesis did not finish within its deadline (`request_timeout`).
curl -X POST 'https://beta-api.b3os.org/v1/infer/audio/speech' \  -H 'Authorization: Bearer YOUR_API_TOKEN' \  -H 'Content-Type: application/json' \  -d '{  "model": "string",  "input": "string",  "voice": "string",  "response_format": "mp3",  "speed": 1,  "instructions": "string",  "timestamps": "none"}'
const response = await fetch('https://beta-api.b3os.org/v1/infer/audio/speech', {  method: 'POST',  headers: {      "Authorization": "Bearer YOUR_API_TOKEN",      "Content-Type": "application/json"  },  body: JSON.stringify({    "model": "string",    "input": "string",    "voice": "string",    "response_format": "mp3",    "speed": 1,    "instructions": "string",    "timestamps": "none"  })});const data = await response.json();console.log(data);
import requestsheaders = {    'Authorization': 'Bearer YOUR_API_TOKEN'}response = requests.post('https://beta-api.b3os.org/v1/infer/audio/speech', headers=headers, json={  "model": "string",  "input": "string",  "voice": "string",  "response_format": "mp3",  "speed": 1,  "instructions": "string",  "timestamps": "none"})print(response.json())
200 Response
{  "object": "speech",  "model": "kokoro-82m",  "voice": "af_heart",  "format": "wav",  "sample_rate": 24000,  "duration_ms": 1330,  "audio_base64": "UklGRiQAAABXQVZFZm10IBAAAAABAAEAwF0AAIC7AAACABAAZGF0YQAAAAA=",  "words": [    {      "text": "Open",      "start_ms": 40,      "end_ms": 310    },    {      "text": "the",      "start_ms": 310,      "end_ms": 420    },    {      "text": "billing",      "start_ms": 420,      "end_ms": 820    },    {      "text": "page.",      "start_ms": 820,      "end_ms": 1290    }  ]}
Ask a question... ⌘I