Speech: text to voice, voice to text
Every tool on this page runs a speech model and is marked model-backed in the catalog. Send text and get audio back in the format you ask for; send audio and get a transcript, with speaker labels and word times when you want them. No API key and no account: each call is paid on its own 402 over x402 or MPP.
The models, caps, voices and formats below are read from the tools' own tables, and the prices from the catalog /api/pricing serves. How to pay covers the wallet side.
Three tiers, one request shape.
Send text, an optional voice and an optional format; the answer is JSON with the audio base64-encoded and the model, voice and format that actually spoke. The three tiers differ in model, price and cap, not in interface, so a buyer moves between them by changing the path.
| route | price | model | max chars | formats | voices |
|---|---|---|---|---|---|
| POST /api/tts-lite | $0.005 | kokoro-82m | 800 | mp3 pcm | the 10 standard names, mapped to the model's nearest voice |
| POST /api/tts | $0.120 | eleven-v4-turbo | 2,000 | mp3 opus aac flac wav pcm | the 10 standard names, each its own voice, or 21 named voices |
| POST /api/tts-hd | $0.240 | eleven-v4 | 2,000 | mp3 opus aac flac wav pcm | the 10 standard names, each its own voice, or 21 named voices |
Example call
# the first request answers 402 with the price; a paying client retries it
curl -X POST https://agent402.tools/api/tts \
-H 'Content-Type: application/json' \
-H "PAYMENT-SIGNATURE: <signed by your x402 client>" \
-d '{"text":"Hello from Agent402!","voice":"alloy","format":"mp3"}' \
| jq -r .audio | base64 -d > hello.mp3
import { Agent402 } from "agent402-client";
import { writeFile } from "node:fs/promises";
const a = new Agent402({ fetch: payFetch }); // a wallet-paying fetch, see /how-to-pay
const out = await a.call("tts", {"text":"Hello from Agent402!","voice":"alloy","format":"mp3"});
await writeFile("hello." + out.format, Buffer.from(out.audio, "base64"));
console.log(out.model, out.voice, out.chars);
What comes back
{
"model": "elevenlabs/eleven-v4-turbo",
"provider": "openrouter",
"voice": "river",
"format": "mp3",
"audio": "<base64-encoded audio>",
"chars": 20
}
model and voice name what spoke, which matters when a backup served the call (below). chars is the number of characters spoken, the unit the cap is counted in; a text over the tier's cap is a 400 before anything is generated, and a 400 is not charged on x402.
Translate and speak in one call
POST /api/dub takes text and a targetLanguage (a BCP-47 tag or an English name), translates the text on the chat gateway and speaks the translation on the speech tier its tier names; the price is the sum of the two tiers, read from the catalog. The answer is the audio as raw bytes with the translation, the detected source language and the models that served in the X-Dub-Translation header, or JSON with the audio base64 when the call says json: 1. The speech tier's cap applies to the translation, and the source text is checked against it before any model runs.
| tier | price | translation | speech | max chars |
|---|---|---|---|---|
| standard | $0.140 | POST /v1/chat/completions | POST /api/tts | 2,000 |
| hd | $0.260 | POST /v1/chat/completions | POST /api/tts-hd | 2,000 |
Ten standard names, and a longer list.
The 10 standard voice names work on every tier. On /api/tts and /api/tts-hd each name is its own voice, listed here; on the lite tier each maps to the nearest voice of its model and the answer names it.
| standard name | speaks as |
|---|---|
| alloy | river |
| ash | chris |
| ballad | callum |
| coral | jessica |
| echo | eric |
| fable | george |
| nova | sarah |
| onyx | brian |
| sage | matilda |
| shimmer | lily |
Name one directly as voice. On /api/tts-hd, inline audio tags such as [whispering] are read as delivery cues.
mp3 is the default. The lite tier serves mp3 and pcm only, and asks for another format with a 400 that names the tiers that serve it, never a silent downgrade.
What a backup model means for a buyer.
A tier is priced for its primary model, and that model serves the call whenever it can. When it is busy, throttling or answering with a server error, the call moves down a chain, and the answer's model field names whichever link spoke. The chain is read from the tier table:
| route | primary, then its chain | backups after the chain |
|---|---|---|
| POST /api/tts-lite | kokoro-82m | none: one model at one price, and an upstream failure is an uncharged error |
| POST /api/tts | eleven-v4-turbo → eleven-turbo-v2.5 → eleven-flash-v2.5 | voxtral-mini-tts-2603 → grok-voice-tts-1.0 → mai-voice-2 |
| POST /api/tts-hd | eleven-v4 → eleven-v3 → eleven-multilingual-v2 | voxtral-mini-tts-2603 → grok-voice-tts-1.0 → mai-voice-2 |
One paid call is at most one generation. A link is skipped only after a failure that could not have been billed: a throttle, a server error, or a connection that never opened. A timeout or an empty answer may already have been generated, so it ends the call with an uncharged error instead of a second attempt. The whole walk, backups included, runs inside 40 seconds.
A backup can have fewer voices and languages than the primary: the standard name you asked for maps to its nearest voice, and a named voice maps back to its standard name first. The price does not change with the link that served you. Read model if your pipeline depends on a particular voice, and retry when it names a backup you would rather not keep.
Transcription, with speakers and word times on request.
Send the URL of an audio file (mp3, wav, m4a, ogg, flac and webm) and the answer is the transcript with the language detected and the duration read. Add diarize: true and the call runs on the scribe-v2 model instead, adding words (each with start, end and a speaker number), speakers and language_confidence, at the same price and cap.
| route | price | model | max audio | input | speaker labels |
|---|---|---|---|---|---|
| POST /api/transcribe | $0.030 | gpt-transcribe | 4 minutes, 25 MB | a URL to the audio file | scribe-v2 with diarize: true |
| POST /api/transcribe-pro | $0.100 | gpt-transcribe | 10 minutes, 25 MB | a URL to the audio file | scribe-v2 with diarize: true |
| POST /v1/audio/transcriptions | $0.030 | gpt-transcribe | 4 minutes, 25 MB | multipart upload, a part named file | scribe-v2 with diarize: true |
| POST /v1/pro/audio/transcriptions | $0.100 | gpt-transcribe | 10 minutes, 25 MB | multipart upload, a part named file | scribe-v2 with diarize: true |
The duration cap is enforced before any model is called, from the file's own headers: a recording over the cap is a 422 naming the tier that takes it, and a 422 is not charged on x402. POST /api/transcribe-pro is the same model on a longer cap, for recordings the shorter tier refuses.
Example call
curl -X POST https://agent402.tools/api/transcribe \
-H 'Content-Type: application/json' \
-H "PAYMENT-SIGNATURE: <signed by your x402 client>" \
-d '{"url":"https://agent402.tools/fixtures/sample-speech.wav"}'
# with speaker labels and word timestamps, same price
curl -X POST https://agent402.tools/api/transcribe \
-H 'Content-Type: application/json' \
-H "PAYMENT-SIGNATURE: <signed by your x402 client>" \
-d '{"url":"https://agent402.tools/fixtures/sample-speech.wav","diarize":true}'
{
"model": "gpt-transcribe",
"provider": "openai",
"text": "Hi, this is the Agent402 sample recording. Can you hear me clearly? Yes, loud and clear. There are two of us on this clip, so the transcript can tell us apart.",
"language": "en",
"duration": 12.6
}
On the hosted MCP connector the same tool is audio.transcribe, one of the flagship tools listed on connect; the long tail (transcribe-pro, the tts tiers) is reached through catalog.find and catalog.call.
Point an existing SDK at the gateway.
POST /v1/audio/speech speaks the request format of the OpenAI speech endpoint, so an SDK's audio.speech.create() works by changing base_url to https://agent402.tools/v1. It answers raw audio bytes (mp3 and pcm), not JSON, at $0.060 per call for up to 2,000 characters. The 11 standard voice names map per model, and any model's native voice id is accepted too.
It runs its own chain of 5 speech models, walked on any upstream failure, so the answer may come from any of them: voxtral-mini-tts-2603, grok-voice-tts-1.0, kokoro-82m, mai-voice-2-flash and mai-voice-2. Pin one with model and the rest stay as fallbacks.
curl -X POST https://agent402.tools/v1/audio/speech \
-H 'Content-Type: application/json' \
-H "PAYMENT-SIGNATURE: <signed by your x402 client>" \
-d '{"input":"Agent402 serves fourteen hundred tools, paid per call.","voice":"alloy"}' \
--output hello.mp3
POST /v1/audio/transcriptions is the matching transcription wire: multipart/form-data with a part named file (and an optional language or diarize), the same model and cap as POST /api/transcribe, at $0.030; POST /v1/pro/audio/transcriptions takes a recording up to 10 minutes at $0.100.
curl -X POST https://agent402.tools/v1/audio/transcriptions \ -H "PAYMENT-SIGNATURE: <signed by your x402 client>" \ -F "file=@meeting.m4a" -F "language=en" -F "diarize=true"
Paid per call, and not for a call that failed.
Every route here takes the same 402 handshake as the rest of the catalog: ask, get the price, pay with a wallet over x402 or MPP, and the identical request completes with a receipt header. The speech tools are wallet-only; there is no proof-of-work tier for a model call. How to pay walks every rail with a snippet to paste.
The model runs first and the payment settles only after a response below 400 is ready, so on x402, on MPP's evm method and on a Tempo charge a 4xx or 5xx (a text over the cap, a format the tier does not serve, an upstream that could not answer) is not charged. A 200 is charged only if its settlement then succeeds. /proof shows settled receipts against their quotes, and /status measures the paid path from outside.