# How to Connect Kimi K3.1 Pro via API: Keys, Pricing and OpenAI-Compatible Setup

> AI Release · @ai_release1 · https://ai-release.org/guides/kak-podklyuchit-kimi-k3-1-pro-cherez-api-klyuchi-tseny_en.html

## TL;DR

- **Time to complete:** 20 minutes
- **Difficulty:** Intermediate
- **You will need:**
  - Python 3.10+ with the `openai` SDK 1.x, or `curl` 7.68+
  - An account on the Kimi API platform and an API key created in its console
  - Any OpenAI-compatible client (LibreChat, Open WebUI, n8n, `curl`, the `openai` SDK)
  - The exact **model ID** and **base URL** from the provider's model list — this guide uses placeholders (`<model-id>`, `<provider-domain>`) because those strings change between releases
- **Pricing:** token rates are **not** quoted here. Read the current per-million-token price from the provider's pricing page before you budget anything — rates differ per model tier and change without notice.

## Requirements and setup
![Illustration: Requirements and setup](https://ai-release.org/guides/img/kak-podklyuchit-kimi-k3-1-pro-cherez-api-klyuchi-tseny-en-1.jpg?v=1792831838)


- **OS:** Linux, macOS, or Windows 10/11 with WSL2. Commands below are bash; PowerShell equivalents are noted where they differ.
- **Python:** 3.10 or newer. Check with `python3 --version`; expected output is `Python 3.10.x` or higher.
- **curl:** 7.68 or newer. Check with `curl --version`; the first line prints `curl 8.x.x (x86_64-pc-linux-gnu)`.
- **Python SDK:** install with `pip install --upgrade "openai>=1.0"`. Expected last line: `Successfully installed openai-1.x.x`.
- **API key:** generated in the provider console (section "API Keys"). It is shown once — store it immediately.
- **Disk/network:** no local model weights are downloaded; the client only needs outbound HTTPS (port 443) to the API host.

Checklist before you start:

- [ ] `python3 --version` prints 3.10+
- [ ] `curl --version` prints 7.68+
- [ ] `pip show openai` prints a version `>= 1.0`
- [ ] You have the API key string
- [ ] You can reach the API host: `curl -sS -o /dev/null -w "%{http_code}\n" https://api.<provider-domain>/v1/models` (401 is a valid answer here — it proves DNS and TLS work)

`<provider-domain>` is the hostname from the provider's API documentation, for example the host shown next to the "OpenAI-compatible endpoint" section. Replace it everywhere it appears.

## Step 1. Create an API key and export it as an environment variable

**What to do:** Sign in to the provider console, open **API Keys → Create new secret key**, name it `kimi-k3-prod`, and copy the value. Then export it in your shell instead of pasting it into source files:

```bash
export KIMI_API_KEY="<paste-key-here>"
mkdir -p ~/.config/kimi && printf 'KIMI_API_KEY=%s\n' "$KIMI_API_KEY" > ~/.config/kimi/.env
chmod 600 ~/.config/kimi/.env
printf '%s\n' ".env" "*.key" >> .gitignore
```

On PowerShell: `$env:KIMI_API_KEY = "<paste-key-here>"`.

**Why:** Every request is authenticated with a bearer token — an HTTP header `Authorization: Bearer <key>` sent with each call. Keeping the key in an environment variable means it never lands in a commit, and rotating it later touches one file.

**How to check:** Confirm the variable exists without printing the whole secret, then ask the API which models your key can see:

```bash
printf '%s\n' "${KIMI_API_KEY:0:6}…"   # prints e.g. sk-abc…
curl -s -H "Authorization: Bearer $KIMI_API_KEY" \
  https://api.<provider-domain>/v1/models | head -c 400
```

Expected output is JSON beginning with `{"object":"list","data":[{"id":"...` — an array of model objects. Copy the `id` of the K3.1 Pro entry; that exact string is your `<model-id>` for the rest of this guide.

## Step 2. Point an OpenAI-compatible client at the Kimi endpoint
![Illustration: Step 2. Point an OpenAI-compatible client at the Kimi endpoint](https://ai-release.org/guides/img/kak-podklyuchit-kimi-k3-1-pro-cherez-api-klyuchi-tseny-en-2.jpg?v=1792831844)


**What to do:** An OpenAI-compatible API means the request and response bodies follow OpenAI's wire format (`POST /v1/chat/completions`, bearer auth, `choices[].message.content`). So you keep your client and change only two values:

| Client setting | Value | Where to find it |
|---|---|---|
| `base_url` | `https://api.<provider-domain>/v1` | provider docs, "API endpoint" |
| `api_key` | value of `KIMI_API_KEY` | your console |
| `model` | `<model-id>` | the `/v1/models` reply from Step 1 |

```python
import os
from openai import OpenAI

client = OpenAI(
    api_key=os.environ["KIMI_API_KEY"],
    base_url="https://api.<provider-domain>/v1",
)

resp = client.chat.completions.create(
    model="<model-id>",              # exact id from /v1/models, e.g. what the console lists
    messages=[
        {"role": "system", "content": "Answer in one sentence."},
        {"role": "user", "content": "What does an OpenAI-compatible endpoint change in my code?"},
    ],
    temperature=0.3,
    max_tokens=200,
)
print(resp.choices[0].message.content)
print(resp.usage)
```

For GUI clients that take a JSON config rather than code (LibreChat, Open WebUI, n8n's OpenAI node), use the same three fields:

```json
{
  "provider": "openai-compatible",
  "baseURL": "https://api.<provider-domain>/v1",
  "apiKey": "${KIMI_API_KEY}",
  "model": "<model-id>",
  "stream": true
}
```

**Why:** Your SDK or tool already speaks that format, so the only difference between calling OpenAI and calling Kimi is the host in `base_url` and the model string. If you later want to keep a second provider in the same client, put a router in front instead of hardcoding hosts — see [How to combine multiple AI providers behind one OpenAI-compatible router](https://ai-release.org/guides/kak-obedinit-neskolko-ii-provayderov-v-odin-api-nastroy.html?utm_source=tg&utm_medium=channel&utm_campaign=guide_inline&utm_content=guide_to_guide). The same swap-the-base-URL pattern applied to a self-hosted server is documented in [How to connect a local LLM to Claude Code: llama-server and an OpenAI-compatible API](https://ai-release.org/guides/kak-podklyuchit-lokalnuyu-model-k-claude-code-llama-ser.html?utm_source=tg&utm_medium=channel&utm_campaign=guide_inline&utm_content=guide_to_guide).

**How to check:** Save the snippet as `kimi_test.py` and run `python3 kimi_test.py`. You should see one sentence of plain text, then a usage object of the form `CompletionUsage(completion_tokens=42, prompt_tokens=38, total_tokens=80)`. If the token counts are non-zero, the request reached the model and was billed.

## Step 3. Verify the connection end to end
![Illustration: Step 3. Verify the connection end to end](https://ai-release.org/guides/img/kak-podklyuchit-kimi-k3-1-pro-cherez-api-klyuchi-tseny-en-3.jpg?v=1792831850)


**What to do:** Test the raw HTTP layer, then streaming, then the client. Start with `curl` so you can see the untouched response:

```bash
curl -s https://api.<provider-domain>/v1/chat/completions \
  -H "Authorization: Bearer $KIMI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"<model-id>","messages":[{"role":"user","content":"Reply with the word OK"}],"max_tokens":10}'
```

**Why:** The HTTP layer tells you which side is broken. A raw `curl` that returns `"object":"chat.completion"` proves the key, host, and model ID are all correct, so any later failure is in your client configuration, not in the account.

Then check streaming, which is what interactive UIs use:

```bash
curl -N -s https://api.<provider-domain>/v1/chat/completions \
  -H "Authorization: Bearer $KIMI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"<model-id>","messages":[{"role":"user","content":"Count to five"}],"stream":true}'
```

**How to check:** Success looks like this, in this order:

- Raw call: JSON containing `"id":`, `"object":"chat.completion"`, `"choices":[{"index":0,"message":{"role":"assistant","content":"OK"}}]`, and a `"usage"` block with non-zero `prompt_tokens` and `completion_tokens`.
- Streaming call: a run of lines starting with `data: {"id":...`, each carrying a `delta` fragment, ending with `data: [DONE]`.
- Client: `python3 kimi_test.py` exits with code `0` (`echo $?` prints `0`).

- [ ] `/v1/models` lists your model ID
- [ ] non-streaming call returns `chat.completion`
- [ ] streaming call ends with `[DONE]`
- [ ] `usage.total_tokens > 0`

## Troubleshooting

**Problem. Every request fails immediately with 401.**
- Error: `{"error":{"message":"Invalid Authentication","type":"invalid_authentication_error","code":"invalid_api_key"}}`
- Cause: the key string carries a trailing newline, was truncated on copy, was revoked in the console, or a different shell session exported an older key. A common variant is a key pasted into a `.env` file with quotes that the client reads literally.
- Solution: re-export cleanly and re-test: `unset KIMI_API_KEY && export KIMI_API_KEY="$(tr -d '[:space:]' < ~/.config/kimi/.env | cut -d= -f2-)"`, then repeat the `curl /v1/models` check. If it still returns 401, create a new key in the console and delete the old one.

**Problem. Authentication succeeds but the completion call returns 404.**
- Error: `{"error":{"message":"The model '<model-id>' does not exist or you do not have access to it","type":"invalid_request_error","code":"model_not_found"}}`
- Cause: the model string differs from the one in `/v1/models` (case, hyphenation, a date suffix), or `base_url` is wrong — either missing the `/v1` suffix or containing it twice (`.../v1/v1/chat/completions`).
- Solution: compare the string byte-for-byte with the `/v1/models` output, then print the effective URL your client builds: `python3 -c "from openai import OpenAI; import os; print(OpenAI(api_key=os.environ['KIMI_API_KEY'], base_url='https://api.<provider-domain>/v1').base_url)"`. It must end in exactly one `/v1/`.

**Problem. Requests start failing under load with 429.**
- Error: `{"error":{"message":"Rate limit reached for requests","type":"rate_limit_error","code":"rate_limit_exceeded"}}`
- Cause: you exceeded the requests-per-minute or tokens-per-minute quota of your tier, usually from a retry loop or parallel workers firing at once.
- Solution: add exponential backoff and cap concurrency — the `openai` SDK retries `429` automatically by default (`max_retries=2`); raise it with `OpenAI(..., max_retries=5, timeout=60.0)` and serialize batch jobs. Check the tier limits on the provider's rate-limit page, since the numbers are per account and not quoted here.

## Summary and next steps

You now have a working key, a verified `/v1/chat/completions` call, and a client configured with three values: base URL, key, model ID. Next, move the key into your orchestrator's secret store and add a second provider behind a router so a quota error on one does not stop your pipeline.

## FAQ

**What exactly is an "OpenAI-compatible API"?**
It is a service that accepts the same HTTP request format as OpenAI's API — same paths, same JSON fields, same bearer-token header — so existing SDKs and tools work after you change only the base URL and model name.

**Do I need a different SDK for Kimi?**
No. Use the official `openai` Python or Node SDK with `base_url` pointed at the provider's endpoint, or use `curl`. Only clients that hardcode `api.openai.com` cannot be reconfigured — most support a custom base URL field.

**How much does Kimi K3.1 Pro cost per million tokens?**
Rates are set by the provider and change over time, so check the pricing page in the console before you commit. This guide deliberately avoids quoting a number that may already be stale; watch the `usage` object in every response to measure real spend.

**Why do I get a `model_not_found` error when the model name looks right?**
Model IDs are exact strings, and some are gated by account tier or region. Copy the `id` value straight from the `/v1/models` response rather than typing it, and if the model is absent from that list, request access in the console.
