Kimi API

Kimi / Developer guide

Kimi API: setup, examples and common errors

Connect to the international Kimi Open Platform, send a bounded first request and check whether the response is usable. Our four direct API trials show the request settings, returned usage and limits of the results.

By China AI Chat Team · API tests: · Streaming guide updated · Independent guide

Start here: use an Open Platform key with https://api.moonshot.ai/v1. This guide uses Chat Completions and kimi-k3. Kimi app membership, Kimi Code and third-party providers have different credentials and billing.

Kimi API workflow from your server to the provider, with response and usage checks.
Editorial illustration by ChinaAI. View full-size image.

1. Match your account, key and endpoint

  1. Open the international Kimi API console and check the available balance and account limits.
  2. Open API Keys, create a separately named key for your project and select its project. The console displays the full key once; store it in a secret manager.
  3. Keep the key ready for the hidden prompt in the Python example below, or have your secret manager supply it as MOONSHOT_API_KEY. Keep it out of source files, browser JavaScript, public repositories, screenshots and logs.
  4. Use the exact model ID and the endpoint for your chosen protocol. Start with one small request before adding files or tools.

The K3 quickstart requires a successful top-up of at least $1 to unlock K3. A free app account or paid app membership is not Open Platform API credit. Regional platform keys and balances are separate; see the official account and authentication guidance.

Chat Completions settings: SDK base URL https://api.moonshot.ai/v1; raw HTTP endpoint POST https://api.moonshot.ai/v1/chat/completions; authorization header Authorization: Bearer YOUR_KEY. An SDK appends the endpoint path, so do not put /chat/completions in its base URL. Source: API overview.

For a first local test, the Python example can ask for your key without displaying it. You do not need to type the key into a command or edit the script. If your secret manager already supplies MOONSHOT_API_KEY to Python, the script uses that value instead. A production site needs a controlled server endpoint; it must never send the provider key to visitors.

2. Send a first request with Python

This Python 3 example uses the standard library, so no SDK installation is required. You need Python 3 installed and an Open Platform key with available API credit.

  1. Copy the code below into a plain-text file named first_request.py, or download the example ZIP and extract it. On Windows, check that the file is not named first_request.py.txt.
  2. Open a terminal in the folder containing the file. On Windows, File Explorer’s Open in Terminal option can do this. On macOS or Linux, open a terminal and use cd to change to that folder.
  3. On Windows, run py first_request.py if the Python launcher is installed, or python first_request.py. On macOS or Linux, run python3 first_request.py. If the command is not found, install Python 3 from python.org, then reopen your terminal.
  4. If MOONSHOT_API_KEY is not already supplied, the script shows Kimi API key (hidden):. Paste or type your key there. No characters or asterisks appear; this is expected. Press Enter to send one paid request, or Ctrl+C to cancel. With an existing environment key, running the script sends the request immediately.
  5. A completed response prints Finish reason: stop, token usage and the answer. Read the answer before using it: completion does not guarantee factual accuracy.

An empty key or cancelled entry stops before a request is sent. If hidden input is unavailable in an editor’s console, use a regular terminal; the script will not fall back to displaying your key.

"""One paid Kimi request. Python 3 standard library; no automatic retries."""
import getpass
import http.client
import json
import os
import warnings

REQUEST = {
    'model': 'kimi-k3',
    'messages': [{'role': 'user', 'content': 'Explain a context window in two short sentences.'}],
    'reasoning_effort': 'low',
    'max_completion_tokens': 4096,
}

def request_once(api_key, payload):
    connection = http.client.HTTPSConnection('api.moonshot.ai', timeout=120)
    try:
        connection.request('POST', '/v1/chat/completions',
            body=json.dumps(payload), headers={
                'Authorization': 'Bearer ' + api_key,
                'Content-Type': 'application/json',
            })
        response = connection.getresponse()
        status = response.status
        data = json.loads(response.read())
    finally:
        connection.close()
    return status, data

def read_api_key():
    key = os.environ.get('MOONSHOT_API_KEY', '').strip()
    if key:
        return key
    try:
        with warnings.catch_warnings():
            warnings.simplefilter('error', getpass.GetPassWarning)
            key = getpass.getpass('Kimi API key (hidden): ').strip()
    except getpass.GetPassWarning:
        raise SystemExit('Hidden input unavailable. Use a regular terminal or a secret manager. No request sent.')
    except (EOFError, KeyboardInterrupt):
        raise SystemExit('Key entry cancelled. No request sent.')
    if not key:
        raise SystemExit('No key entered. No request sent.')
    return key

def main():
    key = read_api_key()
    try:
        status, data = request_once(key, REQUEST)
    except (OSError, http.client.HTTPException, ValueError):
        raise SystemExit('Request failed or response was unreadable. Check usage before retrying.')
    if status != 200:
        error = data.get('error', {})
        print('HTTP:', status, 'Error type:', error.get('type', 'unknown'))
        raise SystemExit('Check the provider error and account; no retry was sent.')
    choices = data.get('choices') or []
    if not choices:
        raise SystemExit('No choice returned; inspect the response before using it.')
    choice = choices[0]
    answer = choice.get('message', {}).get('content') or ''
    print('Finish reason:', choice.get('finish_reason'))
    print('Usage:', json.dumps(data.get('usage', {})))
    if choice.get('finish_reason') != 'stop' or not answer.strip():
        raise SystemExit('No complete text answer; do not treat this as success.')
    print(answer)

if __name__ == '__main__':
    main()

The request_once function and request payload are the ones used in our 25 September live test. The hidden key prompt was added on 27 September and checked locally with dummy values; it was not part of those four API calls.

How key entry works

A nonempty MOONSHOT_API_KEY takes priority. Otherwise, Python’s getpass reads the key without echoing it. The script stops if Python cannot hide the input. It does not print the key or write it to a file, and the prompt supplies only this Python process; it does not set a variable for later terminal commands.

The downloadable example includes local checks for environment-key priority, hidden entry, empty input, cancellation and unavailable hidden input. They use dummy keys and simulated responses, with network access blocked inside the tests. The original live requests and responses remain in the 25 September evidence archive.

The example makes no automatic retries. If it fails after sending a request, check usage before running it again.

K3 always thinks. We used reasoning_effort="low" and a 4,096-token completion ceiling, which covers thinking as well as the final answer. This is a starting allowance, not a claim that every task needs that many tokens or will finish within it. K3 fixes sampling settings such as temperature, so this request omits them. For supported settings, use the K3 parameter guide.

Already using the OpenAI Python SDK? The provider supports its Chat Completions format with Kimi’s key and base URL. Use explicit timeout and retry settings, and check Kimi-specific parameters. Our tests here used direct HTTP, not the SDK; see the official SDK quickstart for that alternative.

3. The equivalent request with cURL

This is a Bash example for Linux, macOS or a Bash terminal. Have your secret manager supply MOONSHOT_API_KEY to that shell first. Entering a key at the Python prompt above does not set this shell variable. Do not paste the multiline Bash syntax into PowerShell; use the Python example there.

curl --fail-with-body --max-time 120 \
  "https://api.moonshot.ai/v1/chat/completions" \
  -H "Authorization: Bearer $MOONSHOT_API_KEY" \
  -H "Content-Type: application/json" \
  --data '{
    "model": "kimi-k3",
    "messages": [{"role": "user", "content": "Explain a context window in two short sentences."}],
    "reasoning_effort": "low",
    "max_completion_tokens": 4096
  }' 

This cURL equivalent was reviewed against the tested JSON payload but was not executed in our four-call trial. It has no retry option. A local timeout does not establish that the provider stopped processing or charged nothing; inspect usage before sending the request again.

What our four live Kimi API requests returned

On 25 September 2026, we sent four first attempts to the official international Chat Completions endpoint from Windows using Python 3’s standard library. There were no retries, tools, file uploads or private documents. All four returned HTTP 200, nonempty final content and finish_reason: stop.

RequestInput tokensCompletion tokensReported reasoning tokens*Estimated cost
K3: ordinary text request95754$0.00141
K3: streamed text request95816$0.0015
K3: strict JSON extraction2106836$0.00165
K2.6: thinking disabled2591$0.00005975

*Reasoning tokens are part of completion usage, not an extra quantity to add to it. No cache-hit count was reported; these estimates price all input at the uncached rate. The K3 responses explicitly reported zero cache-write tokens. K2.6 did not return that field.

Total estimated inference cost was $0.00461975, calculated from returned usage and the published token rates. It is not a reconciled invoice or a fixed price for four future requests. Provider usage reports can lag, and different answers consume different token amounts.

  • Ordinary and streamed explanations: both followed the two-sentence instruction, but described older context as being forgotten too simply. An API request exceeding the context limit can fail; your application controls which history to retain, shorten or summarize. Transport success did not make every explanation precise.
  • Strict JSON: the fictional booking returned {"reference":"WX-204","attendees":18,"room":"Cedar"}. We independently checked the exact keys, types and values against the source fixture. One successful schema test does not establish reliability for other inputs.
  • K2.6 without thinking: the date conversion returned exactly 2031-02-09. Its reasoning_content was empty, but usage reported one reasoning token. We preserve that distinction rather than promise zero billed reasoning.

Download the exact requests, responses and Python example

The ZIP preserves all four first results, including streaming chunks, with request IDs removed and no credentials. These are integration checks, not a K3-versus-K2.6 benchmark, a latency comparison or a test of rate-limit recovery.

Read the answer and usage separately

For a simple text request, read choices[0].message.content, then inspect finish_reason. An empty answer or length finish is not a completed task. A tool_calls finish means the application has another step to handle; it is not automatically a failure.

Our first K3 request returned this usage object:

{
  "prompt_tokens": 95,
  "completion_tokens": 75,
  "total_tokens": 170,
  "completion_tokens_details": {
    "reasoning_tokens": 4
  },
  "prompt_tokens_details": {
    "cache_write_tokens": 0
  }
}

prompt_tokens counts input; completion_tokens counts generated output, including reasoning; total_tokens is their sum. Do not add reasoning_tokens a second time when calculating output cost. A missing cache field is not the same as a provider explicitly reporting zero.

For example, that first request’s estimate is (95 × $3 + 75 × $15) / 1,000,000 = $0.001410. This uses K3’s uncached input and output rates checked on the review date. Cache billing and longer examples belong in the pricing guide. Sources: Chat Completions response reference and thinking-token behavior.

Streaming: receive the answer and keep final usage

Set stream: true and stream_options: {"include_usage": true}. The API returns server-sent events (SSE): small records carrying parts of the response. Append delta.content to the answer and keep delta.reasoning_content separately. A network read can split a record, so decode and assemble complete events before parsing their JSON.

Run a complete Python streaming example

Download the Python streaming example and local checks

The ZIP contains stream_request.py, its offline checks and a README. It uses Python’s standard library and the same K3 prompt, low reasoning effort and 4,096-token completion ceiling as our original stream. It sends one request to the international Chat Completions endpoint and makes no automatic retries.

  1. Extract the ZIP and open a regular terminal in its folder. Use the same Open Platform account and key as for the first request.
  2. On Windows, run py stream_request.py or python stream_request.py. On macOS or Linux, run python3 stream_request.py.
  3. If MOONSHOT_API_KEY is not already supplied, enter your key at Kimi API key (hidden): and press Enter. Nothing appears as you type. This sends one paid request. An existing environment key sends the request immediately when the script runs; empty input or cancelled key entry sends none.
  4. Watch the answer arrive, then read the final completion message and reported token counts. During the stream, the text is provisional. A complete text result needs a nonempty answer, finish_reason: stop and [DONE]; it still needs a factual check.

The script keeps reasoning separate without printing it. It does not execute tools or continue a conversation. A token-limit finish, pending tool step, unreadable event or interrupted connection is not reported as a completed text answer. Press Ctrl+C to cancel; closing the connection does not prove that provider processing or billing stopped.

Read usage before skipping an empty choice

The last usage record may have an empty choices array. Read its top-level usage before skipping it. The parser also accepts usage inside the first choice, as described in the provider’s streaming guide. The core accumulation step looks like this inside an existing SSE reader with initialized variables:

# Inside a reader of server-sent event (SSE) data chunks:
choices = chunk.get("choices") or []
reported = chunk.get("usage") or (choices[0].get("usage") if choices else None)
if reported:
    usage = reported
if choices:
    choice = choices[0]
    delta = choice.get("delta") or {}
    answer += delta.get("content") or ""
    reasoning += delta.get("reasoning_content") or ""
    if choice.get("finish_reason"):
        finish_reason = choice["finish_reason"]

This excerpt explains accumulation; use the complete downloaded file for the network request and event framing. Keep reading after finish_reason: stop until [DONE], so the final usage record is not lost. Our saved 25 September stream reached that marker, and its reconstructed answer and token totals were checked independently.

If the stream ends early, keep the received answer marked as partial and check provider usage before retrying. Missing usage means unknown tokens and cost, not zero. The script prints only supplied numeric token counts and never turns an absent field into zero.

What was checked for this downloadable file?

On 1 October 2026, the new file passed 28 offline checks, including a replay of the sanitized 25 September stream, split UTF-8 and event boundaries, final usage with empty choices, and simulated incomplete or failed responses. Key-entry checks used dummy values. Network access was blocked in these tests; they made no new Kimi API calls. The original four live results above are unchanged. The ZIP includes the fixture, checks and run record so you can inspect this distinction.

To run the checks without a key or API request, use py test_stream_request.py on Windows or python3 test_stream_request.py on macOS/Linux. The request file is a single-request text example, not a complete production or multi-turn client. Apply the server access and spending controls before deploying it.

Strict JSON and a non-thinking alternative

For structured data, K3 supports response_format with type: "json_schema" and json_schema.strict: true. Parse the final answer field as JSON and validate the values against your source, as we did for the booking fixture. The complete schema and request are in the evidence ZIP. See structured output documentation.

Our K2.6 request used "thinking": {"type": "disabled"} at the top level of the HTTP JSON body. With the OpenAI SDK, provider-specific thinking is passed through extra_body. Do not apply this toggle to K3; review the model and thinking-mode comparison before changing models.

Diagnose the response, not just the status

SymptomFirst checkNext action
400: invalid requestJSON shape, current model ID, parameters and context budget.Correct the request before retrying. Changing only the model name may leave incompatible settings.
401: authenticationMissing/revoked key, Bearer header, wrong regional platform or Code key.Match the key’s product and endpoint. Never publish the header while asking for help.
Model not found or unavailableExact ID, account access and correct base URL.Check the current catalog and GET /v1/models with the same account. Avoid invented version aliases.
429The error type and message: rate, capacity and quota problems need different fixes.Use bounded backoff for eligible transient failures; resolve depleted balance or quota before repeating an unchanged request.
5xx or timeoutProvider status, workload and any available request identifier.Record safe metadata. Reconcile uncertain usage and any tool side effects before a bounded retry.
200 but no usable answerFinal content, finish reason, output budget and whether a tool call is pending.Handle the actual state. Do not show an empty thinking-only response as a successful answer.

These remedies follow the official error reference and troubleshooting guide; we did not deliberately trigger these failures. The first Python example also handles unreadable responses, since a gateway error need not be JSON.

Before adding this to a website

An output-token ceiling limits one part of one request; it is not a user-level or sitewide spending limit. Enforce input size, allowed models, output caps, request frequency and a total spend allowance on your server. Reserve a conservative request cost before sending it, and keep that reservation when a timeout leaves billing uncertain.

Keep the provider key in private server storage. Authenticate or otherwise control access to your own endpoint, limit concurrent work and disable unlimited retries. Log only necessary diagnostics, such as model, time, finish reason and token counts. Review any model-requested tool action before executing it; text returned by a model is not authorization to act.

For multi-turn K3 and tool workflows, preserve the complete returned assistant message as required by the model documentation. For images and video, follow its supported input formats rather than assuming that any public URL works. Those workflows were outside this four-request test.

Continue with Kimi overview and official access, model selection, API pricing or the separate Kimi Code guide.

ChinaAI is independent of Kimi and Moonshot AI. Read how we research and test or report a correction.