# Quickstart

Get a key, make Nur speak, read it back, and hold your first live conversation.

This takes about five minutes. You need a terminal with `curl` and `jq`, and
Python 3.10 or newer for the last step.

## 1. Get an API key

Sign in to the [console](https://platform.eesi.ai), open **Developer → API
keys** and create a key. Copy it now: the full secret is shown once.

```bash
export EESI_API_KEY="sk-eesi-..."
```

Keep the key on a server. For browsers, see
[Authentication](/develop/authentication).

## 2. Pick a voice

Every voice has an id. The library includes built-in voices, and any voice you
clone appears next to them.

```bash
curl -s https://api.eesi.ai/v1/voices \
  -H "Authorization: Bearer $EESI_API_KEY" \
  | jq -r '.data[] | "\(.voice_id)  \(.name)"'
```

Use Nur's own voice for the rest of this guide:

```bash
export VOICE=$(curl -s https://api.eesi.ai/v1/voices \
  -H "Authorization: Bearer $EESI_API_KEY" \
  | jq -r '.data[] | select(.name == "Nur") | .voice_id')
```

## 3. Make Nur speak

```bash
curl https://api.eesi.ai/v1/audio/speech \
  -H "Authorization: Bearer $EESI_API_KEY" \
  -H "Content-Type: application/json" \
  -o hello.mp3 \
  -d "{
    \"model\": \"nur-v1\",
    \"voice\": \"$VOICE\",
    \"input\": \"Hi, I'm Nur. Thanks for building with us.\"
  }"
```

Play `hello.mp3`. To speak another language, write the text in it. Add
`"language": "fr"` when you want to be explicit.

## 4. Read it back

```bash
curl https://api.eesi.ai/v1/audio/transcriptions \
  -H "Authorization: Bearer $EESI_API_KEY" \
  -F file=@hello.mp3 \
  -F model=nur-v1
```

```json
{ "text": "Hi, I'm Nur. Thanks for building with us." }
```

Ask for `response_format=verbose_json` to get speakers and timings. See
[Speech to text](/speech/speech-to-text).

## 5. Talk to Nur

Nur is a conversation over one connection: you stream audio in and Nur
streams audio back, deciding for itself when you have finished speaking. This
script sends a recorded question and saves Nur's answer.

```bash
pip install "websockets>=14"
```

```python talk.py
import asyncio, base64, json, os, wave

import websockets

URL = "wss://api.eesi.ai/v1/realtime?model=nur-v1"
KEY = os.environ["EESI_API_KEY"]
VOICE = os.environ["VOICE"]


async def main(question_wav: str) -> None:
    # The question must be 16 kHz, mono, 16-bit PCM WAV.
    with wave.open(question_wav) as clip:
        pcm = clip.readframes(clip.getnframes())

    async with websockets.connect(
        URL, additional_headers={"Authorization": f"Bearer {KEY}"}
    ) as ws:
        await ws.send(json.dumps({
            "type": "session.update",
            "session": {
                "type": "realtime",
                "instructions": "You are a friendly guide. Keep answers short.",
                "audio": {
                    "output": {
                        "voice": VOICE,
                        "format": {"type": "audio/pcm", "rate": 24000},
                    }
                },
            },
        }))

        async def speak() -> None:
            # 20 ms per message, paced like a live microphone, then silence
            # so Nur hears that you stopped.
            chunk = 640
            for start in range(0, len(pcm), chunk):
                await ws.send(json.dumps({
                    "type": "input_audio_buffer.append",
                    "audio": base64.b64encode(pcm[start:start + chunk]).decode(),
                }))
                await asyncio.sleep(0.02)
            silence = base64.b64encode(bytes(chunk)).decode()
            for _ in range(75):
                await ws.send(json.dumps({"type": "input_audio_buffer.append", "audio": silence}))
                await asyncio.sleep(0.02)

        asyncio.create_task(speak())
        reply = bytearray()
        async for raw in ws:
            event = json.loads(raw)
            if event["type"] == "response.output_audio.delta":
                reply += base64.b64decode(event["delta"])
            elif event["type"] == "response.output_audio_transcript.delta":
                print(event["delta"], end="", flush=True)
            elif event["type"] == "response.done" and reply:
                break
            elif event["type"] == "error":
                print("\nerror:", event["error"]["message"])

    with wave.open("reply.wav", "wb") as out:
        out.setnchannels(1)
        out.setsampwidth(2)
        out.setframerate(24000)
        out.writeframes(bytes(reply))
    print("\nSaved reply.wav")


asyncio.run(main("question.wav"))
```

```bash
python talk.py
```

A live app sends microphone audio the same way and plays each delta as it
arrives. In a browser, use WebRTC instead: [Nur](/realtime/live) shows
both.

## Next

<CardGroup cols={2}>
  <Card title="Nur" icon="bolt" href="/realtime/live">
    Sessions, voices, interruptions and the events you will use.
  </Card>
  <Card title="Live translation" icon="language" href="/realtime/translate">
    The same connection, interpreting instead of conversing.
  </Card>
  <Card title="Voices" icon="microphone" href="/speech/voices">
    Clone a voice from a short clip.
  </Card>
  <Card title="OpenAI compatibility" icon="arrows-rotate" href="/develop/openai-compatibility">
    Point an OpenAI client at EESI.
  </Card>
</CardGroup>
