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

# Realtime Speech-to-Text Quickstart

> Stream audio to Ink-2 and receive transcripts in real time.

Use this tutorial to make your first realtime STT calls with Ink-2.

## Prerequisites

* A Cartesia API key. [Create one here](https://play.cartesia.ai/keys), then add it to your shell profile:

  ```sh theme={null}
  export CARTESIA_API_KEY=<your api key here>
  ```

* A language runtime and package manager:
  * **Python**: [Python 3](https://www.python.org/downloads/) and [pip](https://pip.pypa.io/en/stable/installation/)
  * **TypeScript**: [Node.js](https://nodejs.org/) (includes npm)

## Stream audio and receive transcripts

In these scripts, we programmatically invoke Cartesia's TTS to generate audio input that we then pass to Ink-2 for transcription.

<Tabs>
  <Tab title="Python">
    <Steps>
      <Step title="Install the client library">
        ```sh theme={null}
        python3 -m pip install -U "cartesia[websockets]"
        ```
      </Step>

      <Step title="Create a realtime STT script">
        ```python realtime-stt.py theme={null}
        import os
        import time
        from cartesia import Cartesia

        api_key = os.getenv("CARTESIA_API_KEY")
        if not api_key:
            raise RuntimeError("Set CARTESIA_API_KEY first.")

        client = Cartesia(api_key=api_key)

        # Use TTS to generate test audio bytes once, then stream it to STT.
        tts_response = client.tts.generate(
            model_id="sonic-3.5",
            transcript="Hello from Cartesia. This is a realtime STT quickstart.",
            voice={"mode": "id", "id": "694f9389-aac1-45b6-b726-9d9369183238"},
            output_format={"container": "raw", "encoding": "pcm_s16le", "sample_rate": 16000},
            language="en",
        )
        audio = tts_response.read()
        chunk_size = 3200  # 100ms of 16kHz pcm_s16le mono audio

        print("Sending audio chunks to Cartesia's Ink-2 model...")
        with client.stt.auto_finalize.websocket(
            model="ink-2",
            encoding="pcm_s16le",
            sample_rate=16000,
        ) as ws:
            for i in range(0, len(audio), chunk_size):
                ws.send_raw(audio[i : i + chunk_size])
                time.sleep(0.1)

            # We recommend draining the audio as per
            # https://docs.cartesia.ai/use-the-api/stt/turns#draining-events
            ws.send({"type": "close"})

            for event in ws:
                if event.type == "turn.update":
                    print(f"turn.update: {event.transcript}")
                elif event.type == "turn.end":
                    print(f"turn.end: {event.transcript}")
                elif event.type == "error":
                    raise RuntimeError(event.message)
        ```
      </Step>

      <Step title="Run the script">
        ```sh theme={null}
        python3 realtime-stt.py
        ```

        Your console should show Ink-2's emitted events - specifically `turn.update` events with incremental, additive transcripts. Finally, you should see a `turn.end` event with the full TTS transcript.
      </Step>
    </Steps>
  </Tab>

  <Tab title="TypeScript">
    <Steps>
      <Step title="Install dependencies">
        ```sh theme={null}
        npm init -y
        npm pkg set type=module
        npm install @cartesia/cartesia-js ws
        npm install --save-dev tsx typescript @types/node
        ```
      </Step>

      <Step title="Create a realtime STT script">
        ```typescript realtime-stt.ts theme={null}
        import Cartesia from "@cartesia/cartesia-js";

        const apiKey = process.env["CARTESIA_API_KEY"];
        if (!apiKey) {
          throw new Error("Set CARTESIA_API_KEY first.");
        }

        const client = new Cartesia({ apiKey });

        const ttsResponse = await client.tts.generate({
          model_id: "sonic-3.5",
          transcript: "Hello from Cartesia. This is a realtime STT quickstart.",
          voice: { mode: "id", id: "694f9389-aac1-45b6-b726-9d9369183238" },
          output_format: { container: "raw", encoding: "pcm_s16le", sample_rate: 16000 },
          language: "en",
        });

        const audio = Buffer.from(await ttsResponse.arrayBuffer());
        const chunkSize = 3200; // 100ms of 16kHz pcm_s16le mono audio

        console.log("Sending audio chunks to Cartesia's Ink-2 model...");
        const ws = client.stt.autoFinalize.websocket({
          model: "ink-2",
          encoding: "pcm_s16le",
          sample_rate: 16000,
        });

        const sender = (async () => {
          for (let i = 0; i < audio.length; i += chunkSize) {
            ws.sendRaw(audio.subarray(i, i + chunkSize));
            await new Promise((resolve) => setTimeout(resolve, 100));
          }
          // We recommend draining the audio as per
          // https://docs.cartesia.ai/use-the-api/stt/turns#draining-events
          ws.send({ type: "close" });
        })();

        for await (const event of ws.stream()) {
          if (event.type === "message") {
            if (event.message.type === "turn.update") {
              console.log(`turn.update: ${event.message.transcript}`);
            } else if (event.message.type === "turn.end") {
              console.log(`turn.end: ${event.message.transcript}`);
            } else if (event.message.type === "error") {
              throw new Error(event.message.message);
            }
          } else if (event.type === "error") {
            throw event.error;
          }
        }

        await sender;
        ```
      </Step>

      <Step title="Run the script">
        ```sh theme={null}
        npx tsx realtime-stt.ts
        ```

        You should see `turn.update` events, then a `turn.end` transcript.
      </Step>
    </Steps>
  </Tab>
</Tabs>

## What just happened

You streamed raw PCM audio to Ink-2 over WebSocket and read transcript events as they arrived.\
`turn.update` gives live progress for the active turn, and `turn.end` gives the finalized transcript for that turn.

## What's next

<CardGroup cols={3}>
  <Card title="Learn about Ink-2" icon="pen-nib" href="/build-with-cartesia/stt/latest">
    More on the realtime STT model.
  </Card>

  <Card title="Understand turn detection" icon="comments" href="/use-the-api/stt/turns">
    See how Ink-2 detects when a user starts and stops speaking.
  </Card>

  <Card title="Build with Ink" icon="readme" href="/build-with-cartesia/stt/guides">
    Find integrations, SDKs, examples, and migration guides.
  </Card>
</CardGroup>
