I tried out Moonshot AI's Kimi K2, which became generally available on Amazon Bedrock
This page has been translated by machine translation. View original
Hello, I'm Kema.
On September 18, 2026, Moonshot AI's Kimi K3 became generally available on Amazon Bedrock.
I personally feel that Kimi K3 excels in Japanese language capabilities, and I use it frequently when working with GitHub Copilot.
Since its coding abilities are also strong, I regularly use a workflow where I have Opus implement something and then have Kimi K3 review it.
Now that Kimi K3 is supported on Bedrock as well, I'd like to introduce how it works.
It's an open-weight model with 2.8 trillion parameters, supporting image input and a 1 million token context window.
It's also the first open-weight model on Bedrock to support explicit prompt caching.
What's New: Kimi K3 by Moonshot AI is now generally available on Amazon Bedrock | AWS
This time, I tried calling Kimi K3 from bedrock-runtime via the Converse/InvokeModel/OpenAI-compatible APIs, and also tested explicit prompt caching and calls from bedrock-mantle.
1. Test Environment
| Item | Details |
|---|---|
| Test Date | September 21, 2026 |
| OS | macOS (Darwin 25.6.0) |
| AWS CLI | 2.34.25 |
| Python | 3.12 (run with uv) |
| Libraries | openai, botocore, httpx |
| Authentication | IAM role temporary credentials (SigV4) |
2. Test Contents
2.1 Model List
First, I retrieved the list of foundation models filtered to the Moonshot AI provider.
aws bedrock list-foundation-models \
--by-provider "Moonshot AI" \
--region us-east-1 \
--query "modelSummaries[?modelId=='moonshotai.kimi-k3']"
# Example output
[
{
"modelArn": "arn:aws:bedrock:us-east-1::foundation-model/moonshotai.kimi-k3",
"modelId": "moonshotai.kimi-k3",
"modelName": "Kimi K3",
"providerName": "Moonshot AI",
"inputModalities": [
"TEXT",
"IMAGE"
],
"outputModalities": [
"TEXT"
],
"responseStreamingSupported": true,
"customizationsSupported": [],
"inferenceTypesSupported": [
"INFERENCE_PROFILE"
],
"modelLifecycle": {
"status": "ACTIVE",
"startOfLifeTime": "2026-08-27T16:00:00+00:00"
}
}
]
The model ID is moonshotai.kimi-k3, with text and image as inputs and text as output.
Since inferenceTypesSupported is only INFERENCE_PROFILE, you need to specify an inference profile ID when calling it.
Two inference profiles are available: the global global.moonshotai.kimi-k3 and the US regional us.moonshotai.kimi-k3.
When I checked with list-inference-profiles, both were returned in us-east-1, while only global. was returned in ap-northeast-1.
The routing destinations for us. are us-east-1, us-east-2, and us-west-2.
aws bedrock list-inference-profiles \
--region us-east-1 \
--query "inferenceProfileSummaries[?contains(inferenceProfileId,'kimi-k3')].[inferenceProfileId,description]"
# Example output
[
[
"global.moonshotai.kimi-k3",
"Routes requests to Kimi K3 globally across all supported AWS Regions."
],
[
"us.moonshotai.kimi-k3",
"Routes requests to Kimi K3 in us-east-1, us-east-2, us-west-2."
]
]
2.2 Converse
First, I ran Converse targeting us-east-1 with us.moonshotai.kimi-k3 specified.
aws bedrock-runtime converse \
--region us-east-1 \
--model-id us.moonshotai.kimi-k3 \
--messages '[{"role":"user","content":[{"text":"Say ok"}]}]' \
--inference-config '{"maxTokens":512}'
# Example output
{
"output": {
"message": {
"role": "assistant",
"content": [
{
"reasoningContent": {
"reasoningText": {
"text": "The user just said \"Say ok\". This is a very simple request. They want me to say \"ok\". There's nothing harmful about this, nothing complex. It's just a simple instruction.\n\nThe format should be minimal - just \"ok\" or \"OK\" would be appropriate. No need for lengthy explanation or anything else. The user asked me to say something specific, and I should just do it."
}
}
},
{
"text": "Ok"
}
]
}
},
"stopReason": "end_turn",
"usage": {
"inputTokens": 87,
"outputTokens": 97,
"totalTokens": 184,
"cacheReadInputTokens": 0,
"cacheWriteInputTokens": 0
},
"metrics": {
"latencyMs": 873
}
}
Even without specifying any parameters, reasoning is enabled by default, and the response content contained two blocks: reasoningContent and text.
The majority of the 97 output tokens are from the reasoning portion.
When I tried setting maxTokens to 64, the limit was reached just from reasoning, resulting in a stopReason of max_tokens.
Even when expecting a short response, it's safer to set maxTokens generously to account for the reasoning token consumption.
By the way, specifying moonshotai.kimi-k3 without the profile prefix resulted in the following ValidationException.
# Example output
An error occurred (ValidationException) when calling the Converse operation: Invocation of model ID moonshotai.kimi-k3 with on-demand throughput isn't supported. Retry your request with the ID or ARN of an inference profile that contains this model.
Also, specifying us.moonshotai.kimi-k3 against ap-northeast-1 resulted in a ValidationException with The provided model identifier is invalid.
When calling from the Tokyo region, only global. can be specified.
I also tried InvokeModel with the same model ID, and got a successful response there as well.
Here are the results when passing {"messages":[{"role":"user","content":"Say ok"}],"max_tokens":512} as the input body.
# Example output (reasoning_content abbreviated in the middle)
{"choices":[{"finish_reason":"stop","index":0,"message":{"annotations":[],"content":"Ok","role":"assistant","refusal":null,"reasoning_content":"The user has just said \"Say ok\". ..."}}],"created":1789950731,"id":"chatcmpl-omohbqkp5p4qpfcw3rt4vcf6kr3pekhyyapb6weddehtodajjnaa","model":"us.moonshotai.kimi-k3","object":"chat.completion","service_tier":"default","usage":{"completion_tokens":141,"completion_tokens_details":{"accepted_prediction_tokens":0,"audio_tokens":0,"reasoning_tokens":125,"rejected_prediction_tokens":0},"prompt_tokens":87,"prompt_tokens_details":{"audio_tokens":0,"cache_write_tokens":0,"cached_tokens":0},"total_tokens":228},"system_fingerprint":null}
The InvokeModel response is in Chat Completions format, and the reasoning content is included in reasoning_content.
Note that the model card documents a known limitation where sending a multi-turn request via Converse that includes reasoning content from previous turns results in an InternalServerException.
This is said to affect default configurations of frameworks like LangChain and Strands Agents, and the use of the OpenAI-compatible Responses/Chat Completions API is recommended over Converse.
Converse has known limitations with this model, including a failure (
InternalServerException) when reasoning content from earlier turns is included in a multi-turn request, which affects frameworks such as LangChain and Strands Agents in their default configurations, and rejection of attached document inputs such as PDF and HTML. To use Converse for multi-turn requests, remove reasoning blocks from prior turns.
Source: Model card: Kimi K3 | Amazon Bedrock User Guide
However, in my own testing, both sending the second turn with the first turn's reasoningContent included and sending it removed returned responses without errors.
I only tested a simple two-turn conversation of "Say ok" followed by "Say ok again."
When calling via Converse from agent frameworks and similar tools, it's safer to either use the OpenAI-compatible API or implement logic to strip reasoning blocks from prior turns before sending, to avoid the risk of hitting this limitation.
Script used for testing (click to expand)
~/kimi-k3-bedrock/converse_multiturn.py:
import boto3
from botocore.exceptions import ClientError
MODEL_ID = "us.moonshotai.kimi-k3"
client = boto3.client("bedrock-runtime", region_name="us-east-1")
messages = [{"role": "user", "content": [{"text": "Say ok"}]}]
first = client.converse(modelId=MODEL_ID, messages=messages, inferenceConfig={"maxTokens": 512})
assistant = first["output"]["message"]
print("1st turn block types:", [list(b.keys())[0] for b in assistant["content"]])
def second_turn(label, assistant_message):
try:
resp = client.converse(
modelId=MODEL_ID,
messages=messages + [assistant_message, {"role": "user", "content": [{"text": "Say ok again"}]}],
inferenceConfig={"maxTokens": 512},
)
text = [b["text"] for b in resp["output"]["message"]["content"] if "text" in b]
print(f"{label}: OK {text}")
except ClientError as e:
print(f"{label}: {e.response['Error']['Code']} - {e.response['Error']['Message']}")
# Send the 2nd turn with reasoningContent included
second_turn("with reasoning", assistant)
# Send the 2nd turn with reasoningContent removed
stripped = {"role": "assistant", "content": [b for b in assistant["content"] if "reasoningContent" not in b]}
second_turn("without reasoning", stripped)
uv run --python 3.12 --with boto3 python ~/kimi-k3-bedrock/converse_multiturn.py
# Example output
1st turn block types: ['reasoningContent', 'text']
with reasoning: OK ['Ok']
without reasoning: OK ['Ok']
2.3 OpenAI-Compatible Path
Next, I tried sending a Responses API format body to the OpenAI-compatible endpoint of bedrock-runtime.
~/kimi-k3-bedrock/responses-body.json:
{"model":"us.moonshotai.kimi-k3","input":"Say ok","store":false}
I passed the same temporary credentials used by the AWS CLI from environment variables and executed with curl's SigV4 signing option.
curl --silent --show-error \
--aws-sigv4 'aws:amz:us-east-1:bedrock' \
--user "$AWS_ACCESS_KEY_ID:$AWS_SECRET_ACCESS_KEY" \
-H "x-amz-security-token: $AWS_SESSION_TOKEN" \
-H 'Content-Type: application/json' \
--data-binary @$HOME/kimi-k3-bedrock/responses-body.json \
https://bedrock-runtime.us-east-1.amazonaws.com/openai/v1/responses
A successful response was returned with HTTP status 200.
The model in the response was us.moonshotai.kimi-k3, the status was completed, and the output contained two items—reasoning and message—with the output text being Ok.
Here is an excerpt of the usage:
# Example output (usage excerpt)
{
"input_tokens": 87,
"input_tokens_details": {
"cache_write_tokens": 0,
"cached_tokens": 0
},
"output_tokens": 120,
"output_tokens_details": {
"reasoning_tokens": 104
},
"total_tokens": 207
}
The reasoning in the response was {"summary": null, "effort": "max", "context": "all_turns"}.
When no reasoning intensity is specified in the request, it appears to default to max.
The Moonshot AI API documentation also states that reasoning_effort can be set to "low", "high", or "max", with the default being "max".
Source: Quickstart | Kimi API Platform
I also sent a request to /openai/v1/chat/completions with the same model ID, and that also succeeded with status 200.
The request body was {"model":"us.moonshotai.kimi-k3","messages":[{"role":"user","content":"Say ok"}],"max_completion_tokens":512}.
The finish_reason was stop, the response text was Ok, and the usage was prompt_tokens 87 / completion_tokens 69 (of which reasoning_tokens 53) / total_tokens 156.
Note that the model card also recommends using the Chat Completions API for Kimi K3.
Whenever possible, we recommend using the
bedrock-runtimeendpoint for new applications. For Kimi K3, we recommend using the Chat Completions API. See Endpoints supported by Amazon Bedrock for details.
Source: Model card: Kimi K3 | Amazon Bedrock User Guide
2.4 Calling from the OpenAI SDK with IAM Role Environment Variables
The official sample code introduces steps for passing a Bedrock API key or a temporary token generated by aws-bedrock-token-generator to the OpenAI SDK.
This time, I tried a setup where the temporary credentials obtained from an IAM role are placed in environment variables, and SigV4 signatures are added to HTTP requests sent by the OpenAI SDK.
According to the endpoint specification, both bedrock-runtime and bedrock-mantle support SigV4 authentication.
The IAM principal calling the bedrock-runtime OpenAI-compatible API requires bedrock:InvokeModel permission on the inference profile.
For the Responses API, access to the default project is also required, and for streaming, bedrock:InvokeModelWithResponseStream permission is needed as well.
~/kimi-k3-bedrock/sigv4_openai.py:
import os
import httpx
from botocore.auth import SigV4Auth
from botocore.awsrequest import AWSRequest
from botocore.credentials import Credentials
from openai import OpenAI
REGION = "us-east-1"
SIGNING_SERVICE = "bedrock"
credentials = Credentials(
os.environ["AWS_ACCESS_KEY_ID"],
os.environ["AWS_SECRET_ACCESS_KEY"],
os.environ.get("AWS_SESSION_TOKEN"),
)
class SigV4Signer(httpx.Auth):
"""Adds SigV4 signatures to HTTP requests sent by the OpenAI SDK"""
requires_request_body = True
def auth_flow(self, request):
aws_request = AWSRequest(
method=request.method,
url=str(request.url),
data=request.content,
headers={"content-type": request.headers.get("content-type", "application/json")},
)
SigV4Auth(credentials, SIGNING_SERVICE, REGION).add_auth(aws_request)
for key, value in aws_request.headers.items():
request.headers[key] = value
yield request
client = OpenAI(
api_key="unused-sigv4",
base_url=f"https://bedrock-runtime.{REGION}.amazonaws.com/openai/v1",
http_client=httpx.Client(auth=SigV4Signer(), timeout=300.0),
)
~/kimi-k3-bedrock/chat_completions.py:
import json
from sigv4_openai import client
MODEL_ID = "us.moonshotai.kimi-k3"
response = client.chat.completions.create(
model=MODEL_ID,
messages=[{"role": "user", "content": "Can you explain the features of Amazon Bedrock?"}],
)
print("=== content ===")
print(response.choices[0].message.content)
print("=== usage ===")
print(json.dumps(response.usage.model_dump(), indent=2))
I ran the script using uv to resolve the dependencies.
cd ~/kimi-k3-bedrock
uv run --python 3.12 --with openai --with botocore --with httpx python chat_completions.py
When executed, it returned text explaining Bedrock's features organized into 10 items.
The usage breakdown was: input 95, output 2,240 (of which reasoning 1,577), total 2,335 tokens.
Reasoning accounted for about 70% of the output tokens, and it's worth noting that this reasoning portion is also billed at the output rate.
# Example output (excerpt)
=== content ===
# Amazon Bedrock Overview
Amazon Bedrock is a fully managed AWS service for building generative AI applications. It provides access to foundation models (FMs) from multiple providers through a single, unified API—without requiring you to manage any infrastructure.
## Key Features
### 1. Broad Model Selection
...
=== usage ===
{
"completion_tokens": 2240,
"prompt_tokens": 95,
"total_tokens": 2335,
"completion_tokens_details": {
"accepted_prediction_tokens": 0,
"audio_tokens": 0,
"reasoning_tokens": 1577,
"rejected_prediction_tokens": 0,
"text_tokens": null
},
...
}
2.5 Explicit Prompt Caching
Kimi K3 has implicit prompt caching enabled by default, but it also has a feature for explicitly specifying the range to cache.
According to the model card, this explicit caching is only available through the Responses and Chat Completions APIs, with a minimum of 1,024 tokens per checkpoint and a retention period of at least 30 minutes.
Following the example in the launch blog, I set explicit mode with prompt_cache_options and placed a prompt_cache_breakpoint at the end of the system prompt.
Using the sigv4_openai.py created earlier, I sent two requests to the same system prompt with only the question changed.
~/kimi-k3-bedrock/prompt_caching.py:
import json
import time
from sigv4_openai import client
MODEL_ID = "us.moonshotai.kimi-k3"
# Fixed prompt of 1,024 or more tokens to be cached
SYSTEM_PROMPT = "You are a support agent for the fictional service 'Kumo Notes'.\n" + "\n".join(
f"Rule {i}: When a customer asks about topic {i}, answer politely in one sentence and cite rule {i}."
for i in range(1, 121)
)
def ask(question):
start = time.time()
resp = client.responses.create(
model=MODEL_ID,
store=False,
extra_body={"prompt_cache_options": {"mode": "explicit"}},
input=[
{
"type": "message",
"role": "system",
"content": [
{
"type": "input_text",
"text": SYSTEM_PROMPT,
"prompt_cache_breakpoint": {"mode": "explicit"},
}
],
},
{
"type": "message",
"role": "user",
"content": [{"type": "input_text", "text": question}],
},
],
)
print(f"--- {question} ({time.time() - start:.2f}s)")
print(resp.output_text)
print(json.dumps(resp.usage.model_dump(), indent=2))
ask("Tell me about topic 7.")
ask("Tell me about topic 42.")
cd ~/kimi-k3-bedrock
uv run --python 3.12 --with openai --with botocore --with httpx python prompt_caching.py
# Example output
--- Tell me about topic 7. (2.53s)
Thank you for asking about topic 7 — Kumo Notes is happy to help, and we're glad to assist you with any related questions, as per Rule 7.
{
"input_tokens": 2997,
"input_tokens_details": {
"cache_write_tokens": 2972,
"cached_tokens": 0
},
"output_tokens": 212,
"output_tokens_details": {
"reasoning_tokens": 163
},
"total_tokens": 3209
}
--- Tell me about topic 42. (3.70s)
Thank you for asking about topic 42 — I'm happy to help with any specific questions you have about it, as provided under rule 42.
{
"input_tokens": 2997,
"input_tokens_details": {
"cache_write_tokens": 0,
"cached_tokens": 2972
},
"output_tokens": 507,
"output_tokens_details": {
"reasoning_tokens": 463
},
"total_tokens": 3504
}
In the first request, 2,972 out of 2,997 input tokens were written to the cache (cache_write_tokens), and in the second request, the same 2,972 tokens were read from the cache (cached_tokens).
Since the cache read rate is one-tenth of the regular input rate, this could significantly reduce input costs for agent configurations that repeatedly send long system prompts or large numbers of tool definitions.
Note that the response time was actually longer for the second request, but this is because the reasoning token count for the second request was 463, approximately three times that of the first (163 tokens), so this simple measurement alone wasn't enough to judge the impact of caching on response speed.
2.6 Mantle
I also tried calling from bedrock-mantle, but it was not available.
When I specified moonshotai.kimi-k3 against the OpenAI-compatible Responses API in both us-west-2 and us-east-1, both returned HTTP 404.
curl --silent --show-error \
--aws-sigv4 'aws:amz:us-east-1:bedrock-mantle' \
--user "$AWS_ACCESS_KEY_ID:$AWS_SECRET_ACCESS_KEY" \
-H "x-amz-security-token: $AWS_SESSION_TOKEN" \
-H 'Content-Type: application/json' \
--data-binary '{"model":"moonshotai.kimi-k3","input":"Say ok","store":false}' \
https://bedrock-mantle.us-east-1.api.aws/v1/responses
# Example output
{"error":{"code":"not_found_error","message":"The model 'moonshotai.kimi-k3' does not exist","param":null,"type":"invalid_request_error"}}
Changing the model specification to us.moonshotai.kimi-k3 also resulted in 404.
When I checked the list of available models via /v1/models, only two Moonshot AI models were present—moonshotai.kimi-k2.5 and moonshotai.kimi-k2-thinking—and Kimi K3 was not in the lineup.
Looking at the Programmatic Access list in the model card, only bedrock-runtime is listed.
While Kimi K2.5 and Kimi K2 Thinking can be called via Mantle, using Kimi K3 requires targeting bedrock-runtime.
3. Pricing
Bedrock pricing can be found on the model card, and provider direct pricing can be found on the Kimi API pricing page.
Here is a summary of the rates per 1M tokens at the Standard tier.
JPY conversion uses 1 USD = 150 JPY.
| Invocation Method | Input | Output | Cache Read | Cache Write |
|---|---|---|---|---|
| Bedrock (Global) | $3.00 (approx. ¥450) | $15.00 (approx. ¥2,250) | $0.30 (approx. ¥45) | $3.75 (approx. ¥563, 30 min) |
| Bedrock (US) | $3.30 (approx. ¥495) | $16.50 (approx. ¥2,475) | $0.33 (approx. ¥50) | $4.125 (approx. ¥619, 30 min) |
| Moonshot AI Direct | $3.00 (approx. ¥450) | $15.00 (approx. ¥2,250) | $0.30 (approx. ¥45) | $3.00 (approx. ¥450, 5 min) / $6.00 (approx. ¥900, 1 hour) |
When using the Bedrock global inference profile (cross-region inference), the per-unit prices for input, output, and cache reads are set to match Moonshot AI's direct pricing.
Choosing the US regional profile (us.) is approximately 10% more expensive than the global option.
In addition to the Standard tier, Bedrock also offers two other service tiers: Priority and Flex.
Priority is priced at 1.75x the Standard rate, while Flex is at 0.5x.
However, note that service tiers can only be specified through the Responses API and Chat Completions API—when calling via Converse or InvokeModel, the tier is fixed at Standard.
4. Summary
Moonshot AI's Kimi K3 is now available on Amazon Bedrock.
While models like Kimi K2.5 were also accessible via bedrock-mantle, Kimi K3 is provided only through bedrock-runtime, and can be called from four APIs: Converse, InvokeModel, Chat Completions, and Responses.
Existing code using the OpenAI SDK can be adapted to work with it by simply changing three things: the base_url, model ID, and authentication method to target Bedrock.
Based on my hands-on experience, here's a practical guide for deciding how to use it in implementations:
-
For new code, choose the Chat Completions API (officially recommended, and it gives you access to explicit prompt caching and service tiers)
-
When reusing existing Converse code, build requests by removing reasoning blocks (
reasoningContent) from prior turns in multi-turn conversations
Since reasoning runs at full capacity by default (effort: max), output tokens tend to balloon even for short instructions.
I'm looking forward to inference profiles expanding to Japan in the future.
