# POST /v1/audio

Turn speech into text, and text into speech. These routes follow the OpenAI Audio API, so
`client.audio.transcriptions.create` and `client.audio.speech.create` work unchanged with the
base URL `https://demo-api.routerplus.com/v1`.

| Route | Alias | What it does |
|---|---|---|
| `POST /v1/audio/transcriptions` | `POST /audio/transcriptions` | Returns the text of an audio file |
| `POST /v1/audio/speech` | `POST /audio/speech` | Returns spoken audio for a text, streamed as it is made |

## Examples

```bash
curl https://demo-api.routerplus.com/v1/audio/transcriptions \
  -H "Authorization: Bearer $ROUTERPLUS_API_KEY" \
  -F model=whisper-1 \
  -F response_format=srt \
  -F file=@meeting.mp3
```

```bash
curl https://demo-api.routerplus.com/v1/audio/speech \
  -H "Authorization: Bearer $ROUTERPLUS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model": "gpt-4o-mini-tts", "input": "Your order has shipped.", "voice": "coral"}' \
  --output speech.mp3
```

```python
from openai import OpenAI

client = OpenAI(base_url="https://demo-api.routerplus.com/v1", api_key="tm_vk_...")

with open("meeting.mp3", "rb") as f:
    transcript = client.audio.transcriptions.create(model="gpt-4o-transcribe", file=f)
print(transcript.text)

with client.audio.speech.with_streaming_response.create(
    model="tts-1", voice="alloy", input="Your order has shipped."
) as response:
    response.stream_to_file("speech.mp3")
```

Send your key as `Authorization: Bearer tm_vk_...` or as `x-api-key: tm_vk_...`. A missing or
invalid key returns 401.

## Models

| Model | Route | Price | Billed on |
|---|---|---|---|
| `gpt-4o-transcribe` | transcriptions | $2.50 per 1M input tokens, $10.00 per 1M output tokens | `usage.input_tokens` and `usage.output_tokens` |
| `gpt-4o-mini-transcribe` | transcriptions | $1.25 per 1M input tokens, $5.00 per 1M output tokens | `usage.input_tokens` and `usage.output_tokens` |
| `whisper-1` | transcriptions | $0.006 per minute ($0.0001 per second) | seconds of audio, rounded up |
| `gpt-4o-mini-tts` | speech | $0.60 per 1M input tokens, $12.00 per 1M audio output tokens | the tokens the provider reports |
| `tts-1` | speech | $15.00 per 1M characters | characters of `input` |
| `tts-1-hd` | speech | $30.00 per 1M characters | characters of `input` |

OpenAI serves all six models. `GET /v1/models?output_modalities=transcription` lists the
transcription models, and `?output_modalities=audio` lists the speech models.

## POST /v1/audio/transcriptions

### Request

The body is `multipart/form-data`. The audio file is at most 25 MiB. A larger file is a 413. RouterPlus checks every rule below before it reserves money or calls a provider. A
refused request has no ledger row and no charge.

| Field | Type | Behavior |
|---|---|---|
| `file` | file, required | The audio: flac, mp3, mp4, mpeg, mpga, m4a, ogg, wav or webm. |
| `model` | string, required | A transcription model id. A speech, chat, image, video, decision or embeddings model is a 400 that names its own route. An id that is not in the catalog is a 404. |
| `response_format` | string | `json` (the default), `text`, `verbose_json`, `srt` or `vtt`. `gpt-4o-transcribe` and `gpt-4o-mini-transcribe` take `json` and `text` only. |
| `language` | string | The language of the audio as an ISO-639-1 code, for example `en`. |
| `prompt` | string | Text that guides the style or spelling of the transcript. |
| `temperature` | number | From 0 to 1. |
| `timestamp_granularities[]` | string | `segment` or `word`. With `whisper-1` and `verbose_json` only. |
| `provider` | JSON string | Routing controls. See [Provider routing](/docs/provider-routing). |

RouterPlus drops any other field and names it in the `x-tm-dropped-params` header. With
`provider.require_parameters: true`, a dropped field is a 400. `stream=true` is a 400: the
route returns one response.

### Response

`json`:

```json
{
  "text": "Hello there.",
  "usage": { "type": "tokens", "input_tokens": 1500, "output_tokens": 40, "total_tokens": 1540, "cost": 0.00415 }
}
```

`whisper-1` reports the billed seconds:

```json
{
  "text": "Hello there.",
  "usage": { "type": "duration", "seconds": 62, "cost": 0.0062 }
}
```

| Format | Content type | Body |
|---|---|---|
| `json` | `application/json` | `text` and `usage` |
| `verbose_json` | `application/json` | `text`, `language`, `duration`, `segments` (and `words` when asked for), and `usage` |
| `text` | `text/plain` | The transcript |
| `srt` | `text/plain` | SubRip subtitles, one cue per segment |
| `vtt` | `text/vtt` | WebVTT subtitles, one cue per segment |

`usage.cost` is the charge in USD for the request. The `text`, `srt` and `vtt` formats do not
carry it. `GET /v1/generation` shows it for every request.

## POST /v1/audio/speech

### Request

The body is JSON, at most 1 MB.

| Parameter | Type | Behavior |
|---|---|---|
| `model` | string, required | A speech model id. |
| `input` | string, required | The text to speak. At most 4,096 characters. |
| `voice` | string, required | A voice name, for example `alloy`, `coral` or `nova`. |
| `response_format` | string | `mp3` (the default), `opus`, `aac`, `flac`, `wav` or `pcm`. |
| `speed` | number | From 0.25 to 4. The default is 1. |
| `instructions` | string | How to speak, for example "calm and slow". `gpt-4o-mini-tts` only. The tokens count as input. Another model drops the field. |
| `stream_format` | string | `audio` only. Any other value is a 400. |
| `provider` | object | Routing controls. See [Provider routing](/docs/provider-routing). |

### Response

The body is the audio file in `response_format`. RouterPlus sends each part of the audio when
it arrives from the provider, so playback can start before the speech is complete.

| Format | Content type |
|---|---|
| `mp3` | `audio/mpeg` |
| `opus` | `audio/opus` |
| `aac` | `audio/aac` |
| `flac` | `audio/flac` |
| `wav` | `audio/wav` |
| `pcm` | `audio/pcm` (raw 24 kHz, 16-bit, mono samples) |

The response does not carry the cost. `GET /v1/generation` shows it.

## Billing

1. Before the call, RouterPlus holds an estimate of the cost on your balance.
2. After the call, it charges the billed units from the [Models](#models) table and releases the rest of the hold.
3. If the provider reports no usage, RouterPlus charges the estimate it held.

The hold for each model:

| Model | Hold |
|---|---|
| `gpt-4o-transcribe`, `gpt-4o-mini-transcribe` | 16,000 input tokens and 2,000 output tokens |
| `whisper-1` | The length of the audio. For a wav or mp3 file, RouterPlus reads the length from the file. For another format, it assumes 1,000 bytes per second. |
| `gpt-4o-mini-tts` | The input text at 3 bytes per token, and 2 audio tokens per character divided by `speed` |
| `tts-1`, `tts-1-hd` | The characters of `input` |

A failed attempt costs nothing.

## Routing and failover

RouterPlus picks the provider with the same rules as chat. [Guardrails](/docs/guardrails)
apply: a model your guardrail does not allow is a 403 with `error_type: "guardrail_blocked"`.

If a provider fails before it answers (a 5xx, a 429 or a timeout), RouterPlus tries the next
provider of the model. For speech, failover stops at the first byte of audio. After that byte,
the answer comes from that provider. `x-tm-attempts` gives the number of attempts.
`x-tm-provider` names the provider that answered.
