> ## Documentation Index
> Fetch the complete documentation index at: https://docs.rime.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# STT SDK reference

> Transcribe speech with the Rime SDK in Python, Node.js, Go, or Rust. This reference covers the stream call, the whole-file call, recognition options, audio formats, partial and final results, limits, and errors.

`stream` transcribes one utterance. It returns partial text while audio arrives. When speech ends, end the audio source, read the final result, and let the stream complete. `transcribe` sends one complete WAV file and returns only the final result. This reference lists the methods, options, defaults, and errors. For walkthroughs and complete programs, see the [SDK guides and examples](#guides-and-examples).

## Call stream

Each example below starts one stream with a 120-second overall timeout and reads every update. `client` is an open client, and `source` is an audio source. [Options by language](#options-by-language) gives the source type for each language.

```python Python theme={null}
async with client.stt.stream(source, language="en", timeout=120) as transcript:
    async for update in transcript:
        print(update.kind, update.text)
```

```typescript Node.js / TypeScript theme={null}
const transcript = client.stt.stream(source, { language: "en", timeout: 120 });
for await (const update of transcript) {
  console.log(update.kind, update.text);
}
```

```go Go theme={null}
timeout := 120 * time.Second
transcript, err := client.STT.Stream(ctx, source, rime.TranscriptionOptions{Language: "en", Timeout: &timeout})
if err != nil {
	return err
}
defer transcript.Close()
for {
	update, err := transcript.Recv()
	if err == io.EOF {
		break
	}
	if err != nil {
		return err
	}
	switch update := update.(type) {
	case rime.TranscriptionPartial:
		fmt.Println("partial", update.Text)
	case rime.TranscriptionFinal:
		fmt.Println("final", update.Text, update.Language)
	}
}
```

```rust Rust theme={null}
let options = TranscriptionOptions::new("en").timeout(Some(Duration::from_secs(120)));
let mut transcript = client.stt().stream(source, options)?;
while let Some(update) = transcript.next().await {
    match update? {
        TranscriptionUpdate::Partial { text } => println!("partial {text}"),
        TranscriptionUpdate::Final { text, language } => println!("final {text} {language}"),
    }
}
```

## Call transcribe

`transcribe` sends one complete WAV file and returns one final transcript, with no partial updates. Each example below transcribes `audio`, the bytes of a WAV file, with a 120-second overall timeout.

```python Python theme={null}
result = await client.stt.transcribe(audio, language="en", timeout=120)
print(result.text, result.language, result.request_id)
```

```typescript Node.js / TypeScript theme={null}
const result = await client.stt.transcribe(audio, { language: "en", timeout: 120 });
console.log(result.text, result.language, result.requestId);
```

```go Go theme={null}
timeout := 120 * time.Second
result, err := client.STT.Transcribe(ctx, audio, rime.TranscribeOptions{Language: "en", Timeout: &timeout})
if err != nil {
	return err
}
fmt.Println(result.Text, result.Language, result.RequestID)
```

```rust Rust theme={null}
let options = TranscribeOptions::new("en").timeout(Some(Duration::from_secs(120)));
let result = client.stt().transcribe(audio, options).await?;
println!("{} {} {:?}", result.text, result.language, result.request_id);
```

| Language | Call | Result |
| - | - | - |
| Python | `await client.stt.transcribe(audio, language="en", ...)`, with `audio` as `bytes` | `Transcription`, with `.text`, `.language`, and `.request_id` |
| Node.js | `await client.stt.transcribe(audio, { language: "en", ... })`, with `audio` as a `Uint8Array`, such as a `Buffer` | `.text`, `.language`, and `.requestId`, which can be `null` |
| Go | `client.STT.Transcribe(ctx, audio, rime.TranscribeOptions{...})`, with `audio` as `[]byte`. Do not change `audio` until the call returns. | `rime.Transcription`, with `.Text`, `.Language`, and `.RequestID` |
| Rust | `client.stt().transcribe(audio, TranscribeOptions::new("en"))`, with `audio` as any `AsRef<[u8]>` | `Transcription`, with `.text`, `.language`, and `.request_id`, an `Option<String>` |

The call works this way in every SDK:

| Item | Behavior |
| - | - |
| Versions | Python 0.1.0a16, Node.js 0.1.0-alpha.17, Go v0.1.0-alpha.9, Rust 0.1.0-alpha.7, or later |
| Audio | One complete WAV file, with its header, up to 16 MiB. The SDK sends the file without changes, so it does not decode, resample, or add a header. Rime guarantees only PCM16 mono at 16 kHz. |
| Options | The `stream` options, except the input format, because the WAV header gives the format. Go and Rust use `TranscribeOptions` in place of `TranscriptionOptions`. |
| Result | The final text, the language that the service confirmed, and the request ID. Silence can give empty text. |
| Size | Up to 16 MiB. The SDK raises a resource-limit error before it sends a larger file. [Limits](/docs/stt/errors#limits) gives how much speech fits in one call. |
| Deadline | The SDK limits the request to 120 seconds. The optional overall timeout also covers connecting and the upload. |
| Retries | To retry, make a new call, as [Decide whether to retry](/docs/stt/errors#decide-whether-to-retry) explains. |
| Cancel | In Python, cancel the task. In Node.js, abort the `signal`. In Go, cancel the context. In Rust, drop the future. In every SDK, closing the client also cancels the call. |

## Guides and examples

The [Rime SDK repository](https://github.com/rimelabs/rime-sdk) keeps the STT guide and a runnable example for each language next to the SDK source. Each example transcribes a headerless PCM16 recording and prints every update.

| Language | STT guide | Example program |
| - | - | - |
| Python | [`python/docs/stt.md`](https://github.com/rimelabs/rime-sdk/blob/main/python/docs/stt.md) | [`python/examples/stt/stream.py`](https://github.com/rimelabs/rime-sdk/blob/main/python/examples/stt/stream.py) |
| Node.js / TypeScript | [`typescript/docs/stt.md`](https://github.com/rimelabs/rime-sdk/blob/main/typescript/docs/stt.md) | [`typescript/examples/stt/stream.mjs`](https://github.com/rimelabs/rime-sdk/blob/main/typescript/examples/stt/stream.mjs) |
| Go | [Go README, STT section](https://github.com/rimelabs/rime-sdk/blob/main/go/README.md#stt) | [`go/examples/transcribe`](https://github.com/rimelabs/rime-sdk/blob/main/go/examples/transcribe/main.go) |
| Rust | [Rust README, STT section](https://github.com/rimelabs/rime-sdk/blob/main/rust/README.md#stt) | [`rust/examples/transcribe.rs`](https://github.com/rimelabs/rime-sdk/blob/main/rust/examples/transcribe.rs) |

To try STT on a hosted sample recording first, follow the [speech-to-text quickstart](/docs/stt/quickstart).

## Connection and credentials

| Item | Behavior |
| - | - |
| Endpoint | The SDK always connects to `stt.api.rime.ai:443` over gRPC with TLS. STT has no endpoint, model, or voice setting. |
| API key | The SDK reads the key from `RIME_API_KEY`, unless you set a key in the client configuration. It sends the key as a bearer token. Keep the key on your backend. |
| Client settings | `endpoint`, `model`, and `timeout` apply only to TTS. Set the STT timeout on each `stream` or `transcribe` call. |
| Output contract | `stream` always requests the [revised-hypotheses output contract](/docs/speech-to-text#choose-how-to-connect), so each partial transcript is the whole text so far. The SDK has no append-fragment mode. If you need text that does not change after you receive it, call the API directly. |

## Recognition options

Every call needs a language tag. The other options are optional.

| Option | Required | Default | Values |
| - | - | - | - |
| Language | Yes | None | `en` for English or `es` for Spanish |
| Mode | No | Written | Written or verbatim |
| Context terms | No | None | A list of strings |
| Input format | No | 16 kHz mono PCM16 | Headerless PCM16 at 8, 16, 24, or 48 kHz, mono or stereo. Stereo channels are interleaved. |
| Overall timeout | No | None | See [Timeouts and limits](#timeouts-and-limits). |

The SDK sends the language tag to the service without changes, and the service validates it. [Recognition settings](/docs/stt/recognition) explains how the language, mode, and context terms change the transcript, and which values the service rejects. In the input format, headerless PCM16 means signed 16-bit little-endian samples. Set the input format to match your source audio. The option names and types differ by SDK language, as the next section shows.

## Options by language

**Python**

Call `client.stt.stream(source, language="en", ...)` without `await`. The call returns a transcript stream, and network work starts when you enter its async context or read the first result.

| Option | Value |
| - | - |
| `language` | Required language tag |
| `mode` | `TranscriptionMode.WRITTEN` or `.VERBATIM` |
| `context_terms` | List of strings |
| `input_format` | Example override: `PCMFormat(sample_rate=48000, channels=2)` |
| `timeout` | Positive seconds as an `int` or `float`, or a positive `datetime.timedelta`. `None` disables it. A `timedelta` needs 0.1.0a17 or later. |

Import `PCMFormat` and `TranscriptionMode` from `rimelabs_sdk`. The source and the results use these types:

| Item | Python |
| - | - |
| Source | `AsyncIterable[bytes]` |
| Read results | Enter the transcript with `async with`, and then read it with `async for update in transcript`. |
| Partial update | `TranscriptionPartial`, with `kind == "partial"` and `.text` |
| Final update | `TranscriptionFinal`, with `kind == "final"`, `.text`, and `.language` |
| Source lifetime | See the [Python package](https://github.com/rimelabs/rime-sdk/tree/main/python) and the [STT guide](https://github.com/rimelabs/rime-sdk/blob/main/python/docs/stt.md). |

**Node.js / TypeScript**

Call `client.stt.stream(source, { language: "en", ... })` without `await`. The call returns a transcript stream, and the first read starts network work.

| Option | Value |
| - | - |
| `language` | Required language tag |
| `mode` | `"written"` or `"verbatim"` |
| `contextTerms` | Array of strings |
| `inputFormat` | Example override: `{ sampleRate: 48000, channels: 2 }` |
| `timeout` | Positive seconds. `null` disables it. |
| `signal` | `AbortSignal` for this utterance |

The source and the results use these types:

| Item | Node.js |
| - | - |
| Source | `AsyncIterable<Uint8Array>` |
| Read results | `for await (const update of transcript)`. Check `kind` for `"partial"` or `"final"`. |
| Partial update | `.text` |
| Final update | `.text` and `.language` |
| Source lifetime | See the [TypeScript package](https://github.com/rimelabs/rime-sdk/tree/main/typescript) and the [STT guide](https://github.com/rimelabs/rime-sdk/blob/main/typescript/docs/stt.md). |

**Go**

Call `client.STT.Stream(ctx, source, options)` with a non-nil context and `rime.TranscriptionOptions`. It returns `(*rime.TranscriptionStream, error)`, and the first `Recv()` starts network work.

| Field | Value |
| - | - |
| `Language` | Required language tag |
| `Mode` | `rime.TranscriptionWritten` or `rime.TranscriptionVerbatim` |
| `ContextTerms` | `[]string` |
| `InputFormat` | Example override: `rime.PCMFormat{SampleRate: 48000, Channels: 2}` |
| `Timeout` | `*time.Duration`. Nil or a pointer to zero disables it |

Context deadlines always apply, even when `Timeout` is disabled. The source and the results use these types and rules:

| Item | Go |
| - | - |
| Source | A `rime.AudioSource`, which is a `func(context.Context) ([]byte, error)`. Your source must honor context cancellation. |
| Returned bytes | Do not change the bytes that the source returns until the next callback. |
| End of the source | Return `io.EOF`. If the callback returns bytes with `io.EOF`, the SDK sends those bytes before it closes input. |
| Source errors | Any source error other than `io.EOF` cancels recognition. |
| Read results | `transcript.Recv()`. Switch on `rime.TranscriptionPartial` or `rime.TranscriptionFinal`. |
| Partial update | `.Text` |
| Final update | `.Text` and `.Language`. After the final update, `Recv()` returns `io.EOF`. |
| Source lifetime | See the [Go package and STT guide](https://github.com/rimelabs/rime-sdk/tree/main/go#stt). |

**Rust**

Rust STT needs `rimelabs-sdk` version `0.1.0-alpha.4` or later. Call `client.stt().stream(source, options)`. It returns `Result<TranscriptStream, Error>`, and the first poll starts network work.

| `TranscriptionOptions` method | Value |
| - | - |
| `new(language)` | Required language tag |
| `.mode(...)` | `TranscriptionMode::Written` or `TranscriptionMode::Verbatim` |
| `.context_terms(...)` | `Vec<String>` |
| `.input_format(...)` | Example override: `PcmFormat { sample_rate: 48000, channels: 2 }` |
| `.timeout(...)` | `Option<std::time::Duration>`. `None` disables it. Zero is invalid |

Import these types from `rimelabs_sdk`. The source, the runtime, and the results use these types and rules:

| Item | Rust |
| - | - |
| Source | Must implement `Stream<Item = Result<Bytes, Error>> + Send + 'static`. Yield `bytes::Bytes` chunks. |
| Source errors | Convert source errors with `Error::input(error)`. The `Result` items let a source report a failure apart from its normal end. A source that cannot fail still wraps each chunk in `Ok`, for example with `source.map(Ok)`. |
| Source reads | Keep source reads asynchronous, because a blocking read blocks the Tokio runtime. |
| Runtime | Like every SDK operation, call `stream` inside a Tokio runtime that has I/O and time enabled. Outside a runtime, `stream` returns an `Input` error. If the runtime does not have time enabled, `stream` panics. |
| Read results | With `futures_util::StreamExt`, `transcript.next().await` returns `Option<Result<TranscriptionUpdate, Error>>`. |
| Partial update | `TranscriptionUpdate::Partial { text }` |
| Final update | `TranscriptionUpdate::Final { text, language }`. The next read after a final result or error returns `None`. |
| Types and methods | See the [Rust package and STT guide](https://github.com/rimelabs/rime-sdk/tree/main/rust#stt) or the [Rust API reference](https://docs.rs/rimelabs-sdk). |

## Audio formats and source completion

| Item | Behavior |
| - | - |
| Encoding | Headerless, signed 16-bit little-endian PCM. `stream` does not decode WAV headers or compressed formats such as MP3. For a WAV file, use [`transcribe`](#call-transcribe). |
| Rate and channels | 8, 16, 24, or 48 kHz, with one or two channels. Stereo audio must be interleaved. The SDK converts this PCM input to 16 kHz mono. |
| Chunks | A chunk can split a sample frame, and the SDK keeps the incomplete bytes for the next chunk. An incomplete final frame fails with an audio-format error. |
| Upload | Audio upload starts after the service accepts the configuration. From then on, upload and transcript reading run at the same time. |
| End of the utterance | End the source to finish the utterance, because an empty chunk does not end it. When the source ends, the SDK closes gRPC input and continues to read results. |

```mermaid theme={null}
sequenceDiagram
    autonumber
    participant Backend as Your backend
    participant SDK as Rime SDK
    participant STT as Rime STT
    Backend->>SDK: Call stream. Start to read.
    SDK->>STT: Connect. Send the configuration.
    STT->>SDK: Configuration accepted
    Note over Backend,STT: Upload and reading run at the same time.
    Backend->>SDK: Audio chunks from the source
    SDK->>STT: Audio as 16 kHz mono
    STT->>SDK: Revised hypotheses
    SDK->>Backend: Partial updates
    Backend->>SDK: The source ends
    SDK->>STT: Close gRPC input
    STT->>SDK: Final transcript
    SDK->>Backend: Final update. The stream completes.
```

## Partial and final results

Each update contains the complete current transcript, so replace the previous text with each update, and do not append. [How a transcription works](/docs/speech-to-text#how-a-transcription-works) explains partial and final transcripts. The results of an SDK stream also behave this way:

| Item | Behavior |
| - | - |
| Final result | A successful stream returns one final result. It arrives after the service confirms completion and the gRPC call succeeds. |
| Language | The `language` of the final result is the [language](/docs/stt/recognition#language) that the service confirmed, `en` or `es`. |
| Silence | Silence can give empty final text with no partials. |
| Failure or cancellation | The last partial transcript is incomplete. |
| Readers | Use one result reader for each stream. In Go, a second concurrent `Recv()` returns `rime.ErrInput`. |
| End of speech | Your application decides when speech ends, and then it [ends the source](/docs/stt/live-audio#end-the-utterance). The SDK does not detect speech or end a turn. |
| Content | Transcript text only, with no word timings or speaker labels |

## Timeouts and limits

Service limits apply to every stream, whatever you set in the SDK, and [Errors and limits](/docs/stt/errors#limits) lists them all. For example, a stream fails after 15 seconds without audio. Silence counts as audio, so keep sending silence through pauses.

Pass `timeout` to set an overall deadline for one stream. There is no overall deadline by default. The deadline covers connecting, sending, and reading, and it includes the time that your source or your reader spends waiting. It stays active until you read the final transcript. In every SDK, the clock starts when work starts:

| Language | The clock starts at |
| - | - |
| Python | Entering `async with`, or the first read |
| Node.js | The first read |
| Go | The first `Recv()` |
| Rust | The first poll |

The SDK also enforces these limits, whatever your timeout is. Each limit applies in all four languages, unless its row says something different:

| SDK limit | Value |
| - | - |
| Connecting | 10 seconds |
| Service accepting the configuration | 10 seconds |
| Final transcript after your source ends | 120 seconds. The service has its own 120-second deadline, which counts from connecting. That deadline comes first. |
| Source cleanup after cancellation | 2 seconds in Python, Node.js, and Go. Rust has no cleanup limit. `close().await` waits for the worker to finish. |
| Unread updates queued | 16 |
| Text in one update | 64 KiB of UTF-8 |
| One gRPC response | 256 KiB |

## Cancel and clean up

When you cancel one stream, the other streams stay active. When you close the client, all its operations stop. A source that owns a device must handle cancellation and release its resources. Each SDK cancels a stream this way:

| Language | Cancel and clean up |
| - | - |
| Python | Task cancellation, context exit, or `await transcript.cancel()` cancels unfinished work. A task cancellation applies while the task waits for the next update, or while it runs inside the `async with` block. The task raises `asyncio.CancelledError`, not `RimeCancelledError`. |
| Node.js | When your code leaves the iterator, the SDK cancels unfinished work. |
| Go | Use `defer transcript.Close()` to clean up. |
| Rust | To request cancellation, drop the stream or call `transcript.cancel()`. The SDK then drops the source, but it cannot stop external work that your application started separately. To also wait for worker cleanup, use `transcript.close().await`. `client.close().await` stops all TTS and STT streams of the client. |

## Errors

Every SDK failure belongs to one error category, so branch on the category, not on the message text. The SDK does not retry a failed stream or send its audio again. [Decide whether to retry](/docs/stt/errors#decide-whether-to-retry) tells which categories are worth a new stream.

| Category | Python and Node.js | Go (`errors.Is`) | Rust (`ErrorKind`) | When STT raises it |
| - | - | - | - | - |
| Invalid input | `RimeInputError` | `rime.ErrInput` | `Input` | The service rejected the language, mode, or context terms. Or your code used the stream incorrectly, for example with a second reader. |
| Audio format | `RimeAudioFormatError` | `rime.ErrAudioFormat` | `AudioFormat` | The input format is not supported. Or the source ended partway through a sample frame. |
| Resource limit | `RimeResourceLimitError` | `rime.ErrResourceLimit` | `ResourceLimit` | The audio, a transcript update, or the model context went over a limit. Or the service had no capacity. |
| Timeout | `RimeTimeoutError` | `rime.ErrTimeout` | `Timeout` | Your timeout, an SDK limit, or a service deadline passed. |
| Unavailable | `RimeUnavailableError` | `rime.ErrUnavailable` | `Unavailable` | The service was not ready, was shutting down, or ended without a final transcript. |
| Cancelled | `RimeCancelledError` | `rime.ErrCancelled` | `Cancelled` | The stream was cancelled. |
| Stream | `RimeStreamError` | `rime.ErrStream` | `Stream` | An internal service error, or a protocol failure that the SDK detected |
| Authentication | `RimeAuthenticationError` | `rime.ErrAuthentication` | `Authentication` | The client had no API key when you created it, or Rime did not accept the key. |

Python and Node.js errors inherit from `RimeError`. In Rust, call `error.kind()` for the category. Keep the request ID of every stream, and include it when you [report a failure](/docs/stt/errors#report-a-failure). Each SDK gives the request ID this way:

| Language | From the stream | From an error |
| - | - | - |
| Python | `transcript.request_id` | `error.request_id` |
| Node.js | `transcript.requestId` | `error.requestId` |
| Go | `transcript.RequestID()` | Use `errors.As` with `*rime.Error` to read `RequestID`. |
| Rust | `transcript.request_id()`, which returns `Option<&str>` | `error.request_id()` |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.