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

# Quickstart

> Get an API key, make a chat completion, and stream the response

<Steps>
  <Step title="Get an API key">
    Create a key at [withhopper.com/console/keys](https://withhopper.com/console/keys). The full `sk_hopper_...` key is shown once, at creation; the console keeps only the prefix. New accounts start with **\$5** in credits.
  </Step>

  <Step title="Make a chat completion">
    Any OpenAI-compatible client works. Set the base URL and pass your key.

    <CodeGroup>
      ```python Python theme={null}
      from openai import OpenAI

      client = OpenAI(
          base_url="https://api.withhopper.com/v1",
          api_key="sk_hopper_...",
      )

      response = client.chat.completions.create(
          model="gemma-4-31b",
          messages=[{"role": "user", "content": "Say hello in one sentence."}],
      )
      print(response.choices[0].message.content)
      ```

      ```typescript TypeScript theme={null}
      import OpenAI from "openai";

      const client = new OpenAI({
        baseURL: "https://api.withhopper.com/v1",
        apiKey: "sk_hopper_...",
      });

      const response = await client.chat.completions.create({
        model: "gemma-4-31b",
        messages: [{ role: "user", content: "Say hello in one sentence." }],
      });
      console.log(response.choices[0].message.content);
      ```

      ```bash cURL theme={null}
      curl https://api.withhopper.com/v1/chat/completions \
        -H "Authorization: Bearer sk_hopper_..." \
        -H "Content-Type: application/json" \
        -d '{
          "model": "gemma-4-31b",
          "messages": [{"role": "user", "content": "Say hello in one sentence."}]
        }'
      ```
    </CodeGroup>

    The response is a standard chat completion object. Two headers report spend:

    ```text theme={null}
    x-hopper-request-cost-usd: 0.000123
    x-hopper-credits-remaining-usd: 4.999877
    ```
  </Step>

  <Step title="Stream the response">
    Set `stream: true`. Tokens arrive as SSE chunks in the OpenAI chunk format.

    <CodeGroup>
      ```python Python theme={null}
      stream = client.chat.completions.create(
          model="gemma-4-31b",
          messages=[{"role": "user", "content": "Count to five."}],
          stream=True,
      )
      for chunk in stream:
          delta = chunk.choices[0].delta.content
          if delta:
              print(delta, end="", flush=True)
      ```

      ```typescript TypeScript theme={null}
      const stream = await client.chat.completions.create({
        model: "gemma-4-31b",
        messages: [{ role: "user", content: "Count to five." }],
        stream: true,
      });
      for await (const chunk of stream) {
        process.stdout.write(chunk.choices[0]?.delta?.content ?? "");
      }
      ```

      ```bash cURL theme={null}
      curl -N https://api.withhopper.com/v1/chat/completions \
        -H "Authorization: Bearer sk_hopper_..." \
        -H "Content-Type: application/json" \
        -d '{
          "model": "gemma-4-31b",
          "messages": [{"role": "user", "content": "Count to five."}],
          "stream": true
        }'
      ```
    </CodeGroup>

    Streaming responses carry only `x-hopper-credits-remaining-usd`, set to the pre-request balance; the cost of a stream is not known until it ends. Non-streaming responses carry both headers.
  </Step>

  <Step title="For voice agents: hopper-client">
    `hopper-client` is a drop-in wrapper around the OpenAI SDK for turn-taking workloads: it holds one warm HTTP/2 connection per process with a 300 s keepalive, so the connection survives conversational gaps instead of re-handshaking on the next turn. Measurements are in [the blog post](https://withhopper.com/blog/llm-client-voice-agents).

    ```bash theme={null}
    pip install hopper-client
    ```

    ```python theme={null}
    from hopper import AsyncHopper

    # initialize once, at startup — reused for every turn
    client = AsyncHopper(
        base_url="https://api.withhopper.com/v1",
        api_key="sk_hopper_...",
    )
    ```
  </Step>

  <Step title="Next steps">
    * [Models](/models) — the catalog: `gemma-4-31b`, `omnivoice`, `nemotron-asr`
    * [Generate speech](/tts/generate-speech) — 24 kHz streaming TTS
    * [Transcription](/stt/transcription) — batch and streaming speech to text
    * [Pricing](/platform/pricing) — \$1 per million input tokens and \$3 per million output tokens for the LLM; per-character TTS and per-audio-hour STT
  </Step>
</Steps>
