---
title: Quickstart
description: Three minutes from the browser to a model’s answer — a key in the console, a base URL in your client, a first call. The same thing in detail below.
keywords: quickstart, start, key, first request, base url, streaming
group: get-started
---

## In three minutes {#quick keywords="fast, three minutes, first call, base url"}

:::steps
- **Create a key** — In the console, on the keys screen. The secret is shown once, so copy it right away.
- **Top up the wallet** — A new wallet is created empty; without a top-up the first call has nothing to pay with.
- **Set the base URL** — Point any OpenAI-compatible client at `https://api.kumorouter.com/v1` and hand it the key.
- **Make the call** — Name a model and an output ceiling. The answer arrives in the shape your client already parses.
:::

:::note
A new wallet is empty, and a call with nothing to pay with answers `402` — top up the balance on the [billing page →](/en/billing).
:::

:::code-group
```bash title=curl
curl https://api.kumorouter.com/v1/chat/completions \
  -H "Authorization: Bearer $KUMO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "<model>",
    "max_tokens": 128,
    "messages": [{ "role": "user", "content": "Explain tokens in one line." }]
  }'
```
```python title=Python
import os
from openai import OpenAI

client = OpenAI(
    base_url="https://api.kumorouter.com/v1",
    api_key=os.environ["KUMO_API_KEY"],
)

answer = client.chat.completions.create(
    model="<model>",
    max_tokens=128,
    messages=[{"role": "user", "content": "Explain tokens in one line."}],
)
print(answer.choices[0].message.content)
```
```javascript title=Node
import OpenAI from "openai";

const client = new OpenAI({
  baseURL: "https://api.kumorouter.com/v1",
  apiKey: process.env.KUMO_API_KEY,
});

const answer = await client.chat.completions.create({
  model: "<model>",
  max_tokens: 128,
  messages: [{ role: "user", content: "Explain tokens in one line." }],
});

const reply = answer.choices[0].message.content;
process.stdout.write((reply ?? "") + "\n");
```
:::

:::tip
Pick a model in the list above the examples: its identifier is substituted into every example on this page, so the code copies out ready to run.
:::

> [The model catalog →](/en/models) [How the header is written →](/en/authentication)

## Create an account {#create-account keywords="account, sign up, register"}

Everything in this section happens inside the console. An account needs nothing but an email address and a password; there is nothing to install and no card to hand over before you have a key in your hand.

:::steps
- **Open the console** — It runs on a hostname of its own, and every account operation lives there.
- **Create the account** — An address, a password of at least **6 characters** (the ceiling is 256 **bytes** of UTF-8), and the two consents the form asks for. The floor counts characters, so six letters are six letters in any alphabet.
- **You are already inside** — The same answer that creates the account mints the session, so the console sets its own cookie and lands you on the overview with nothing to sign in to and nothing to copy anywhere.
:::

A verification email normally follows on its own. Its link confirms that the address is yours, and today that is all it does: nothing on the way to a key or a first call waits on it — which is also why an email that never arrives is not a dead end. Sending it is best-effort and can be skipped under load or fail quietly, and registration succeeds either way.

## Mint a key {#create-key keywords="key, console, secret, reveal"}

A key is created on the keys screen of the console. The secret is shown **once**, on the screen that creates it, and never again: the platform keeps only a hash of it together with the prefix and the tail that name it in a list.

- Copy the secret into your secret store before you leave the screen that created it.
- Keep it in an environment variable, never in the source tree.
- A lost key is not recovered — it is replaced by a new one.

> [Open the console →](https://console.kumorouter.com/) [What the platform records about a key →](/en/authentication)

## Top up the wallet {#top-up keywords="wallet, balance, top-up, 402"}

The wallet is created together with the organization and starts empty. Top it up from the console — by card, SBP or crypto, depending on what the payment provider offers; there is no minimum amount.

> [How to top up the wallet →](/en/billing)

## Make the call {#first-call keywords="request, chat completions, curl, base url, max_tokens"}

Point any OpenAI-compatible client at `https://api.kumorouter.com/v1` and give it the key. That is the whole migration: the base URL and the key change, and the rest of your code does not.

The model is named by its canonical identifier or by an alias the published catalog carries. `<model>` in the examples stands in for one — ask the gateway for the list with `curl https://api.kumorouter.com/v1/models`, read it on the [models page](/en/models), or check the rates on the [price list](https://kumorouter.com/en/pricing).

An output ceiling is required. `max_tokens` (or `max_completion_tokens`, which means the same thing) is what lets the platform reserve for the call before it goes upstream; a request without one is refused rather than left unbounded.

## What comes back {#response keywords="response, choices, usage, finish_reason"}

The answer is this protocol's own, not a translation of another: the choices, the finish reason and the token counts arrive in the shape the client already parses.

```json title=Response
{
  "id": "chatcmpl-8f2b7e10c9",
  "object": "chat.completion",
  "model": "<model>",
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": "Tokens are the small chunks of text a model reads and writes."
      },
      "finish_reason": "stop"
    }
  ],
  "usage": {
    "prompt_tokens": 12,
    "completion_tokens": 18,
    "total_tokens": 30
  }
}
```

> [Every member of the operation →](/en/chat-completions) [What a refusal looks like →](/en/errors)

## Stream it {#stream keywords="streaming, sse, server-sent events, chunks"}

Add `"stream": true` and the same call is answered as `text/event-stream`: `chat.completion.chunk` events in arrival order, then the terminal `[DONE]` frame. Ask for the token counts with `stream_options` and a usage chunk arrives before that frame.

A refusal decided before the first event is answered as an ordinary JSON error, exactly as a unary call would be. A failure after the stream has started ends it **without** the terminal frame — which is how a client tells a finished answer from a truncated one, so treat a stream that stops before `[DONE]` as a failed call rather than a short one.

```bash title=curl
curl -N https://api.kumorouter.com/v1/chat/completions \
  -H "Authorization: Bearer $KUMO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "<model>",
    "max_tokens": 128,
    "stream": true,
    "stream_options": { "include_usage": true },
    "messages": [{ "role": "user", "content": "Write a haiku about latency." }]
  }'
```

> [The stream in full, event by event →](/en/streaming)

## Prove the key works {#verify-key keywords="verify, check, identity"}

Before you wire the key into anything, ask the platform what it thinks of it. The identity echo is the one operation that answers with the presenting key's own identity — its name, what funds it, when it expires. It says "yes, this key is live, and here is what it may do" without you writing a line of code.

Every refusal of a presentation is the same 401. A missing header, a malformed key, an unknown key, a revoked key and an expired key are answered alike, and the answer does not say which of them it was. Which of your own keys is live is a question for the console, not for the gateway.

> [Check your key →](/en/key-check)

## What next {#next keywords="next, catalog, integrations, errors"}

:::cards
- [Models](/en/models) — the platform's catalog: what is available, what it can do, and how to name it in a request.
- [Integrations](/en/integrations) — ready settings for agents, editors and SDKs.
- [Chat Completions](/en/chat-completions) — the operation of that first call, member by member.
- [Errors](/en/errors) — every status the gateway answers with, and what to do about it.
:::
