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\
 23  provided by Moonshot AI. You are proficient in English conversations. You provide u\
 24 sers with safe, helpful, and accurate answers."}
 25                              {:role "user" :content prompt}]
 26                   :temperature 0.3}
 27             ;; Make the POST request
 28             response (client/post url {:headers headers
 29                                        :body    (json/write-str body)
 30                                        ;; Throw an exception for non-2xx response
 31                                        :throw-exceptions false})
 32             ;; Parse the JSON response body
 33             parsed-body (json/read-str (:body response) :key-fn keyword)]
 34 
 35         (if (= (:status response) 200)
 36           ;; Extract the content from the response using the -> (thread-first) macro
 37           ;; This is equivalent to:
 38           ;;   (get (get (first (get parsed-body :choices)) :message) :content)
 39           (-> parsed-body :choices first :message :content)
 40           ;; Handle potential errors from the API
 41           (str "Error: Received status " (:status response)
 42                ". Body: " (:body response))))
 43       (catch Exception e
 44         (str "An exception occurred: " (.getMessage e))))))
 45 
 46 (defn- chat
 47   "Sends a chat request to the Moonshot AI chat completions API with tool support."
 48   [messages]
 49   (let [url (str moonshot-base-url "/chat/completions")
 50         headers {"Authorization" (str "Bearer " moonshot-api-key)
 51                  "Content-Type" "application/json"}
 52         body {:model moonshot-model
 53               :messages messages
 54               :temperature 0.3
 55               :tools [{:type "builtin_function"
 56                        :function {:name "$web_search"}}]}
 57         response (client/post url {:headers headers
 58                                    :body    (json/write-str body)
 59                                    :throw-exceptions false})
 60         parsed-body (json/read-str (:body response) :key-fn keyword)]
 61     (if (= (:status response) 200)
 62       (-> parsed-body :choices first)
 63       (throw (Exception. (str "Error: Received status " (:status response)
 64                               ". Body: " (:body response)))))))
 65 
 66 (defn search
 67   "Performs a Kimi 2 completion with web search."
 68   [user-question]
 69   (if-not moonshot-api-key
 70     (println "Error: MOONSHOT_API_KEY environment variable not set.")
 71     (try
 72       (loop [messages [{:role "system" :content "You are Kimi an AI assistant who re\
 73 turns all answers in English."}
 74                        {:role "user" :content user-question}]]
 75         (let [choice (chat messages)
 76               finish-reason (:finish_reason choice)]
 77           (if (= finish-reason "tool_calls")
 78             (let [assistant-message (:message choice)
 79                   tool-calls (-> assistant-message :tool_calls)
 80                   tool-messages (map (fn [tool-call]
 81                                        (let [tool-call-name
 82                                              (-> tool-call :function :name)]
 83                                          (if (= tool-call-name "$web_search")
 84                                            (let [tool-call-args
 85                                                  (json/read-str
 86                                                    (-> tool-call
 87                                                        :function :arguments)
 88                                                    :key-fn keyword)]
 89                                              {:role "tool"
 90                                               :tool_call_id (:id tool-call)
 91                                               :name tool-call-name
 92                                               :content (json/write-str tool-call-arg\
 93 s)})
 94                                            (let [error-message
 95                                                  (str
 96                                                   "Error: unable to find tool by nam\
 97 e '"
 98                                                   tool-call-name "'")]
 99                                              {:role "tool"
100                                               :tool_call_id (:id tool-call)
101                                               :name tool-call-name
102                                               :content
103                                               (json/write-str error-message)}))))
104                                      tool-calls)]
105               (recur (concat messages [assistant-message] tool-messages)))
106             (-> choice :message :content))))
107       (catch Exception e
108         (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 walke\
3 d to the river")]
4   (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 gri\
 5 ef it carried.
 6 
 7 The grass was still wet with dew, and each step left a darker footprint, as though t\
 8 he earth itself were taking notes. When he reached the bank, the water was the color\
 9  of tarnished coins, sliding past without a sound. He knelt, cupped his hands, and l\
10 et the river run through them. The cold stung, but it was the first thing all mornin\
11 g that felt real.
12 
13 A dragonfly hovered, wings catching the early light like splinters of glass. It dipp\
14 ed once, skimming the surface, and he thought of his daughter’s laugh—bright, brief,\
15  skimming the surface of every room she had ever entered.
16 
17 He had come to scatter her ashes, but the plastic urn in his coat pocket might as we\
18 ll have been a brick. His fingers closed around it, then opened again. Not yet.
19 
20 Across the water, a willow leaned so low its branches combed the current. He imagine\
21 d the tree drinking her in, leaf by leaf, until every green blade carried a memory. \
22 That seemed better than surrendering her to the anonymous pull of the river.
23 
24  ...

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** skie\
 2 s.  
 3 - **Wind:** Southwest at 15-16 mph  
 4 - **Humidity:** 33 %  
 5 - **UV index:** 7 of 11 (high)  
 6 - **Rain chance:** 15 % with no measurable precipitation so far  
 7 
 8 Today’s high will reach about **77-78 °F** and tonight’s low will drop to around **5\
 9 2 °F**. There is a slight chance of an isolated shower or thunderstorm later in the \
10 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.