# SDKs

The official Gladia SDKs work with the Gladia Enterprise API: set the API address of your region.

> **Use the SDK.** The SDKs create the session, stream the audio, keep each chunk until the API has transcribed it, and reconnect
after a network drop, resending what was not transcribed. That is the hard part of a live integration: most
support requests we receive come from hand-written WebSocket clients.

```bash
pip install gladiaio-sdk      # Python 3.10 or later
npm install @gladiaio/sdk     # Node.js 20 or later, and browsers
```

## Options of Gladia's cloud API

Language, endpointing, custom vocabulary and spelling, translation, sentiment, callbacks, `custom_metadata`: pass
them to `start_session` / `startSession` as usual.

**Python SDK**

```python title="sdk_session.py"
# pip install gladiaio-sdk
import os
import sys
import threading
import wave

from gladiaio_sdk import GladiaClient, LiveV2InitRequest

API_URL = os.environ.get("GLADIA_API_URL", "https://eu1.api.gladia.io")
API_KEY = os.environ["GLADIA_API_KEY"]

with wave.open(sys.argv[1]) as wav:  # a 16-bit PCM WAV file
    rate, channels = wav.getframerate(), wav.getnchannels()
    audio = wav.readframes(wav.getnframes())

config = {
    "encoding": "wav/pcm",
    "sample_rate": rate,
    "bit_depth": 16,
    "channels": channels,
    "model": "solaria-3",
    "language_config": {
        "languages": [
            "en"
        ]
    },
    "realtime_processing": {
        "custom_vocabulary": True,
        "custom_vocabulary_config": {
            "vocabulary": [
                "Gladia"
            ]
        }
    }
}

client = GladiaClient(api_key=API_KEY, api_url=API_URL)
session = client.live_v2().start_session(LiveV2InitRequest.from_dict(config))
ended, errors = threading.Event(), []

@session.on("message")
def on_message(message):
    if message.type == "transcript" and message.data.is_final:
        print(message.data.utterance.text)

session.on("error", errors.append)
session.once("ended", lambda _: ended.set())

step = rate * channels * 2 // 10  # 100 ms of audio per chunk
for start in range(0, len(audio), step):
    session.send_audio(audio[start : start + step])  # live audio: send each chunk as it is captured
session.stop_recording()

ended.wait(timeout=60)
sys.exit(1 if errors or not ended.is_set() else 0)
```

**JavaScript SDK**

```js title="sdk_session.mjs"
// npm install @gladiaio/sdk
import { readFileSync } from "node:fs";
import { GladiaClient } from "@gladiaio/sdk";

const { sampleRate, channels, pcm } = readWav(process.argv[2]); // a 16-bit PCM WAV file

const config = {
  encoding: "wav/pcm",
  sample_rate: sampleRate,
  bit_depth: 16,
  channels,
  model: "solaria-3",
  language_config: {
    languages: [
      "en"
    ]
  },
  realtime_processing: {
    custom_vocabulary: true,
    custom_vocabulary_config: {
      vocabulary: [
        "Gladia"
      ]
    }
  }
};

const client = new GladiaClient({
  apiKey: process.env.GLADIA_API_KEY,
  apiUrl: process.env.GLADIA_API_URL ?? "https://eu1.api.gladia.io",
});
const session = client.liveV2().startSession(config);

session.on("message", (message) => {
  if (message.type === "transcript" && message.data.is_final) console.log(message.data.utterance.text);
});
session.on("error", (error) => {
  console.error(error);
  process.exitCode = 1;
});
session.once("ended", () => process.exit());

const step = (sampleRate * channels * 2) / 10; // 100 ms of audio per chunk
for (let start = 0; start < pcm.length; start += step) {
  session.sendAudio(pcm.subarray(start, start + step)); // live audio: send each chunk as it is captured
}
session.stopRecording();

function readWav(path) {
  const file = readFileSync(path);
  let fmt, pcm;
  for (let pos = 12; pos < file.length; ) {
    const id = file.toString("ascii", pos, pos + 4);
    const size = file.readUInt32LE(pos + 4);
    if (id === "fmt ") fmt = { channels: file.readUInt16LE(pos + 10), sampleRate: file.readUInt32LE(pos + 12) };
    if (id === "data") pcm = file.subarray(pos + 8, pos + 8 + size);
    pos += 8 + size + (size % 2);
  }
  return { ...fmt, pcm };
}
```

`GLADIA_API_KEY` and `GLADIA_API_URL` in the environment work too.

## Options of Gladia Enterprise

`profile`, `punctuation`, `numerals`, `end_of_turn`, `diarization`, `named_entity_recognition` with options, `pii`
and `turn_router` are not in the SDKs' types yet (gladiaio/sdk#61).

- **JavaScript**: pass them anyway, the SDK sends the configuration as is. In TypeScript, the configuration needs a
  cast (`as LiveV2InitRequest`).
- **Python**: `LiveV2InitRequest` **drops** the fields it does not know, without an error. Create the session over
  HTTP, then hand it to the SDK with `connect_session`: it streams, buffers and reconnects as usual.

**Python SDK**

```python title="sdk_enterprise_options.py"
# pip install gladiaio-sdk requests
import os
import sys
import threading
import wave

import requests
from gladiaio_sdk import GladiaClient, LiveV2ConnectSessionOptions

API_URL = os.environ.get("GLADIA_API_URL", "https://eu1.api.gladia.io")
API_KEY = os.environ["GLADIA_API_KEY"]

with wave.open(sys.argv[1]) as wav:  # a 16-bit PCM WAV file
    rate, channels = wav.getframerate(), wav.getnchannels()
    audio = wav.readframes(wav.getnframes())

config = {
    "encoding": "wav/pcm",
    "sample_rate": rate,
    "bit_depth": 16,
    "channels": channels,
    "model": "solaria-3",
    "language_config": {
        "languages": [
            "en"
        ]
    },
    "numerals": True,
    "end_of_turn": True
}

client = GladiaClient(api_key=API_KEY, api_url=API_URL)
# `numerals` and `end_of_turn` are Gladia Enterprise options the Python SDK does not send yet
# (gladiaio/sdk#61): create the session over HTTP, then let the SDK stream it.
created = requests.post(f"{API_URL}/v2/live", headers={"x-gladia-key": API_KEY}, json=config, timeout=10)
created.raise_for_status()
info = created.json()
session = client.live_v2().connect_session(LiveV2ConnectSessionOptions(id=info["id"], url=info["url"]))
ended, errors = threading.Event(), []

@session.on("message")
def on_message(message):
    if message.type == "transcript" and message.data.is_final:
        print(message.data.utterance.text)

def on_error(error):
    if "Unsupported websocket message type" not in str(error):  # end_of_turn messages: gladiaio/sdk#61
        errors.append(error)

session.on("error", on_error)
session.once("ended", lambda _: ended.set())

step = rate * channels * 2 // 10  # 100 ms of audio per chunk
for start in range(0, len(audio), step):
    session.send_audio(audio[start : start + step])  # live audio: send each chunk as it is captured
session.stop_recording()

ended.wait(timeout=60)
sys.exit(1 if errors or not ended.is_set() else 0)
```

**JavaScript SDK**

```js title="sdk_enterprise_options.mjs"
// npm install @gladiaio/sdk
import { readFileSync } from "node:fs";
import { GladiaClient } from "@gladiaio/sdk";

const { sampleRate, channels, pcm } = readWav(process.argv[2]); // a 16-bit PCM WAV file

const config = {
  encoding: "wav/pcm",
  sample_rate: sampleRate,
  bit_depth: 16,
  channels,
  model: "solaria-3",
  language_config: {
    languages: [
      "en"
    ]
  },
  numerals: true,
  end_of_turn: true
};

const client = new GladiaClient({
  apiKey: process.env.GLADIA_API_KEY,
  apiUrl: process.env.GLADIA_API_URL ?? "https://eu1.api.gladia.io",
});
// `numerals` and `end_of_turn` are Gladia Enterprise options not in the SDK's types yet: the SDK sends it as is.
const session = client.liveV2().startSession(config);

session.on("message", (message) => {
  if (message.type === "transcript" && message.data.is_final) console.log(message.data.utterance.text);
  if (message.type === "end_of_turn") console.log("  (end of turn)");
});
session.on("error", (error) => {
  console.error(error);
  process.exitCode = 1;
});
session.once("ended", () => process.exit());

const step = (sampleRate * channels * 2) / 10; // 100 ms of audio per chunk
for (let start = 0; start < pcm.length; start += step) {
  session.sendAudio(pcm.subarray(start, start + step)); // live audio: send each chunk as it is captured
}
session.stopRecording();

function readWav(path) {
  const file = readFileSync(path);
  let fmt, pcm;
  for (let pos = 12; pos < file.length; ) {
    const id = file.toString("ascii", pos, pos + 4);
    const size = file.readUInt32LE(pos + 4);
    if (id === "fmt ") fmt = { channels: file.readUInt16LE(pos + 10), sampleRate: file.readUInt32LE(pos + 12) };
    if (id === "data") pcm = file.subarray(pos + 8, pos + 8 + size);
    pos += 8 + size + (size % 2);
  }
  return { ...fmt, pcm };
}
```

## Limits and gotchas

- **Messages the Python SDK does not know** (`pii`, `end_of_turn`, `end_of_turn_score`, `error`) reach your
  `error` handler as "Unsupported websocket message type", and are lost to it. Read them with the JavaScript SDK or a
  raw WebSocket ([Live WebSocket](/api-reference/v2/live/websocket/)). The JavaScript SDK delivers every message.
- **Fields the Python SDK does not know on known messages** (`text_raw` and `entities` of an utterance, the `speaker`
  of a word) are not in its objects. The JavaScript SDK keeps them.
- **`model`** is typed `"solaria-1"` in both SDKs: `solaria-3` and `trantoria-1` work, with a cast in TypeScript.
- **In a browser**, never put the API key in the page: create the session on your server and give the page its
  `url`, then use `connectSession` ([Authentication](/introduction/authentication/#keep-the-key-on-your-server)).
- **`region`**: leave the SDKs' `region` option unset; the API address you set is the region.
