Using Moonshot’s Kimi K2 Model with Built In $web_search Tool Support
Moonshot AI (or “Dark Side of the Moon”) is a prominent Chinese artificial intelligence startup founded in March 2023. The company quickly achieved a multi-billion dollar valuation, notably securing a $1 billion funding round led by Alibaba. Founded by a team with strong academic roots from institutions like Tsinghua University and Carnegie Mellon, Moonshot AI has established itself as one of China’s leading “AI Tigers.” The company’s primary strategic focus is on developing large language models (LLMs) capable of handling exceptionally long context windows, aiming to push the boundaries of what AI can process and comprehend in a single prompt.
Their flagship model, Kimi K2, is a state-of-the-art, open-weight language model that has drawn significant attention. It is built on a Mixture-of-Experts (MoE) architecture with one trillion total parameters, of which 32 billion are active during inference, making it both powerful and efficient. Kimi K2 is specifically designed as an “agentic” AI, meaning it’s optimized not just for conversation but for performing complex, multi-step tasks, using tools, and executing code autonomously. With a 128k token context window and benchmark performance that rivals or exceeds leading proprietary models in coding and reasoning tasks, Kimi K2 represents a major milestone for open-source AI.
Kimi K2 has built in support for web searching, as the following code example shows. You will need a Moonshot API key from the Moonshot console https://login.moonshot.ai and the model documentation can be found here https://platform.moonshot.ai/docs. If you don’t want to use servers in China, the Kimi K2 model is open source and several US-based providers offer inference services.
Clojure Library Moonshot.ai’s Kimi K2 Model (Including Web Search Tool)
The following Clojure code defines a client for interacting with the Moonshot AI API, providing two primary functions for chat completions. The first, completions, performs a basic, single-turn request by sending a user prompt along with a predefined system message to the Kimi 2 model and returns the text content of the model’s response. The second, more advanced function, search, orchestrates a multi-turn, agentic conversation that leverages the model’s built-in web search tool; it initiates a loop that repeatedly calls a private chat helper function, checks if the model’s response is a request to use a tool (“tool_calls”), and if so, constructs and appends the necessary tool-related messages to the conversation history before continuing the loop until the model provides a final content-based answer. The entire namespace relies on the clj-http.client and clojure.data.json libraries for making HTTP POST requests and handling JSON serialization, respectively, while retrieving the required MOONSHOT_API_KEY from system environment variables and including basic error handling for failed API calls.
1 (ns moonshot-api.core
2 (:require [clj-http.client :as client]
3 [clojure.data.json :as json])
4 (:gen-class))
5
6 ;; Define the API key, base URL, and model for Moonshot AI
7 (def moonshot-api-key (System/getenv "MOONSHOT_API_KEY"))
8 (def moonshot-base-url "https://api.moonshot.ai/v1")
9 (def moonshot-model "kimi-k2-0711-preview")
10
11 (defn completions
12 "Sends promp request to the Moonshot AI chat completions API."
13 [prompt]
14 (if-not moonshot-api-key
15 (println "Error: MOONSHOT_API_KEY environment variable not set.")
16 (try
17 (let [url (str moonshot-base-url "/chat/completions")
18 headers {"Authorization" (str "Bearer " moonshot-api-key)
19 "Content-Type" "application/json"}
20 ;; Construct the request body to match the Python example
21 body {:model moonshot-model
22 :messages [{:role "system" :content "You are Kimi, an AI assistant provided by Moonshot AI. You are proficient in English conversations. You provide users with safe, helpful, and accurate answers."}
23 {:role "user" :content prompt}]
24 :temperature 0.3}
25 ;; Make the POST request
26 response (client/post url {:headers headers
27 :body (json/write-str body)
28 ;; Throw an exception for non-2xx response
29 :throw-exceptions false})
30 ;; Parse the JSON response body
31 parsed-body (json/read-str (:body response) :key-fn keyword)]
32
33 (if (= (:status response) 200)
34 ;; Extract the content from the response using the -> (thread-first) macro
35 ;; This is equivalent to:
36 ;; (get (get (first (get parsed-body :choices)) :message) :content)
37 (-> parsed-body :choices first :message :content)
38 ;; Handle potential errors from the API
39 (str "Error: Received status " (:status response)
40 ". Body: " (:body response))))
41 (catch Exception e
42 (str "An exception occurred: " (.getMessage e))))))
43
44 (defn- chat
45 "Sends a chat request to the Moonshot AI chat completions API with tool support."
46 [messages]
47 (let [url (str moonshot-base-url "/chat/completions")
48 headers {"Authorization" (str "Bearer " moonshot-api-key)
49 "Content-Type" "application/json"}
50 body {:model moonshot-model
51 :messages messages
52 :temperature 0.3
53 :tools [{:type "builtin_function"
54 :function {:name "$web_search"}}]}
55 response (client/post url {:headers headers
56 :body (json/write-str body)
57 :throw-exceptions false})
58 parsed-body (json/read-str (:body response) :key-fn keyword)]
59 (if (= (:status response) 200)
60 (-> parsed-body :choices first)
61 (throw (Exception. (str "Error: Received status " (:status response)
62 ". Body: " (:body response)))))))
63
64 (defn search
65 "Performs a Kimi 2 completion with web search."
66 [user-question]
67 (if-not moonshot-api-key
68 (println "Error: MOONSHOT_API_KEY environment variable not set.")
69 (try
70 (loop [messages [{:role "system" :content "You are Kimi an AI assistant who returns all answers in English."}
71 {:role "user" :content user-question}]]
72 (let [choice (chat messages)
73 finish-reason (:finish_reason choice)]
74 (if (= finish-reason "tool_calls")
75 (let [assistant-message (:message choice)
76 tool-calls (-> assistant-message :tool_calls)
77 tool-messages (map (fn [tool-call]
78 (let [tool-call-name
79 (-> tool-call :function :name)]
80 (if (= tool-call-name "$web_search")
81 (let [tool-call-args
82 (json/read-str
83 (-> tool-call
84 :function :arguments)
85 :key-fn keyword)]
86 {:role "tool"
87 :tool_call_id (:id tool-call)
88 :name tool-call-name
89 :content (json/write-str tool-call-args)})
90 (let [error-message
91 (str
92 "Error: unable to find tool by name '"
93 tool-call-name "'")]
94 {:role "tool"
95 :tool_call_id (:id tool-call)
96 :name tool-call-name
97 :content
98 (json/write-str error-message)}))))
99 tool-calls)]
100 (recur (concat messages [assistant-message] tool-messages)))
101 (-> choice :message :content))))
102 (catch Exception e
103 (str "An exception occurred: " (.getMessage e))))))
Test code for the Moonshot.ai Kimi K2 API Client Library
The test code for this library has two examples: a simple text completion augmented by the text completion with the web search tool. Here is the test code to simple text completion:
1 (let [results
2 (moonshot-api.core/completions "Write a story starting with the text: He walked to the river")]
3 (println results)
Here is a few lines of sample output:
1 $ lein test
2
3 lein test moonshot-api.core-test
4 He walked to the river because the house behind him had become too small for the grief it carried.
5
6 The grass was still wet with dew, and each step left a darker footprint, as though the earth itself were taking notes. When he reached the bank, the water was the color of tarnished coins, sliding past without a sound. He knelt, cupped his hands, and let the river run through them. The cold stung, but it was the first thing all morning that felt real.
7
8 A dragonfly hovered, wings catching the early light like splinters of glass. It dipped once, skimming the surface, and he thought of his daughter’s laugh—bright, brief, skimming the surface of every room she had ever entered.
9
10 He had come to scatter her ashes, but the plastic urn in his coat pocket might as well have been a brick. His fingers closed around it, then opened again. Not yet.
11
12 Across the water, a willow leaned so low its branches combed the current. He imagined the tree drinking her in, leaf by leaf, until every green blade carried a memory. That seemed better than surrendering her to the anonymous pull of the river.
13
14 ...
Here is example code for completion augmented with web search:
1 (let [results
2 (moonshot-api.core/search "Current weather in Flagstaff Arizona")]
3 (println results)
Here is some sample output:
1 Right now in Flagstaff, Arizona it is **76 °F (24 °C)** under **partly cloudy** skies.
2 - **Wind:** Southwest at 15-16 mph
3 - **Humidity:** 33 %
4 - **UV index:** 7 of 11 (high)
5 - **Rain chance:** 15 % with no measurable precipitation so far
6
7 Today’s high will reach about **77-78 °F** and tonight’s low will drop to around **52 °F**. There is a slight chance of an isolated shower or thunderstorm later in the day.
Wrap Up for Moonshot.ai Kimi K2 Model
The Kimi K2 model is very efficient making it one of the least expensive commercial APIs to use. The model is at the same time powerful and is excellent at tool use and as a software assitant. As I write this chapter on July 21, 2025 almost all of my commercial LLM API use is either Google Gemini 2.5 Flash and Pro, and the Kimi K2 model.
Optional Practice Problems
- Evaluation Logging: Write a script to evaluate prompt variations against the Moonshot API in
source-code/moonshotand log response characteristics. - Secure Key Loading: Modify the code to load the API key from a secure environment variable or a configuration file rather than hardcoding it.
- Translation Agent: Build a translation agent that takes English text and requests the Moonshot API to translate it into three target languages in parallel.