Get API key
APIs

Audio

llm.buzz serves speech to text and text to speech with OpenRouter's request and answer shapes, so the OpenAI and OpenRouter SDKs reach them with the base URL alone. Each audio model serves one of the two. Chat Completions, Responses and Messages refuse an audio model with a 400 that names its route.

On this page

Every audio route takes your key as a bearer token, as on every other route (Authentication).

Find an audio model

GET https://api.llm.buzz/api/v1/models lists audio models as OpenRouter does. Its default, output_modalities=text, leaves them out: with output_modalities=transcription it lists the speech-to-text models, with output_modalities=speech the text-to-speech models, and with output_modalities=all every model.

curl "https://api.llm.buzz/api/v1/models?output_modalities=speech" \
  -H "Authorization: Bearer $LLM_BUZZ_KEY"

A speech-to-text model's architecture is audio->transcription, and a text-to-speech model's is text->speech, with its voices in supported_voices; the model's page on the Models page lists them too, under Voices. pricing.prompt is the price in USD per second of audio or per character of input text, at your rate, and pricing.completion is "0".

Three more keys say what a model takes. A speech-to-text model's verbose_json says whether it answers response_format: "verbose_json". A text-to-speech model's speech_speed says whether it takes speed, and its default_voice names the voice that speaks when you send none, or is null when the model needs a voice.

The Models page shows each audio model's price per million seconds of audio or per million characters, such as "$3.33/M seconds", and its Output filter picks out transcription and speech models.

Speech to text

POST https://api.llm.buzz/api/v1/audio/transcriptions transcribes one audio file. Send it as OpenRouter's JSON body, the file in base64 in input_audio, or as an OpenAI multipart upload in file, as the OpenAI SDK does.

from openai import OpenAI
import os

client = OpenAI(
    base_url="https://api.llm.buzz/api/v1",
    api_key=os.environ["LLM_BUZZ_KEY"],
)
with open("call.mp3", "rb") as audio:
    transcript = client.audio.transcriptions.create(
        model="openai/whisper-large-v3-turbo",
        file=audio,
    )
print(transcript.text)

The fields are OpenRouter's. A multipart upload sends each one as a text part, provider and trace as JSON text, and timestamp_granularities[] once per value.

FieldRule
modelRequired: a speech-to-text model. Another model is a 400 on model.
input_audioThe JSON form: data, the file in base64, and format. A url is a 400: send the bytes.
fileThe multipart form: the audio file. A source_url is a 400.
response_formatjson, the default, answers text and usage. verbose_json, on a model whose verbose_json is true, adds task, language, duration, segments, and words when the provider sends them; on another model it is a 400. text, srt, vtt and any other value are a 400.
languageAn ISO-639-1 code such as en. Omitted, the provider detects the language.
temperature0 to 1.
timestamp_granularitiessegment and word, with verbose_json only.
diarize, keytermsdiarize: true and any keyterms are a 400: no model here diarizes or takes key terms.
providerOnly zdr and data_collection apply, as described in Data policy. order, only, ignore and options are accepted and not applied, as on OpenRouter for audio.
user, session_id, trace, promptAccepted. user is recorded as the end-user label; the others are not sent to the provider.

The file is at most 25 MiB: a larger file is a 413, and so is a JSON body over 39,234,221 bytes. It is one of wav, mp3, flac, ogg (Opus or Vorbis), webm, m4a, aac, or pcm (headerless signed 16-bit little-endian mono at 16 kHz). The format comes from input_audio.format, or from a multipart file's extension, else its content type.

llm.buzz reads the file and measures its duration before it calls a provider. A file it cannot read to the end, an empty file, or one too small to hold its duration (under 1,000 bits per second) is a 400 on input_audio.data or file that names the formats it takes. Such a file is never sent to a provider and never billed.

The answer:

{"text": "Thanks for calling.", "usage": {"seconds": 12.48, "cost": 0.000041558}}

usage.seconds is what the call billed, and usage.cost its price in USD at your rate. With verbose_json, the provider's task, language, duration, segments and words come with it unchanged.

Text to speech

POST https://api.llm.buzz/api/v1/audio/speech speaks one text and answers with the audio.

These samples send no voice, so the model's default voice speaks. To choose a voice, send voice with one of the IDs listed under Voices on the model's page, or as supported_voices by GET https://api.llm.buzz/api/v1/models?output_modalities=speech. The Python sample posts over plain HTTP with httpx, which the OpenAI Python SDK installs, because the OpenAI SDKs require voice.

import os
import httpx

reply = httpx.post(
    "https://api.llm.buzz/api/v1/audio/speech",
    headers={"Authorization": f"Bearer {os.environ['LLM_BUZZ_KEY']}"},
    json={"model": "hexgrad/kokoro-82m", "input": "Your order has shipped.", "response_format": "mp3"},
    timeout=60,
)
reply.raise_for_status()
with open("reply.mp3", "wb") as file:
    file.write(reply.content)
FieldRule
modelRequired: a text-to-speech model. Another model is a 400 on model.
inputRequired: one string of 1 to 4,096 characters, counted as Unicode code points. An empty or longer string, or an array of turns, is a 400 on input.
voiceOne of the model's voices, listed as supported_voices by GET https://api.llm.buzz/api/v1/models?output_modalities=speech; another voice is a 400 on voice. Omitted, the model's default_voice speaks, and a model whose default_voice is null answers a 400 on voice. With the OpenAI SDKs, which require voice, pass one of those IDs.
response_formatpcm, the default, or mp3. Any other value is a 400.
speed0.25 to 4.0, on a model whose speech_speed is true. A speed other than 1 on another model is a 400 on speed.
input_referencesVoice cloning: a 400 unless empty.
instructionsAccepted and ignored, as on OpenRouter for these models.
provider, user, session_id, traceAs on speech to text.

The answer is the whole audio, not streamed: Content-Type: audio/mpeg for mp3, or audio/pcm for pcm (signed 16-bit little-endian mono at the provider's sample rate, 24 kHz for hexgrad/kokoro-82m), with Cache-Control: no-store. A provider answer that is not valid audio in the format you asked for moves the call to the next provider and is not billed; from the last provider, the call bills the characters, as Billing says.

Cost and the generation record

Every audio answer carries X-Generation-Id: gen- and 32 hex digits. A text-to-speech answer has no JSON body for its cost, so read it from the generation record. The record covers any finished request of your organization, not only audio: any request's generation ID is the gen- form of its x-request-id.

curl "https://api.llm.buzz/api/v1/generation?id=gen-..." \
  -H "Authorization: Bearer $LLM_BUZZ_KEY"

The answer is OpenRouter's {"data": {...}}, read in your key's organization:

KeyValue
total_cost, usageWhat you were charged, in USD at your rate.
api_typestt for speech to text, tts for text to speech, completions for Chat Completions, Responses and Messages, embeddings, image and decisions.
modelThe model's name.
provider_nameThe provider that served the call.
latency, generation_timeIn milliseconds.
data_regioneurope for a provider in the EU, else global.

A generation ID of another organization, a malformed one, and one whose request has not finished all answer 404. A request without id answers 400.

Billing

Speech to text is billed per second of audio and text to speech per character of input text, at the serving provider's price and your rate. Neither has an output price. GET https://api.llm.buzz/api/v1/models/{author}/{slug}/endpoints lists each provider's price.

  • Speech to text bills the seconds of audio llm.buzz measures from the file, or the provider's own count when it is longer, up to the measured duration rounded up to a whole multiple of 30 seconds. Some providers count a minimum or whole chunks; such a count is billed up to that bound, never more.
  • Text to speech bills the characters of input.
  • Before any provider is called, llm.buzz reserves the most the call can cost. A budget, a key limit or credit that cannot cover it answers 402 or 403, and nothing is sent to a provider. Errors lists the codes.
  • An attempt that fails and moves to the next provider bills nothing. When the last provider fails after it received the request, the call bills the measured seconds or the characters, never the 30-second rounding: a 1.5-second file whose only provider answers 500 bills 1.5 seconds. When the last attempt never reaches its provider (the connection is refused, or DNS or TLS fails), the call bills nothing.
  • Neither route reads an Idempotency-Key: a retry is a new request, served and billed again.

Privacy

No audio or text of an audio call is kept in llm.buzz's prompt logs.

Not supported

Streaming synthesis, URL inputs, diarization, key terms, voice cloning, multi-speaker input, the text, srt and vtt transcription formats, and audio output from chat models (modalities: ["text", "audio"]). Audio input to a chat model (input_audio content parts on Chat Completions) is a chat capability of its own, apart from these routes.

Docs