What This Tutorial Solves
Kimi K2 is useful when you want an agent-capable model that can fit into an existing application without forcing a large rewrite. The supplied context says Moonshot AI exposes Kimi K2 through an OpenAI- and Anthropic-compatible interface and encourages developers to use its tool calling API for agent applications. That matters because it lowers the cost of adoption for teams that already have chat, tool execution, or orchestration code in place.
This tutorial focuses on the practical side of Kimi K2 API integration in Python. You will see how to set up credentials, send a basic request, add function calling, handle image inputs correctly, and build error handling that fails cleanly instead of breaking your app.
One important constraint from the supplied context: vision features are not supported for Kimi K2 yet. So while this guide covers how to design for image inputs, it does not pretend that the model currently accepts them as native multimodal inputs. The right production behavior today is to detect images, route them elsewhere, or return a clear error.
Prerequisites
Before you start, make sure you have the following:
- A Moonshot AI account with access to Kimi K2 API credentials.
- A valid API key.
- Python 3.10 or newer.
- Basic familiarity with JSON, HTTP requests, and environment variables.
- A project where you can safely store secrets outside the source code.
A minimal local setup usually looks like this:
python3 -m venv .venv
source .venv/bin/activate
pip install requests python-dotenvCreate a .env file for local development:
KIMI_API_KEY=your_api_key_here
KIMI_BASE_URL=your_moonshot_api_base_url_here
KIMI_MODEL=kimi-k2Keep the base URL and model name aligned with the Moonshot documentation or console settings you were given. The supplied context confirms that the platform supports an OpenAI/Anthropic-compatible interface, but it does not provide a single universal endpoint string here, so treat the base URL as an account-specific configuration value.
Step 1: Store Authentication Safely
The supplied context shows two authentication styles:
Authorization: Bearer YOUR_API_KEYX-API-Key: YOUR_API_KEYfor Anthropic-compatible endpoints
Do not hardcode either value into your application. Load it from the environment instead.
import os
from dotenv import load_dotenv
load_dotenv()
API_KEY = os.environ["KIMI_API_KEY"]
BASE_URL = os.environ["KIMI_BASE_URL"]
MODEL = os.environ.get("KIMI_MODEL", "kimi-k2")If you deploy to a server, move those values into your secret manager or platform environment variables rather than a checked-in .env file.
Step 2: Make Your First Chat Request
The fastest way to verify your integration is with a simple text-only request. Because the supplied context says the interface is OpenAI-compatible, a chat-style payload is the most natural first test.
This example uses requests so the request/response flow is explicit:
import os
import requests
from dotenv import load_dotenv
load_dotenv()
API_KEY = os.environ["KIMI_API_KEY"]
BASE_URL = os.environ["KIMI_BASE_URL"]
MODEL = os.environ.get("KIMI_MODEL", "kimi-k2")
headers = {
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json",
}
payload = {
"model": MODEL,
"messages": [
{"role": "system", "content": "You are a concise technical assistant."},
{"role": "user", "content": "Say hello and confirm the API is working."}
]
}
response = requests.post(f"{BASE_URL}/chat/completions", json=payload, headers=headers, timeout=60)
response.raise_for_status()
print(response.json())What to check in the response:
- You receive HTTP 200.
- The response body is valid JSON.
- The model returns a structured assistant message.
- Your key is accepted without authentication errors.
If your provider uses a different path or header shape, keep the same debugging process but adjust the transport details to match the Moonshot-compatible endpoint you were issued.
Step 3: Add Function Calling for Tool Use
The official Kimi K2 page says Moonshot encourages developers to explore the tool calling API for agent applications. In practice, that means you let the model decide when to ask for a tool, then your code executes the tool and sends the result back.
A good first tool is something deterministic, such as a weather lookup, database query, or internal calculator.
import json
import os
import requests
from dotenv import load_dotenv
load_dotenv()
API_KEY = os.environ["KIMI_API_KEY"]
BASE_URL = os.environ["KIMI_BASE_URL"]
MODEL = os.environ.get("KIMI_MODEL", "kimi-k2")
headers = {
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json",
}
def get_weather(city: str) -> dict:
return {
"city": city,
"temperature_c": 18,
"condition": "cloudy"
}
tools = [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get current weather for a city.",
"parameters": {
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "City name"
}
},
"required": ["city"]
}
}
}
]
payload = {
"model": MODEL,
"messages": [
{"role": "user", "content": "What's the weather in Tokyo?"}
],
"tools": tools
}
response = requests.post(f"{BASE_URL}/chat/completions", json=payload, headers=headers, timeout=60)
response.raise_for_status()
result = response.json()
print(json.dumps(result, indent=2))The exact response shape depends on the API flavor you are using, but the workflow is the same:
- The model asks for a tool.
- Your application extracts the tool name and arguments.
- Your code runs the tool.
- You send the tool result back to the model.
- The model produces the final answer.
For production, keep the tool execution layer strict. Validate the tool name, validate the arguments, and reject anything that does not match your schema.
Step 4: Handle Image Inputs Safely
This is the part many teams get wrong. The supplied context states that vision features are not supported for Kimi K2 yet. That means you should not build a workflow that assumes native multimodal image understanding is available today.
Instead, design your app to do one of three things when it receives image input:
- Reject the request with a clear message.
- Route the image to a separate vision-capable service.
- Store the image metadata and continue with text-only analysis.
A simple guard looks like this:
def handle_user_input(text: str, image_paths: list[str] | None = None) -> dict:
if image_paths:
return {
"ok": False,
"error": "Kimi K2 vision features are not supported yet. Please remove image inputs or route them to a vision-capable service."
}
return {
"ok": True,
"text": text
}If your product roadmap expects multimodal support later, keep the interface ready now. That means separating text messages, image attachments, and file metadata in your app layer so you can swap in a compatible vision workflow later without rewriting the whole request pipeline.
Step 5: Add Error Handling That Survives Real Traffic
A reliable integration needs more than a successful demo call. You need to handle:
- Invalid or expired API keys.
- Missing or wrong model names.
- Payloads that do not match the expected JSON structure.
- Timeouts or transient network failures.
- Tool execution failures.
- Unsupported image inputs.
A simple wrapper gives you a predictable failure mode:
import requests
class KimiAPIError(Exception):
pass
def call_kimi(payload: dict) -> dict:
try:
response = requests.post(
f"{BASE_URL}/chat/completions",
json=payload,
headers=headers,
timeout=60
)
response.raise_for_status()
return response.json()
except requests.exceptions.HTTPError as e:
status = getattr(e.response, "status_code", None)
body = getattr(e.response, "text", "")
raise KimiAPIError(f"Kimi API returned HTTP {status}: {body}") from e
except requests.exceptions.Timeout as e:
raise KimiAPIError("Kimi API request timed out") from e
except requests.exceptions.RequestException as e:
raise KimiAPIError(f"Network or transport error: {e}") from eFor retries, be conservative. Retry only when the failure is likely transient, such as a timeout or a 5xx response. Do not retry authentication failures or schema errors. Those need a code or configuration fix, not another request.
Step 6: Put It Together in a Small Production-Shaped Flow
A basic end-to-end flow should look like this:
- Read the user request.
- Reject or reroute image attachments if they are present.
- Build a text-only payload for Kimi K2.
- Send the request with authenticated headers.
- Parse the response.
- Execute any requested tool calls.
- Return the final response to the user.
That structure is intentionally boring. Boring is good here. It keeps model calls isolated, makes failures visible, and prevents your UI or backend from depending on hidden behavior.
A minimal application entry point could look like this:
def answer_question(user_text: str, image_paths: list[str] | None = None) -> str:
attachment_result = handle_user_input(user_text, image_paths)
if not attachment_result["ok"]:
return attachment_result["error"]
payload = {
"model": MODEL,
"messages": [
{"role": "system", "content": "You are a precise assistant."},
{"role": "user", "content": attachment_result["text"]}
]
}
data = call_kimi(payload)
return str(data)From there, you can extend the wrapper with logging, tracing, quota checks, and structured output parsing.
Common Errors
401 Unauthorized
Usually means the API key is missing, expired, or sent with the wrong header. Check whether your endpoint expects Authorization: Bearer ... or X-API-Key: ....
403 Forbidden
Often indicates the key is valid but does not have access to the model or project you are calling. Verify the account, project, and model selection in your console.
404 Not Found
Usually means the base URL or endpoint path is wrong. This is common when a client is configured for one provider and pointed at another without updating the route.
400 Bad Request
Typically caused by malformed JSON, unsupported fields, or a tool schema that does not match what the API expects. Print the exact payload before sending it and validate it locally.
Tool Call Fails
This usually means your application accepted the model request but failed while running the local function. Validate argument types and return JSON that the model can consume.
Image Input Fails
This is expected if you try to send image data directly to Kimi K2 today. The supplied context explicitly says vision features are not supported yet, so treat that as a design constraint rather than a bug in your code.
FAQ
Does Kimi K2 API integration require a full rewrite of an existing app?
No. The supplied context says the platform offers an OpenAI/Anthropic-compatible interface, which is specifically useful for adapting existing applications instead of rebuilding them from scratch.
Can I send images to Kimi K2 today?
Not as a native vision workflow, based on the supplied context. Vision features are not supported for Kimi K2 yet, so image uploads should be rejected, rerouted, or handled by a separate service.
What is the safest first thing to test after setup?
Send a small text-only prompt and verify that authentication, base URL configuration, and JSON parsing all work before you add tool calls or more complex logic.
How should I implement function calling?
Keep the model and the tool runner separate. Let the model request a tool, validate the requested function and arguments in your app, execute the tool locally, and feed the result back into the conversation.
What should I log in production?
Log the request ID, model name, latency, status code, and whether a tool call or image rejection occurred. Avoid logging raw secrets or unnecessary user content.