nineninesix.ai API

Create Speech

Generate audio from text using the Nineninesix TTS API.

POST /tts/bytes

Generates audio from the input transcript. The response body is the raw audio in the requested format, streamed as it's generated.

Request Body

ParameterTypeRequiredDescription
model_idstringYesThe TTS model. Currently gepard-1.0.
transcriptstringYesThe text to synthesize.
voiceobjectYesVoice specifier: { "mode": "id", "id": "<voice-id>" }.
output_formatobjectYesOutput format (see below).
languagestringNoLanguage code (e.g. en).

Output Format

The model is natively 22050 Hz. Set sample_rate to 22050 to skip resampling; any other rate is resampled server-side. encoding may be pcm_s16le or pcm_f32le.

// WAV (PCM)
{ "container": "wav", "encoding": "pcm_s16le", "sample_rate": 22050 }
// Raw PCM (lowest latency for streaming pipelines)
{ "container": "raw", "encoding": "pcm_s16le", "sample_rate": 22050 }

MP3 is not yet supported — the API currently emits wav and raw PCM only. MP3 (container: "mp3") is planned; for now request wav and transcode client-side if you need MP3.

Models

ModelDescription
gepard-1.0Dialogue-native model, tuned for conversational speech and low-latency streaming.

Billing

1 credit = 1 character of transcript (Unicode code points). The charge is taken before generation and refunded automatically on failure.

Examples

curl
curl -N https://api.nineninesix.ai/tts/bytes \
  -H "Authorization: Bearer sk_996_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "model_id": "gepard-1.0",
    "transcript": "Today is a wonderful day to build something people love!",
    "voice": { "mode": "id", "id": "<voice-id>" },
    "output_format": { "container": "wav", "encoding": "pcm_s16le", "sample_rate": 22050 }
  }' --output speech.wav
Python
from cartesia import Cartesia

client = Cartesia(api_key="sk_996_your_api_key", base_url="https://api.nineninesix.ai")

audio = client.tts.bytes(
    model_id="gepard-1.0",
    transcript="Today is a wonderful day to build something people love!",
    voice={"mode": "id", "id": "<voice-id>"},
    output_format={"container": "wav", "encoding": "pcm_s16le", "sample_rate": 22050},
)

with open("speech.wav", "wb") as f:
    f.write(audio)
Node.js
import { Cartesia } from "@cartesia/cartesia-js";

const client = new Cartesia({ apiKey: "sk_996_your_api_key", baseURL: "https://api.nineninesix.ai" });

const res = await client.tts.generate({
  model_id: "gepard-1.0",
  transcript: "Today is a wonderful day to build something people love!",
  voice: { mode: "id", id: "<voice-id>" },
  output_format: { container: "wav", encoding: "pcm_s16le", sample_rate: 22050 },
});

const buffer = Buffer.from(await res.arrayBuffer());
fs.writeFileSync("speech.wav", buffer);

POST /tts/sse

Streams audio over Server-Sent Events for low time-to-first-audio without opening a WebSocket. The request body is identical to /tts/bytes, except the container must be raw (SSE can't wrap a WAV/RIFF header):

{ "container": "raw", "encoding": "pcm_s16le", "sample_rate": 22050 }

Each event's data is a JSON object. chunk events carry a base64-encoded slice of raw PCM; a final done event closes the stream:

data: {"type":"chunk","data":"<base64 pcm>","context_id":"..."}
data: {"type":"chunk","data":"<base64 pcm>","context_id":"..."}
data: {"type":"done","context_id":"..."}

Billing is identical to /tts/bytes — one pre-charge on the full transcript, confirmed when the stream completes.

Node.js
import { Cartesia } from "@cartesia/cartesia-js";

const client = new Cartesia({ apiKey: "sk_996_your_api_key", baseURL: "https://api.nineninesix.ai" });

const stream = await client.tts.generateSSE({
  model_id: "gepard-1.0",
  transcript: "Streaming speech, chunk by chunk.",
  voice: { mode: "id", id: "<voice-id>" },
  output_format: { container: "raw", encoding: "pcm_s16le", sample_rate: 22050 },
});

for await (const message of stream) {
  if (message.type === "chunk") {
    const pcm = Buffer.from(message.data, "base64"); // feed into your audio sink
  }
}
Python
import base64
from cartesia import Cartesia

client = Cartesia(api_key="sk_996_your_api_key", base_url="https://api.nineninesix.ai")

for message in client.tts.sse(
    model_id="gepard-1.0",
    transcript="Streaming speech, chunk by chunk.",
    voice={"mode": "id", "id": "<voice-id>"},
    output_format={"container": "raw", "encoding": "pcm_s16le", "sample_rate": 22050},
):
    if message.type == "chunk":
        pcm = base64.b64decode(message.data)  # feed into your audio sink

WebSocket Streaming

GET /tts/websocket

For real-time, low-latency generation, open a WebSocket connection. Each transcript frame is billed and synthesized independently; audio frames stream back per context_id.

Because browsers and many WebSocket clients can't set headers, pass your key as a query parameter:

wss://api.nineninesix.ai/tts/websocket?api_key=sk_996_your_api_key

Send one JSON frame per utterance; group related frames under a shared context_id:

{
  "model_id": "gepard-1.0",
  "transcript": "Hello there.",
  "voice": { "mode": "id", "id": "<voice-id>" },
  "output_format": { "container": "raw", "encoding": "pcm_s16le", "sample_rate": 22050 },
  "context_id": "conversation-1"
}

The server streams back chunk frames (base64 raw PCM) for that context_id, then a done frame; an error frame is sent instead if synthesis fails. Each transcript frame is billed and settled independently — confirmed on done, refunded on error. Control frames without a transcript are not billed.

The Cartesia SDK's client.tts.websocket() helper manages the connection and framing for you.

Error Responses

StatusErrorDescription
400missing_transcript / invalid_jsonMalformed request body
401unauthorizedMissing or invalid API key
402payment_requiredInsufficient credits
429rate_limited / concurrent_limitPer-project rate or concurrency limit reached
502upstream_unavailableSynthesis backend error

Error response format:

{ "error": "payment_required", "message": "insufficient credits" }

On this page