Skip to main content

Give your app a voice with text-to-speech in AI Gateway

Shipped by Mohamed, Brenden Ehlers, and Jarod Clark

Every on-call robot deserves a proper voicemail greeting. AI Gateway now serves text-to-speech models from six providers, so the access key that answers your chat requests can also read your text out loud.

We wrote the robot a greeting and sent it to four voices:

Hi, you’ve reached the on-call robot. I’m restarting the database for the third time tonight. Leave a message after the beep, or open a ticket like a civilized human. Beep.

Same 172 characters, four robots, and a price spread of about 6x: Qwen3-TTS-Flash cost $0.00172, Grok TTS $0.00258, Gemini 3.8 Flash TTS $0.00265, and MiniMax’s Speech 2.8 Turbo $0.01032. Audition voices against your real script, then pick one for its sound and its cost. Switching providers changes the endpoint and the model, not your key or your bill.

Speech models arrive two ways. Google’s Gemini TTS models run on the Gemini Interactions and GenerateContent APIs and bill per token. Prices are per million tokens:

Model IDInputAudio output
gemini-3.8-flash-tts$0.50$9.00
gemini-3.8-flash-lite-tts$0.50$6.00

The rest use each provider’s own speech endpoint and bill per character of the text you send:

Model IDProviderEndpointPer million characters
qwen3-tts-flashQwen/api/v1/services/aigc/multimodal-generation/generation$10.00
grok-ttsxAI/v1/tts$15.00
qwen-audio-3.1-tts-plusQwen/api/v1/services/audio/tts/SpeechSynthesizer$30.00
speech-2.8-turboMiniMax/v1/t2a_v2$60.00
speech-2.8-hdMiniMax/v1/t2a_v2$100.00
qwen-audio-3.1-tts-flashQwen/api/v1/services/audio/tts/SpeechSynthesizerYour own Qwen key
orpheus-v1-english, orpheus-arabic-saudiGroq/v1/audio/speechYour own Groq key
voxtral-mini-tts-3-0, voxtral-mini-tts-2603Mistral/v1/audio/speechYour own Mistral key

Google’s Gemini TTS rates double on January 1, 2027, so check Model Catalog for current pricing. With your own provider key attached to the gateway, that provider bills you directly.

Each speech endpoint keeps its provider’s request and response format. If your code already calls xAI, MiniMax, or Qwen for speech, point it at https://gateway.ngrok.ai and swap in your ngrok access key.

In the playground, pick Gemini 3.8 Flash TTS or Gemini 3.8 Flash-Lite TTS, type a script, and the reply comes back as audio you can play and Download. The per-character models show up in the model picker too, but the playground can’t call their endpoints yet, so try them from your code.

For a model that talks back instead of reading a script, try GPT Audio 1.5 (gpt-audio-1.5) on chat completions. It answers with text and audio in the same reply, and the playground plays it with a transcript. Audio costs $32.00 per million input tokens and $64.00 per million output tokens.

Get started

Create an ngrok account or sign in. Create an access key that allows your chosen provider, and confirm your account has credits. To use your own provider key instead, attach it to the gateway. The Groq, Mistral, and qwen-audio-3.1-tts-flash models need one.

To hear the greeting before you write any code, paste it into the playground with Gemini 3.8 Flash TTS.

This example uses Google’s Gen AI SDK for TypeScript, because Gemini TTS runs on Google’s native Interactions API. The Google Gen AI SDK guide covers the rest of the SDK setup. Install the SDK and a TypeScript runner:

npm install @google/genai && npm install --save-dev tsx

Save your ngrok access key in .env.local. Keep this file out of version control:

AI_GATEWAY_API_KEY=your_access_key_here

The example records the robot’s greeting in Gemini’s Kore voice and saves it to disk. response_format asks for audio, and speech_config picks the voice. Use an ES module project ("type": "module" in package.json) for top-level await. Save this example as index.ts:

import { writeFile } from "node:fs/promises";import { GoogleGenAI } from "@google/genai"; const client = new GoogleGenAI({	apiKey: process.env.AI_GATEWAY_API_KEY,	httpOptions: {		baseUrl: "https://gateway.ngrok.ai",		apiVersion: "v1beta",	},}); const interaction = await client.interactions.create({	model: "gemini-3.8-flash-tts",	input:		"Hi, you've reached the on-call robot. I'm restarting the database for the third time tonight. Leave a message after the beep, or open a ticket like a civilized human. Beep.",	response_format: { type: "audio" },	generation_config: { speech_config: [{ voice: "Kore" }] },}); const greeting = interaction.output_audio;if (greeting?.data) {	await writeFile("voicemail.wav", Buffer.from(greeting.data, "base64"));	console.log(`Saved voicemail.wav (${greeting.mime_type})`);}

Run the example from your project directory:

node --env-file=.env.local --import tsx index.ts

interaction.output_audio.data holds the recording as base64. Our run saved a 12-second, 24 kHz mono WAV:

Saved voicemail.wav (audio/wav)

To try the cheaper voice, change model to "gemini-3.8-flash-lite-tts".

The per-character endpoints take plain HTTP. This version sends the same greeting to xAI’s /v1/tts with Node.js’s built-in fetch, so it doesn’t need an SDK. Run it on Node.js 20.6 or later, which supports --env-file. Save it as xai.ts in the same project:

import { writeFile } from "node:fs/promises"; const response = await fetch("https://gateway.ngrok.ai/v1/tts", {	method: "POST",	headers: {		Authorization: `Bearer ${process.env.AI_GATEWAY_API_KEY}`,		"Content-Type": "application/json",	},	body: JSON.stringify({		text: "Hi, you've reached the on-call robot. I'm restarting the database for the third time tonight. Leave a message after the beep, or open a ticket like a civilized human. Beep.",		language: "en",	}),}); if (!response.ok) {	throw new Error(`${response.status}: ${await response.text()}`);} await writeFile("voicemail.mp3", Buffer.from(await response.arrayBuffer()));console.log("Saved voicemail.mp3");

Run it with node --env-file=.env.local --import tsx xai.ts. xAI’s body names no model, so the gateway sends it to grok-tts, and the MP3 comes back as the response body. xAI requires language, and the request fails without it.

Open Activity and find your greetings. Each row shows the model, provider, status, and cost. Usage shows tokens for Gemini and billed characters for the per-character models.