Speech and catalogue
GET /v1/chat/catalog lists public models, voices and current credit prices. Text chat uses POST /v1/chat/completions; speech uses the endpoints below.
Models and prices
Loading model catalogue…
Speech to text
POST /v1/chat/transcribe accepts { "audio": "data:audio/webm;base64,..." }. Plain base64 is also supported, up to 10MB decoded. Returns text and language.
Text to speech
Loading model catalogue…
Expand a model to see its enabled voices. Administrators can change voice names and descriptions. Disabled voices are omitted from the catalogue and cannot be used for new synthesis requests; refresh this page to see current settings. Disabling a model also removes all its voices from the documentation and catalogue voice lists and rejects new synthesis requests. Re-enabling a model preserves individual voice settings. If the default voice or its model is disabled, an enabled voice from another enabled model is selected instead.
POST /v1/chat/speech accepts { "text": "Hello", "voice": "speech-light:af_bella" } (up to 2,000 characters). Omit voice for the configured default. Use voices[].id from the catalogue; modelId identifies its speech model and rate. Families: Speech Standard, Speech Light, Speech Pro and Speech Expressive. Speech and chat models are independent.
The response includes audio (MP3 data URI, data:audio/mpeg;base64,...), voice and billing: { model, credits, characters, price, unit: "1000_characters" }. All voices, including Speech Pro, return MP3. Assign audio to an audio element's src, or decode the base64 after the comma and save it as .mp3. Speech Pro requires no client-side conversion; voice IDs and character-based pricing are unchanged. See the runnable Python example.
Streaming speech
Send "stream": true to the same POST /v1/chat/speech endpoint. Speech Standard, Speech Light, Speech Pro and Speech Expressive support streaming; the catalogue exposes speechModels[].streaming. Omitting stream, or setting it to false, retains the JSON response above.
{ "text": "Hello. How was your day?", "voice": "speech-pro:Ashley", "stream": true }The response is a continuous MP3 binary stream with Content-Type: audio/mpeg, not JSON, base64 or SSE. The proxy forwards audio as it arrives; Speech Pro is converted incrementally to MP3. This does not guarantee a model's generation latency. Text is sent as one request, with no automatic sentence splitting.
With curl and FFmpeg installed, this example plays audio while it arrives:
curl --fail --no-buffer https://api.coldstart.win/v1/chat/speech \
-H "Authorization: Bearer $GIRLLAB_API_KEY" \
-H "Content-Type: application/json" \
-d '{"text":"Hello. How was your day?","voice":"speech-pro:Ashley","stream":true}' \
| ffplay -nodisp -autoexit -f mp3 -i pipe:0For a voice-call client, consume the response body incrementally and feed a persistent MP3 decoder/player. In a browser, use response.body.getReader() with a streaming playback implementation; response.blob(), arrayBuffer() and json() wait for the whole response. Network chunks are arbitrary byte ranges, not standalone MP3 files. Cancel the fetch/reader when the user interrupts or hangs up. Browser playback still requires the usual user interaction to unlock audio.
Response headers expose X-Speech-Voice (URL-encoded public voice ID), X-Speech-Credits, X-Speech-Characters and X-Speech-Usage-Id. The credit amount is the initial debit; completed requests use the same character-based price as non-streaming requests. A failed stream or a disconnect before the server completes processing refunds the request and records failed usage. Cancellation after server completion does not undo a successful charge.
Before audio starts, errors use the normal HTTP status and JSON error body. After audio starts, a failure closes the connection abruptly; discard incomplete output and check Task history for the final billing result. Do not treat HTTP 200 or receiving the first chunk as proof of successful completion. Additional proxies must disable response buffering and compression for this route; the API sends X-Accel-Buffering: no and Cache-Control: no-store, no-transform.
If speech text is explicitly rejected by the content policy, both response modes return HTTP 400 with error code content_policy_violation before audio starts. The charge is refunded. Do not automatically retry the same text; revise it to comply with the content policy. Streaming support does not remove content restrictions.
Speech requests are independent; explicitly pass the desired public voice ID. Speech JSON contains the audio data URI and billing; streaming returns audio bytes.
Speech credits = normalized text's billable character count × model price / 1,000, rounded up to 8 decimal places. Speech Light, Speech Pro and Speech Expressive count UTF-16 code units, according to the selected model: ordinary Chinese/English characters count as one, and a supplementary character such as 😀 counts as two. Speech Standard uses Unicode code points. The catalogue's speechModels[].characterCounting and response's billing.character_counting identify the counting rule (utf16_code_units or unicode_codepoints); billing.characters is the billed quantity. Formatting and stage directions are removed before synthesis and counting. Failed or empty synthesis is refunded. Each synthesis request is billed; replaying returned audio costs nothing. The console caches audio by message and voice for the current page session.