Kimi K3 API Quickstart: Python, Streaming, Vision, and Tool Calls

Learn how to call the Kimi K3 API with Python and cURL, including reasoning effort, streaming, vision input, structured output, tool calls, and 1M-context limits.

Kimi K3 is Kimi’s flagship model for coding, knowledge work, and complex reasoning. It uses Kimi Delta Attention (KDA), Attention Residuals, and a MoE architecture, has 2.8 trillion parameters, and provides native visual understanding with a 1-million-token context window.

Based on the official Kimi K3 Quickstart, this guide starts with an API key and a minimal request, then provides runnable examples for Python, cURL, streaming, vision input, structured output, and tool calls. It also highlights parameter differences that commonly cause problems when migrating from K2.x.

Quick Answer

Prepare Python 3.9+ and a Kimi API key, install the OpenAI SDK, store the key in MOONSHOT_API_KEY, point the OpenAI client’s base_url to https://api.moonshot.ai/v1, and use kimi-k3 as the model name.

1
python3 -m pip install --upgrade 'openai>=1.0'
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
import os

from openai import OpenAI

client = OpenAI(
    api_key=os.environ["MOONSHOT_API_KEY"],
    base_url="https://api.moonshot.ai/v1",
)

completion = client.chat.completions.create(
    model="kimi-k3",
    messages=[{"role": "user", "content": "用一句话介绍 Kimi K3。"}],
)

print(completion.choices[0].message.content)

K3 always has thinking mode enabled. To set reasoning effort explicitly, use the top-level field reasoning_effort="max"; do not reuse the K2.x thinking parameter.

Prepare the API key and environment variable

First obtain an API key from Kimi API Platform. Do not hard-code the key in a script or commit it to Git. Pass it through an environment variable instead.

Windows PowerShell

1
2
$env:MOONSHOT_API_KEY = "你的 API Key"
python .\kimi_k3_demo.py

To save it permanently for the current Windows user, run:

1
[Environment]::SetEnvironmentVariable("MOONSHOT_API_KEY", "你的 API Key", "User")

Open a new terminal after saving it.

macOS and Linux

1
2
export MOONSHOT_API_KEY="你的 API Key"
python3 kimi_k3_demo.py

Make a minimal request with cURL

If you do not want to create a Python project yet, call the OpenAI-compatible Chat Completions endpoint directly:

1
2
3
4
5
6
7
curl https://api.moonshot.ai/v1/chat/completions \
  --header "Authorization: Bearer $MOONSHOT_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{
    "model": "kimi-k3",
    "messages": [{"role": "user", "content": "用一句话介绍 Kimi K3。"}]
  }'

In PowerShell, use Invoke-RestMethod instead of copying Bash backslash line continuations:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
$headers = @{
    Authorization = "Bearer $env:MOONSHOT_API_KEY"
    "Content-Type" = "application/json"
}

$body = @{
    model = "kimi-k3"
    messages = @(
        @{ role = "user"; content = "用一句话介绍 Kimi K3。" }
    )
} | ConvertTo-Json -Depth 5

Invoke-RestMethod `
    -Uri "https://api.moonshot.ai/v1/chat/completions" `
    -Method Post `
    -Headers $headers `
    -Body $body

Configure K3 reasoning effort

K3 currently supports only max, which is also the default. More levels are planned, so you can omit the field for now. To make the configuration explicit, use:

1
2
3
4
5
6
7
8
9
completion = client.chat.completions.create(
    model="kimi-k3",
    reasoning_effort="max",
    messages=[
        {"role": "user", "content": "证明根号 2 是无理数。"}
    ],
)

print(completion.choices[0].message.content)

For multi-turn conversations and tool calls, append the complete assistant message returned by the API to the next request. Do not keep only content; the full message may contain reasoning and tool-call information required to preserve context.

Handle streamed reasoning and final output separately

Streaming responses put reasoning in reasoning_content and the final answer in content. Your application can process the two delta streams separately:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
stream = client.chat.completions.create(
    model="kimi-k3",
    messages=[{"role": "user", "content": "解释天空为什么是蓝色的。"}],
    stream=True,
)

for chunk in stream:
    delta = chunk.choices[0].delta
    reasoning = getattr(delta, "reasoning_content", None)
    if reasoning:
        print(reasoning, end="", flush=True)
    if delta.content:
        print(delta.content, end="", flush=True)

In production, store or display reasoning_content separately from the user-facing final answer so the UI does not mix the two streams.

Send a local image

For vision messages, content must be an array of objects, not a serialized string. K3 does not support public image URLs, so encode a local image as a Base64 data URL:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import base64
from pathlib import Path

image_data = base64.b64encode(Path("image.png").read_bytes()).decode()

completion = client.chat.completions.create(
    model="kimi-k3",
    messages=[
        {
            "role": "user",
            "content": [
                {
                    "type": "image_url",
                    "image_url": {
                        "url": f"data:image/png;base64,{image_data}"
                    },
                },
                {"type": "text", "text": "描述这张图片。"},
            ],
        }
    ],
)

print(completion.choices[0].message.content)

Upload videos through the Files API and reference them with ms://<file-id>. Delete temporary files when the task finishes to avoid retaining unused data.

Produce strict JSON Schema output

When downstream code needs a predictable response, constrain the final output with json_schema and strict: true. Parse only message.content, not reasoning_content.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import json

completion = client.chat.completions.create(
    model="kimi-k3",
    messages=[
        {"role": "user", "content": "小林今年 28 岁,提取姓名和年龄。"}
    ],
    response_format={
        "type": "json_schema",
        "json_schema": {
            "name": "person",
            "strict": True,
            "schema": {
                "type": "object",
                "properties": {
                    "name": {"type": "string"},
                    "age": {"type": "integer"},
                },
                "required": ["name", "age"],
                "additionalProperties": False,
            },
        },
    },
)

person = json.loads(completion.choices[0].message.content or "{}")
print(person)

additionalProperties: false prevents the model from adding fields outside the schema, which is useful for extraction, form filling, and downstream automation.

Implement a minimal tool-calling loop

Set tool_choice="required" on the first turn to require at least one tool call. After executing the tool, preserve the complete assistant message and append one tool message with the matching tool_call_id for every call.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
import json
from typing import Any

tools: list[dict[str, Any]] = [
    {
        "type": "function",
        "function": {
            "name": "get_weather",
            "description": "查询城市天气",
            "parameters": {
                "type": "object",
                "properties": {"city": {"type": "string"}},
                "required": ["city"],
            },
        },
    }
]

messages: list[Any] = [
    {"role": "user", "content": "今天上海天气如何?"}
]

first = client.chat.completions.create(
    model="kimi-k3",
    messages=messages,
    tools=tools,
    tool_choice="required",
)

assistant_message = first.choices[0].message
messages.append(assistant_message)

for tool_call in assistant_message.tool_calls or []:
    arguments = json.loads(tool_call.function.arguments)
    result = json.dumps(
        {"city": arguments["city"], "weather": "sunny", "temperature_c": 32},
        ensure_ascii=False,
    )
    messages.append(
        {
            "role": "tool",
            "tool_call_id": tool_call.id,
            "content": result,
        }
    )

final = client.chat.completions.create(
    model="kimi-k3",
    messages=messages,
    tools=tools,
)

print(final.choices[0].message.content)

In a real project, validate tool arguments, limit accessible resources, set timeouts, and require human confirmation before high-risk actions such as writing, deleting, or sending data.

Load tools dynamically

If you have many tools, inject a complete tool definition at the point where it is needed through a system message without content. The definition must include name, description, and parameters, and it must remain in later request history because the server does not retain it for the application.

This works well for large agents: begin with a small set of basic tools, then load database, browser, or business-system tools after the task direction is clear, reducing the initial tool description payload.

Use the 1M context and automatic caching

Regular model requests automatically attempt context caching. No cache ID, TTL, or extra parameter is required. To improve the chance of a cache hit, put long stable content near the beginning and keep that prefix unchanged in later requests.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
from pathlib import Path

knowledge = Path("knowledge-base.md").read_text(encoding="utf-8")

for question in ["总结主要结论。", "列出三个实施风险。"]:
    completion = client.chat.completions.create(
        model="kimi-k3",
        messages=[
            {"role": "system", "content": knowledge},
            {"role": "user", "content": question},
        ],
    )
    print(completion.choices[0].message.content)

A 1-million-token limit is capacity, not a reason to fill every request. File filtering, chunk retrieval, and result deduplication usually reduce latency and cost while preventing irrelevant content from distracting the model.

Current limits and migration notes

Check these limits before integration:

  • K3 always reasons, and reasoning_effort currently supports only max;
  • max_completion_tokens defaults to 131072 and can be set up to 1048576;
  • temperature=1.0, top_p=0.95, n=1, presence_penalty=0, and frequency_penalty=0 are fixed, so omit them from requests;
  • multi-turn conversations and tool calls must return the complete assistant message unchanged;
  • vision input does not support public image URLs; use Base64 or ms://<file-id>;
  • Kimi’s official web search tool is being updated and is not currently recommended for near-term production workflows.

When migrating a K2.x project, first search for thinking, sampling parameters, and message-handling code that stores only content. Then add tests for streamed reasoning_content, the vision-array format, and tool-call round trips.

FAQ

Can Kimi K3 use the OpenAI SDK directly?

Yes. Install openai>=1.0, set base_url to https://api.moonshot.ai/v1, and pass the key through MOONSHOT_API_KEY.

Why does the request fail after setting temperature?

K3 uses fixed values for temperature, top_p, and other sampling parameters. The official guidance is to omit them rather than copy a generic configuration from another OpenAI-compatible model.

Can I send an HTTP image URL directly?

No. Encode a local image as a Base64 data URL, or upload the file and use ms://<file-id>.

Must I enable caching manually for the 1M context?

No. Regular model requests automatically attempt caching. Keeping a long prefix exactly unchanged helps later requests hit the cache.

How is Kimi K3 billed?

Kimi uses pay-as-you-go pricing without context-length tiers for the 1-million-token window. Input pricing distinguishes cache hits from misses, and output is billed per token. Prices may change, so check the Kimi K3 Pricing page before use.

记录并分享
Built with Hugo
Theme Stack designed by Jimmy