kimik3.io/API guide
Kimi K3 API
Model ID, auth, a request that works, and how to tell a real success from a silent failure. Every response body on this page came off the wire on 2026-07-16.
K3 is OpenAI-format compatible. Any OpenAI SDK works — you point base_url at an endpoint that carries kimi-k3 and set the model string. That is the whole integration.
Quickstart
This calls K3 through EvoLink, which carries kimi-k3 on an OpenAI-compatible endpoint. Swap the base_url for any other gateway and the rest is unchanged.
from openai import OpenAI
client = OpenAI(
api_key="YOUR_EVOLINK_API_KEY",
base_url="https://direct.evolink.ai/v1", # <-- the one line you change
)
response = client.chat.completions.create(
model="kimi-k3",
messages=[
{"role": "user", "content": "Explain prompt caching in two sentences."},
],
)
print(response.choices[0].message.content)
print(response.usage)
curl https://direct.evolink.ai/v1/chat/completions \
-H "Authorization: Bearer $EVOLINK_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "kimi-k3",
"messages": [
{"role": "user", "content": "Explain prompt caching in two sentences."}
]
}'
Auth is a bearer token: Authorization: Bearer <key>. Keep it in an environment variable, never in source. The endpoint path is /v1/chat/completions — if you are using an OpenAI SDK, give it the /v1 root and let the SDK append the rest.
The key: two honest ways to get one
The code above needs an API key, and you have two legitimate options. Three things are identical either way — the rates ($3.00 / $0.30 / $15.00 per 1M, date-stamped), the model's behaviour (cache blocks, reasoning billing — it's the same model), and your code (OpenAI format both ways; switching later is one base_url). What differs is everything around the call:
| Moonshot direct | Via EvoLink | |
|---|---|---|
| Getting started | A separate Moonshot platform account | One account, 10 free credits, sign up from anywhere in minutes |
| Models on the key | The Kimi family | GPT, Claude, Gemini, K3, and dozens of the world's mainstream models — one key, one endpoint |
| Billing operations | Another balance to fund, watch, and reconcile — per provider you add | One balance and one statement across every model you call |
| Multi-model & agent work | Model routing, fallbacks, and A/B evals mean juggling one account per provider | Routing, fallback, and model comparison are a string change on the same key |
When is direct the right call? If you will only ever use Kimi models and want no intermediary in the path, go direct — the code on this page works there too, with Moonshot's base_url. For everyone else, the gateway column is why this site's examples default to it.
Get an EvoLink API key — 10 free credits
Sign-up drops you into the dashboard with onboarding. Already have an account? Grab a key from your dashboard →
What a real response looks like
Trimmed, but otherwise exactly what came back:
{
"id": "chatcmpl-6a593017ec44f116fb614895",
"object": "chat.completion",
"created": 1784229923,
"model": "kimi-k3",
"choices": [{
"index": 0,
"message": {
"role": "assistant",
"content": "OK",
"reasoning_content": "The user is asking me to reply with exactly \"OK\". This is a simple request with no complications..."
},
"finish_reason": "stop"
}],
"usage": {
"prompt_tokens": 90,
"completion_tokens": 47,
"total_tokens": 137,
"completion_tokens_details": {"reasoning_tokens": 31},
"prompt_tokens_details": {"cached_tokens": 90}
}
}
Two fields deserve attention because they have no OpenAI equivalent:
message.reasoning_content— K3's thinking, returned alongside the answer. Readcontentfor the answer. Reasoning is not a mode you enable on K3; it is always on.usage.completion_tokens_details.reasoning_tokens— how many output tokens went to thinking. Above, 31 of 47 tokens for a two-letter reply. That is billed at the output rate.
Confirm the call actually worked
An HTTP 200 is not proof of success on K3. Check four things:
modelechoes backkimi-k3— not a fallback the gateway silently substituted.finish_reasonisstop, notlength—lengthwith empty content is the signature of the reasoning-budget trap.contentis a real string, not"".usage.total_tokensis non-zero — and tells you what you spent.
In code, the one assertion worth writing:
choice = response.choices[0]
if choice.finish_reason == "length" and not choice.message.content:
raise RuntimeError(
f"Reasoning consumed the whole budget "
f"({response.usage.completion_tokens_details.reasoning_tokens} reasoning tokens). "
f"Raise max_completion_tokens."
)
Parameters that matter
| Parameter | Default | What you need to know |
|---|---|---|
model | — | Exactly kimi-k3. Not kimi-k3-chat, not moonshot-k3. |
max_completion_tokens | 131,072 | Leave it alone unless you know what you're doing. Reasoning draws from this budget; cap it low and you get billed for an empty string. The full story. |
stream | false | Strongly recommended at long context — we measured 52s on a 498k-token prompt. |
stream_options | — | Set {"include_usage": true} or you get no usage block while streaming, and you are blind to spend. |
tools | — | Function calling, max 128 tools, JSON Schema format. |
tool_choice | auto |
auto · none · required · or a named function. |
response_format | {"type":"text"} |
Supports json_object and json_schema for structured output. |
prompt_cache_key | null | We could not measure any effect for prefix reuse — caching already happens automatically. What we tested. |
reasoning_effort | — | Only max is supported — but passing an unsupported value returns HTTP 200, not an error. Do not trust it to validate. |
Full parameter and response-schema reference: EvoLink's kimi-k3 API docs.
Streaming
Server-sent events, terminated by data: [DONE]. The wrinkle specific to K3: the first tokens you receive are reasoning, not your answer. We measured a 2.8s median to first token — and in three of five runs, no content token ever arrived because a 128-token ceiling was consumed by thinking.
stream = client.chat.completions.create(
model="kimi-k3",
messages=[{"role": "user", "content": "Count to three."}],
stream=True,
stream_options={"include_usage": True}, # or you get no usage at all
)
for chunk in stream:
if not chunk.choices:
continue
delta = chunk.choices[0].delta
if getattr(delta, "reasoning_content", None):
pass # thinking — usually not what you render
if delta.content:
print(delta.content, end="", flush=True)
If you render reasoning_content to users, know that you are showing them the model's scratchpad. Most products want to show a spinner during reasoning and stream only content.
API FAQ
What is the Kimi K3 model ID?
Exactly kimi-k3 — not kimi-k3-chat, not moonshot-k3. The response echoes it back in the model field, which is worth asserting on.
Is Kimi K3 OpenAI-compatible?
Yes. K3 is served over the standard /v1/chat/completions shape, so any OpenAI SDK works unchanged — you set base_url and the model string. The two K3-specific additions are reasoning_content in the message and reasoning_tokens in usage.
Do I need a Moonshot account to use the Kimi K3 API?
No. Any endpoint that carries kimi-k3 works — through EvoLink, one account with 10 free credits reaches K3 alongside GPT, Claude, Gemini, and dozens of the world's mainstream models, at the same rates as Moonshot direct.
What base_url do I use for Kimi K3?
Through EvoLink: https://direct.evolink.ai/v1. Direct to Moonshot: https://api.moonshot.ai/v1. The rest of your code is identical either way.
Get a key and run the code above
EvoLink carries kimi-k3 on an OpenAI-compatible endpoint — one key reaches GPT, Claude, Gemini, and dozens of the world's mainstream models. 10 free credits, sign up from anywhere — no Chinese phone number.