Skip to main content

Gemini 3.8 Flash-Lite TTS is Google's faster, lower-cost text-to-speech model: pick one of 30 voices and get a 24 kHz mono WAV, usually within seconds. Dialogue supports up to two voices, and punctuation shapes the delivery.

From $0.00021/s

Workbench

Send a request

Quickstart

Examples use the selected endpoint’s public parameter definitions. Replace the API key and example media, then send the request from your server.

SDKs & integrations
POSThttps://api.spicyapi.ai/api/v1/jobs/createTask

API authentication

Keep the API key in the server environment variable SPICY_API_KEY. Use Bearer authentication and Content-Type: application/json for JSON requests.

Authorization: Bearer $SPICY_API_KEY
# Generate the idempotency key once; reuse it when retrying after a lost response.
export SPICY_IDEMPOTENCY_KEY="${SPICY_IDEMPOTENCY_KEY:-$(uuidgen)}"
curl -X POST https://api.spicyapi.ai/api/v1/jobs/createTask \
  -H "Authorization: Bearer $SPICY_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $SPICY_IDEMPOTENCY_KEY" \
  -d '{
  "model": "google/gemini-3.8-flash-lite-tts/text-to-speech",
  "input": {
    "text": "Welcome back. Your files finished rendering a minute ago and are ready to download.",
    "voice_id": "Kore"
  }
}'

# Copy data.taskId from the response into TASK_ID, then poll the status.
TASK_ID="TASK_ID_FROM_RESPONSE"
curl "https://api.spicyapi.ai/api/v1/jobs/recordInfo?taskId=$TASK_ID" \
  -H "Authorization: Bearer $SPICY_API_KEY"

Generated from this model’s input schema — the same one the API validates every request against.

Input parameters

Place these fields inside the request body’s input object. Names and types match the API. Do not assume a default when none is specified.

This model has dependencies between fields. Fields marked conditionally required depend on other inputs; see the complete JSON Schema below for the rules.

temperaturenumberOptional

Variation between takes, from 0 to 2: higher values make the delivery less predictable. Leave it unset to use the model's default. Does not change the price per second.

Default
Not specified
Constraints
minimum:0
maximum:2
scenestringOptional

Where and to whom the words are spoken, such as “a quiet hotel room after midnight”. Shapes the delivery and is not spoken. Up to 500 characters. Does not change the price per second.

Default
Not specified
Constraints
maxLength:500
instructionsstringOptional

Free-text direction on how to read: pace, mood, distance from the microphone, who is listening. Shapes the delivery and is not spoken. Up to 1,000 characters.

Default
Not specified
Constraints
maxLength:1000
accentstringOptional

English accent for the reading. Omit it to use the voice's own accent. In dialogue it applies to speakers that set none.

Default
Not specified
Allowed values
"neutral""american_general""american_valley""american_south""british_rp""british_brixton""transatlantic""australian"
pacestringOptional

Rhythm of the reading: natural, rapid_fire (fast and clipped), the_drift (slow and lingering) or staccato (short, separated phrases). Omit it to keep the voice's own rhythm; in dialogue it applies to speakers that set none. Slower pacing yields more seconds of audio, which is what you pay for.

Default
Not specified
Allowed values
"natural""rapid_fire""the_drift""staccato"
stylestringOptional

Overall manner of the reading. Leave empty to use the voice's own default manner. In dialogue it applies to speakers that set none.

Default
Not specified
Allowed values
"whisper""empathetic""vocal_smile""newscaster""promo_hype""deadpan"
voice_idstringOptional

One of 30 prebuilt voices, each with a fixed character. Defaults to Kore. It voices text; in dialogue each speaker sets its own voice_id.

Default
"Kore"
Allowed values
"Achernar""Achird""Algenib""Algieba""Alnilam""Aoede""Autonoe""Callirrhoe""Charon""Despina""Enceladus""Erinome""Fenrir""Gacrux""Iapetus""Kore""Laomedeia""Leda""Orus""Puck""Pulcherrima""Rasalgethi""Sadachbia""Sadaltager""Schedar""Sulafat""Umbriel""Vindemiatrix""Zephyr""Zubenelgenubi"
speakersarray<object>Conditionally required

The cast for dialogue: one or two entries, each with a name the lines refer to, a voice_id, and optionally accent, style, pace and profile (a short description of who is speaking, which is not read aloud). Anything a speaker leaves out falls back to the top-level accent, style and pace. Does not change the price per second.

Default
Not specified
Constraints
minItems:1
maxItems:2
dialoguearray<object>Conditionally required

A scripted conversation for up to two voices, used instead of text: up to 20 lines, each with speaker (a name from speakers) and text of up to 250 characters. Lines are performed in order. Billed per second of audio returned, like text.

Default
Not specified
Constraints
minItems:1
maxItems:20
textstringConditionally required

The words to be spoken, exactly as written. Bracketed cues such as [sighs] are not reliably performed and may be read aloud; spell sounds out instead. Up to 5,000 characters. For a conversation between two voices, use dialogue instead.

Default
Not specified
Constraints
minLength:1
maxLength:5000

Track the task

HTTP 202 means the task was accepted, not completed. Save the returned taskId and query that task. Do not create another task while waiting for its result.

Accepted response
HTTP/1.1 202 Accepted
Location: /api/v1/jobs/recordInfo?taskId=job_01k3m8x9q2z4v7n5p6r8s0t1w3

{
  "code": 200,
  "msg": "success",
  "request_id": "req_example_create",
  "data": {
    "taskId": "job_01k3m8x9q2z4v7n5p6r8s0t1w3",
    "state": "queued",
    "estimatedCost": "0.750000"
  }
}

The accepted response includes the estimate so your app can show the same amount in task history and billing.

Read the job
curl --get "https://api.spicyapi.ai/api/v1/jobs/recordInfo" \
  --data-urlencode "taskId=$TASK_ID" \
  -H "Authorization: Bearer $SPICY_API_KEY"

Start polling at about 2 seconds and gradually increase the interval. Stop at a terminal state. After a connection failure, query the original taskId. Retry creation only with the original idempotency key and body.

Or let the callback find you
import { verifyWebhook } from "@spicyapi/sdk";

// express.raw keeps the bytes we signed; express.json never matches.
app.post("/hooks/spicy", express.raw({ type: "application/json" }), (req, res) => {
  const { payload } = verifyWebhook({
    rawBody: req.body,
    timestamp: req.header("X-Webhook-Timestamp"),
    signature: req.header("X-Webhook-Signature"),
    payloadVersion: Number(req.header("X-Webhook-Payload-Version")),
    secret: process.env.SPICY_WEBHOOK_SECRET,
  });
  res.status(200).end();
  void handleFinishedTask(payload);
});

Pass callBackUrl when you create the task and the finished record is POSTed to you — no polling loop at all. Every delivery carries X-Webhook-Timestamp, X-Webhook-Signature and X-Webhook-Payload-Version. The signature is a base64 HMAC-SHA256 over taskId.timestamp.sha256(body), so it must be checked against the raw bytes: parsing the JSON and re-serialising it changes them and the check will fail. Failed deliveries are retried, so the same taskId can arrive twice — make the handler idempotent. Answer 2xx first and do the work afterwards; a slow handler is treated as a failed delivery.

Read the result

Consume output only when state is succeeded. On failure, use errorCode, errorMessage and request_id to investigate. Example costs and states do not represent an actual bill.

Completed response example
{
  "code": 200,
  "msg": "success",
  "request_id": "req_example_record",
  "data": {
    "taskId": "job_01k3m8x9q2z4v7n5p6r8s0t1w3",
    "model": "google/gemini-3.8-flash-lite-tts/text-to-speech",
    "state": "succeeded",
    "output": {
      "assets": [
        {
          "key": "tasks/2026/08/28/job_01k3m8x9q2z4v7n5p6r8s0t1w3/3f7a1c9e5b2d48f0a6c1e3d5b7f9a1c3.mp3",
          "mime": "audio/mpeg",
          "url": "https://example.r2.cloudflarestorage.com/results/job_01k3m8x9q2z4v7n5p6r8s0t1w3/result.mp3?X-Amz-Signature=SIGNATURE_FROM_RESPONSE",
          "expiresAt": "2026-08-28T12:20:08.000Z"
        }
      ]
    },
    "cost": "0.750000",
    "settled": true,
    "createdAt": "2026-08-28T12:00:00Z",
    "completedAt": "2026-08-28T12:00:30Z"
  }
}

For each available asset, open or download assets[].url directly without an API Authorization header. The signed URL expires at expiresAt; poll the task again to refresh it while the asset remains within its retention period. A pending or unavailable asset has no URL. Treat signed URLs as private.

SDKs & integrations

Use SpicyAPI in your app, terminal, AI assistant or visual workflow. Choose a setup guide below.

Use it in ComfyUI

Install the SpicyAPI nodes from ComfyUI Manager or with the command below, then search for Gemini 3.8 Flash-Lite TTS · Text to Speech on the canvas. The node has the same options as this page and shows its price before it runs.

cd ComfyUI/custom_nodes
git clone https://github.com/spicyapi-ai/comfyui-spicyapi