I tried out Moonshot AI's Kimi K2, which became generally available on Amazon Bedrock

I tried out Moonshot AI's Kimi K2, which became generally available on Amazon Bedrock

I tried out Moonshot AI's Kimi K3, which became generally available on Amazon Bedrock, using multiple invocation methods including Converse, the OpenAI-compatible API, and explicit prompt caching.
2026.09.21

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 also feel that Kimi K3 has excellent Japanese language capabilities, and I often use it with GitHub Copilot.
Since it also has high coding ability, I regularly use it in workflows like having Opus implement something and then having 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

So this time, I tried calling Kimi K3 from bedrock-runtime via the Converse/InvokeModel/OpenAI-compatible APIs, and also tested explicit prompt caching and calling from bedrock-mantle.

1. Verification Environment

Item Details
Verification 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. Verification Contents

2.1 Model List

First, I tried retrieving 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 input and text output.
Since inferenceTypesSupported only has 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 tried running Converse against 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
    }
}

Reasoning was enabled even without specifying any parameters, 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 limiting maxTokens to 64, the reasoning alone hit the limit and stopReason became max_tokens.
Even when expecting a short response, it's safest to set maxTokens generously to account for reasoning token consumption.

Incidentally, when specifying moonshotai.kimi-k3 directly without the profile prefix, the following ValidationException occurred:

# 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 also resulted in a ValidationException saying 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 it returned a response without issues.
Here are the results when passing {"messages":[{"role":"user","content":"Say ok"}],"max_tokens":512} as the input body:

# Example output (reasoning_content truncated 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 mentions 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 LangChain and Strands Agents in their default configurations, and the use of Responses/Chat Completions API over OpenAI compatibility 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 environment, both cases—sending the second turn with the first turn's reasoningContent included and sending it without—returned responses without errors.
This time I only tested a simple two-turn conversation of "Say ok" followed by "Say ok again".
When calling via Converse from agent frameworks or similar, to avoid the risk of hitting this limitation, it's safer to either use the OpenAI-compatible API or implement it so that reasoning blocks from previous turns are removed before sending.

Script used for verification (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 ran it 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 response was returned normally with HTTP status 200.
The model in the response was us.moonshotai.kimi-k3, status was completed, and 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 reasoning intensity is not specified on the request side, it appears to operate at max by default.
The Moonshot AI API documentation also states that reasoning_effort can be set to "low" / "high" / "max", with the default being "max".

Source: Quickstart | Kimi API Platform

Also, sending a request to /openai/v1/chat/completions with the same model ID 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 usage was prompt_tokens 87 / completion_tokens 69 (of which reasoning_tokens 53) / total_tokens 156.
The model card also recommends using the Chat Completions API for Kimi K3.

Whenever possible, we recommend using the bedrock-runtime endpoint 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 procedures for passing a Bedrock API key or a temporary token generated by aws-bedrock-token-generator to the OpenAI SDK.
This time, I tried attaching SigV4 signatures to HTTP requests sent by the OpenAI SDK while keeping the temporary credentials obtained from an IAM role in environment variables.
According to the endpoint specifications, both bedrock-runtime and bedrock-mantle support SigV4 authentication.

The IAM principal executing the bedrock-runtime OpenAI-compatible API needs 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):
    """Attaches 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 dependencies.

cd ~/kimi-k3-bedrock
uv run --python 3.12 --with openai --with botocore --with httpx python chat_completions.py

When executed, text was returned organizing Bedrock's features into 10 categories.
The usage breakdown was: input 95, output 2,240 (of which reasoning 1,577), total 2,335 tokens.
Reasoning accounts for about 70% of 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 the ability to explicitly specify the range to cache.
According to the model card, this explicit caching is only available for the Responses and Chat Completions APIs, with a minimum of 1,024 tokens per checkpoint and a minimum retention period of 30 minutes.

Following the example in the launch blog, I set the 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 against 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 tokens out of the 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 normal input rate, this could significantly reduce input costs in agent configurations that repeatedly send long system prompts or large numbers of tool definitions.
Note that the response time itself was longer for the second request, but this is because the second request's reasoning token count was 463, approximately three times that of the first (163 tokens), so this simple measurement alone was not sufficient to determine the benefit 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 a 404.
When I checked the available models via /v1/models, only two Moonshot AI models were listed: moonshotai.kimi-k2.5 and moonshotai.kimi-k2-thinking, with Kimi K3 not in the lineup.
Looking at the Programmatic Access list in the model card, only bedrock-runtime is listed.
Kimi K2.5 and Kimi K2 Thinking can be called via Mantle, but for Kimi K3 you need to target bedrock-runtime.

3. Pricing

Bedrock pricing can be found on the model card, and provider direct pricing on the Kimi API pricing page.
Here is a summary of the Standard tier pricing per 1M tokens.
Yen conversion is based on 150 yen per dollar.

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 unit prices for input, output, and cache reads are set identically to Moonshot AI direct pricing.
Choosing the US regional profile (us.) is approximately 10% more expensive compared to global.

In addition to the Standard tier, Bedrock also offers two other service tiers: Priority and Flex.
Priority is priced at 1.75 times Standard, and Flex at 0.5 times.
Note, however, that service tiers can only be specified for the Responses API and Chat Completions API; when calling via Converse or InvokeModel, it is fixed to Standard.

4. Summary

Moonshot AI's Kimi K3 is now available on Amazon Bedrock.

While models like Kimi K2.5 were also available via bedrock-mantle, Kimi K3 is only provided through bedrock-runtime, and can be called from four APIs: Converse, InvokeModel, Chat Completions, and Responses.
Existing code using the OpenAI SDK can be run as-is by simply swapping out three things for Bedrock: the base_url, model ID, and authentication method.

From the experience of actually running it, the following approach for choosing between implementations seems useful:

  1. If writing new code, choose the Chat Completions API (it is officially recommended, and you can also benefit from explicit prompt caching and service tiers)

  2. If reusing existing Converse code, build requests by removing reasoning blocks (reasoningContent) from previous turns for multi-turn conversations

Since reasoning runs at full capacity by default (effort: max), output tokens tend to grow even for short instructions.

I'm looking forward to the inference profile being expanded to Japan in the future.

References

Share this article

AWSのお困り事はクラスメソッドへ