# Switch from Deepgram

A client written for Deepgram’s live transcription API connects to .wave by changing its base URL and its key. The messages it reads are the same. This page shows the change for each client, and lists what carries over and what does not.

[Create an account](https://api.dotwave.ai/auth/signup) · [Deepgram-compatible API](https://dotwave.ai/docs/deepgram/)

> **Public beta.** We publish no accuracy figures per language yet. Run your own audio through both before you switch production traffic, and keep Deepgram configured as a fallback while you do.

## The change

- **Base URL**: `api.deepgram.com` becomes `api.dotwave.ai`. The path stays `/v1/listen`.
- **Key**: Your .wave API key, sent as before: `Authorization: Token <API key>`.
- **Model**: `nemotron-asr-streaming`, or leave your plugin’s default: the `model` parameter is accepted as long as it is not empty.
- **End of speech**: Keep sending `Finalize` when your voice detection ends a turn, or set `utterance_end_ms`. Deepgram’s acoustic `endpointing` has no equivalent here. See [what changes](https://dotwave.ai/docs/deepgram/switch/#differences-title).

## Price

| Streaming transcription | Price per minute | Billed |
| --- | --- | --- |
| .wave, Nemotron 3.5 ASR Streaming 0.6B | $0.00045 | Per minute of session, from the first audio until it closes |
| Deepgram Nova-3, monolingual, pay as you go | $0.0048 (Promotional rate; $0.0077 listed as the regular rate) | Per second of audio |
| Deepgram Nova-3, multilingual, pay as you go | $0.0058 (Promotional rate; $0.0092 listed as the regular rate) | Per second of audio |

Deepgram’s rates are read from [deepgram.com/pricing](https://deepgram.com/pricing) on 10 October 2026 and may have changed since. .wave’s rate and credits are on [Pricing and limits](https://dotwave.ai/docs/pricing-and-limits/).

## Deepgram Python SDK

With `deepgram-sdk` 5 and later, the client takes an environment that names the servers. Give it .wave’s, and the rest of the program stays as it is.

```python
import os
from deepgram import DeepgramClient, DeepgramClientEnvironment
from deepgram.core.events import EventType

WAVE = DeepgramClientEnvironment(
    base="https://api.dotwave.ai",
    production="wss://api.dotwave.ai",
    agent="wss://api.dotwave.ai",        # unused: .wave has no agent API
    agent_rest="https://api.dotwave.ai",
)
client = DeepgramClient(api_key=os.environ["DOTWAVE_API_KEY"], environment=WAVE)

with client.listen.v1.connect(
    model="nemotron-asr-streaming",
    language="pt-BR",          # leave it out to detect the language
    encoding="linear16",
    sample_rate="16000",
    interim_results="true",
) as connection:
    def on_message(message):
        if message.type == "Results" and message.is_final:
            print(message.channel.alternatives[0].transcript)

    connection.on(EventType.MESSAGE, on_message)
    # Send 16 kHz mono PCM16 at the speed of speech with connection.send_media(chunk),
    # connection.send_finalize() at the end of each turn, and
    # connection.send_close_stream() to end the session.
    connection.start_listening()
```

With `deepgram-sdk` 3 and 4, pass `DeepgramClientOptions(url="api.dotwave.ai")` to `DeepgramClient` instead.

## Deepgram JavaScript SDK

With `@deepgram/sdk` 5 and later, `baseUrl` is the root the SDK joins `/v1/listen` to.

```typescript
import { DeepgramClient } from "@deepgram/sdk";

const client = new DeepgramClient({
  apiKey: process.env.DOTWAVE_API_KEY,
  baseUrl: "wss://api.dotwave.ai",
});

const connection = await client.listen.v1.connect({
  model: "nemotron-asr-streaming",
  language: "pt-BR",           // leave it out to detect the language
  encoding: "linear16",
  sample_rate: "16000",
  interim_results: "true",
});

connection.on("message", (message) => {
  if (message.type === "Results" && message.is_final) {
    console.log(message.channel.alternatives[0].transcript);
  }
});

connection.connect();
await connection.waitForOpen();
// connection.sendMedia(chunk) at the speed of speech,
// connection.sendFinalize({ type: "Finalize" }) at the end of each turn,
// connection.sendCloseStream({ type: "CloseStream" }) to end the session.
```

With `@deepgram/sdk` 3 and 4, set `global.websocket.options.url` to `wss://api.dotwave.ai` in the options of `createClient`.

## LiveKit Agents

The Deepgram plugin takes a base URL. Keep `endpointing_ms=0`, since enabled endpointing is refused here, and let the agent’s own turn detection send `Finalize`.

```python
import os
from livekit.plugins import deepgram

stt = deepgram.STT(
    api_key=os.environ["DOTWAVE_API_KEY"],
    base_url="https://api.dotwave.ai/v1/listen",
    model="nemotron-asr-streaming",
    language="pt-BR",
    endpointing_ms=0,
)
```

## Pipecat

The Deepgram service takes a bare host as its base URL. The pipeline’s voice detection sends `Finalize` at the end of each turn, as it does with Deepgram. For a new pipeline, the `pipecat-dotwave` service on the [plugins page](https://dotwave.ai/docs/deepgram/plugins/) is the shorter path.

```python
import os
from pipecat.services.deepgram.stt import DeepgramSTTService
from pipecat.transcriptions.language import Language

stt = DeepgramSTTService(
    api_key=os.environ["DOTWAVE_API_KEY"],
    base_url="api.dotwave.ai",
    settings=DeepgramSTTService.Settings(
        model="nemotron-asr-streaming",
        language=Language.PT_BR,
    ),
)
```

## Vapi

Vapi’s transcriber catalog has no .wave entry yet. Its [custom transcriber](https://docs.vapi.ai/customization/custom-transcriber) interface sends the call’s audio to a WebSocket server you host; a small open-source server forwards each channel to .wave and returns the transcripts in Vapi’s shape. Run it where Vapi can reach it, then point the assistant at it.

```json
{
  "transcriber": {
    "provider": "custom-transcriber",
    "server": {
      "url": "wss://transcriber.example.com/api/custom-transcriber",
      "credentialId": "cred_…"
    }
  }
}
```

## A plain WebSocket

Without an SDK, only the host changes.

- **Connect**: `wss://api.dotwave.ai/v1/listen?encoding=linear16&sample_rate=16000&language=pt-BR&interim_results=true`
- **Header**: `Authorization: Token <API key>`

The [Deepgram-compatible API](https://dotwave.ai/docs/deepgram/) page has a complete example in Python.

## What carries over

| Deepgram | On .wave |
| --- | --- |
| `Authorization: Token` | The same header, with your .wave API key. |
| `Metadata`, `Results`, `SpeechStarted`, `UtteranceEnd` | The same messages, in the same shapes. See [Messages](https://dotwave.ai/docs/deepgram/messages/). |
| `is_final`, `speech_final`, `from_finalize` | The same meaning: a final holds the words since the previous final; `speech_final` ends a segment. |
| `words[]` with `word`, `punctuated_word`, `start`, `end` | The same fields. `confidence` is always 1.0. |
| `Finalize`, `CloseStream`, `KeepAlive` | Accepted. `Finalize` answers with a final marked `from_finalize`. |
| `encoding=linear16`, `mulaw`, `alaw` | Accepted: PCM16 at 16 or 24 kHz, G.711 at 8 kHz. See [Parameters](https://dotwave.ai/docs/deepgram/parameters/). |
| `language`, `language=multi` | Accepted, with the tags on the [language list](https://dotwave.ai/docs/realtime/languages/). `multi`, or no language, detects the language. |
| `interim_results`, `utterance_end_ms` | Accepted. `utterance_end_ms` takes 1000 to 3200. |
| `punctuate`, `smart_format`, `model` | Accepted, so plugins keep their defaults. The model punctuates and capitalises on its own. |

## What changes

| Deepgram | On .wave |
| --- | --- |
| `endpointing` | Acoustic end of speech is not detected. `endpointing=false` is accepted; an enabled value is refused by name. Send `Finalize` from your own voice detection, or set `utterance_end_ms`, which ends a segment on the pause between recognised words. |
| `diarize`, `multichannel`, `keywords`, `keyterm` | Refused, with an error that names the parameter. Send one channel; split stereo audio into two sessions. |
| `channels=2` | Audio must have one channel. |
| Pre-recorded transcription over HTTP | Not offered. Stream a file at the speed of speech over the same socket. |
| Audio sent faster than real time | The server holds up to 640 ms ahead of real time; past that the session closes with code 1013. Pace recorded audio. |
| Quiet sessions | A session closes after 30 seconds without audio. `KeepAlive` does not extend it; send silence instead. |
| Interim results that revise earlier words | Text arrives as whole words that do not change. An interim is the segment so far; a final holds the words since the previous final. |
| Word `confidence` | Always 1.0: the model does not score its words. |
| HTTP 401, 429 on connect | The socket opens, then closes with 4401 for a bad credential or 4429 when there is no capacity. See the close codes under [Messages](https://dotwave.ai/docs/deepgram/messages/). |

## Before you switch production traffic

- **Transcribe your own audio**: Run a sample of your calls through both and compare the transcripts yourself; accuracy depends on the language, the microphone and the vocabulary.
- **Check your end-of-turn path**: If you relied on `endpointing`, make sure your client sends `Finalize` or that `utterance_end_ms` is set, or finals arrive only when the socket closes.
- **Handle refusals and close codes**: Remove `diarize`, `multichannel` and `keywords` from the query, and retry on 4429 and 4413 after a short wait.
- **Mind the concurrency limit**: Each project has a limit on concurrent sessions. Read it with `GET /v1/project` and write to [info@dotwave.ai](mailto:info@dotwave.ai) to raise it before you move traffic.
