# Kimi K3.1 Pro Open Weights: When Self-Hosting Pays for Review Labels

> Kimi K3.1 Pro weights are 1.56 TB and need 8 B300 GPUs. On 1,130 live reviews at low reasoning, spot GPUs beat the API only above 131,595 reviews per run.

- Canonical page: https://everyinfra.com/en/blog/kimi-k3-1-pro-open-weights-review-classification
- Published: 2026-11-14T03:32:00+08:00 · Fact-checked: 2026-11-14T01:50:00+08:00

Moonshot AI launched Kimi K3.1 Pro on October 23, 2026. The weights went public on Hugging Face on November 4, 2026. The model has 2.8T total parameters and 104B active parameters per token. The context window is 1,048,576 tokens, and the weights fill 96 files with 1,561.3 GB in total. For app review labels, we priced two paths: rented GPUs with your own copy of the model, and the official API.

We pulled 1,130 real Duolingo reviews with the EveryInfra API on November 14, 2026, between 00:19 and 00:23 Singapore time. The sources were the App Store in three countries, Google Play in two languages, Trustpilot and Xiaohongshu. Then we counted tokens with the tokenizer file from the Kimi K3.1 Pro repository. Reviews are short: the median is 7 to 77 tokens. The label JSON for one review is 33.8 tokens. Thus about 80% of the model cost goes to output tokens.

We used public spot GPU prices and a third-party throughput test. In the low reasoning scenario (200 reasoning tokens per batch of 20 reviews), self-hosting beats the standard API only above 131,595 reviews per run. It beats the Batch API only above 569,464 reviews. The mid and high scenarios move the first line to 94,773 and 60,767 reviews.

We have no GPU here. The model commands, prompt and labelling script are configuration examples, and we did not measure model accuracy. We measured the data API calls and the token counts, and we read the original text of 51 keyword hits.

> **Key points**
>
> - Kimi K3.1 Pro weights went public on Hugging Face on 2026-11-04. The license is MIT-like with two commercial thresholds. The smallest official NVIDIA setup is one 8-GPU tensor-parallel group: one 8x B300 node or two GB300 trays.
> - The "local" question: llama.cpp merged K3.1 Pro text support on November 6, 2026. The smallest community GGUF (1-bit) is still about 580 GB, and Q2_K is about 1,009 GB.
> - On the K3.1 Pro tokenizer, one review in a request is about 54 input tokens and its label JSON is about 34 output tokens. The standard API costs about $0.55 to $1.45 per 1,000 reviews. Fetching 1,000 App Store reviews costs $0.0069.
> - A spot 8x B300 node ($49.61 per hour, $34.07 per start) beats the standard API only above 131,595 reviews per run in the low reasoning scenario (94,773 mid, 60,767 high). AWS Capacity Blocks ($129.168 per hour) cost more than the API in all three reasoning scenarios.
> - Keyword rules are weak for billing complaints. In the US App Store sample, 19 reviews matched billing keywords. We read each hit, and only 4 were real billing complaints. No second person checked this read.
> - Send only 1-star to 3-star reviews to the model. In the US and China stores, all 18 billing complaints that we found had 1 to 3 stars. For the US store, the low-scenario model cost per 1,000 raw reviews drops from $0.5498 to below $0.16. You lose the issues in 4-star and 5-star reviews.

![Animated flow: on the left, source boxes App Store, Google Play and Trustpilot plus Xiaohongshu feed an arrow into "EveryInfra, POST /api/v1/social"; then "deduplicate by id, keep id, stars and text"; then a teal box "Kimi K3.1 Pro: self-hosted vLLM or official API / Batch" with a note on strict JSON response_format; then "label table: billing, ads, pressure, content, ai, bug, praise, other". A small dot moves along the arrows in a loop.](https://everyinfra.com/articles/kimi-k3-1-pro-open-weights-review-classification/fig-01-review-pipeline-flow.en.light.svg)

## What the Kimi K3.1 Pro open weights release contains

The hosted model and the weights came in two steps. The Kimi [technical blog](https://www.kimi.ai/blog/kimi-k3-1-pro) gives October 23, 2026 as the launch date on kimi.ai, the app and the developer platform. K3.1 Pro Highspeed launched on the same day and runs two to three times faster. The weights repository is [moonshotai/Kimi-K3.1-Pro](https://huggingface.co/moonshotai/Kimi-K3.1-Pro). The public Hugging Face API shows that it was created on November 2 and made public on November 4. The last commit is from November 5, 2026, and it updates `encoding_k3.py`.

We read the repository at 00:15 Singapore time on November 14, 2026. It had 112 files. Its 96 safetensors files added up to 1,561.3 GB. It had 40,953 downloads and 2,003 likes. Those numbers change every day, so treat them as a snapshot.

The model card and `config.json` give the architecture. It is a mixture-of-experts model with 2.8T total parameters and 104B active parameters per token. It has 93 layers: 69 use Kimi Delta Attention and 24 use gated sparse attention. Each layer has 896 routed experts, and the router picks 16 of them.

Two shared experts also run for every token. The vocabulary has 163,840 entries. The model reads text and images through a 401M-parameter MoonViT-V2 vision encoder.

The weights ship in MXFP4, with MXFP8 activations. Moonshot trained the model with quantization-aware training at this precision. So the official repository has no larger BF16 copy. The MX "microscaling" formats come from the [OCP MX paper on arXiv](https://arxiv.org/abs/2310.10537). NVIDIA describes its own NVFP4 format on its [developer blog](https://developer.nvidia.com/blog/introducing-nvfp4-for-efficient-and-accurate-low-precision-inference/).

The license file is the Kimi K3.1-Pro License ([full text](https://huggingface.co/moonshotai/Kimi-K3.1-Pro/blob/main/LICENSE)). The base is the MIT license, with two added conditions. First, a model-as-a-service business with more than $20 million revenue in the last 12 months needs a separate agreement. Second, a product with more than 100 million monthly active users or $20 million monthly revenue must show "Kimi K3.1-Pro" in a visible place. These two thresholds target public model services and very large products. To know if your use falls inside them, read the LICENSE text and ask your legal team.

| Item | Kimi K3.1 Pro value | Source |
| --- | --- | --- |
| Hosted launch | 2026-10-23, plus K3.1 Pro Highspeed | Kimi technical blog |
| Weights public | 2026-11-04 (repo created 11-02, last commit 11-05) | Hugging Face API |
| Parameters | 2.8T total, 104B active, 93 layers, 16 of 896 experts plus 2 shared | Model card, config.json |
| Context | 1,048,576 tokens, text and image input | Model card |
| Precision and size | MXFP4 weights, MXFP8 activations, 96 files, 1,561.3 GB | File list |
| License | MIT-like, with revenue and user thresholds | LICENSE |
| Engines | vLLM, SGLang, TokenSpeed | Model card |
| Smallest NVIDIA setup | One TP8 group: one 8x B300 node or two GB300 trays | vLLM recipe |
| Official API price | Per 1M tokens: $2.00 input, $10.00 output, $0.20 cached input | Kimi pricing page |

![Screenshot: two parts of the Model Summary table on the Kimi-K3.1-Pro model card, with teal boxes around four rows: Total Parameters 2.8T, Activated Parameters 104B, Context Length 1M, and Quantization MXFP4 weights / MXFP8 activations; a dashed line separates the two parts.](https://everyinfra.com/articles/kimi-k3-1-pro-open-weights-review-classification/img-shot-hf-model-card.webp)

The parameter table on the Kimi-K3.1-Pro model card (screenshot, read on 2026-11-14) [Kimi-K3.1-Pro model card on Hugging Face](https://huggingface.co/moonshotai/Kimi-K3.1-Pro)

Outside benchmarks are positive. In its [launch review](https://artificialanalysis.ai/articles/kimi-k3-1-pro-launch-benchmarks), Artificial Analysis gave K3.1 Pro an Intelligence Index score of 55, in fourth place. More hosted options also appeared. AWS published a post on November 10, 2026 (Pacific time) about [K3.1 Pro on Amazon Bedrock](https://aws.amazon.com/blogs/machine-learning/introducing-kimi-k3-1-pro-on-amazon-bedrock/). For review labels, check the price, the throughput and a stable output format.

## Tool calls and structured output: XTML tags and the kimi_k3 parser

Inside the model, K3.1 Pro uses its own tags for reasoning, answers and tool calls. The rules are in [`encoding_k3.py`](https://huggingface.co/moonshotai/Kimi-K3.1-Pro/blob/main/encoding_k3.py) in the repository. Tokens such as `<|open|>`, `<|close|>` and `<|sep|>` wrap sections named think, response, tools, call and argument. A turn ends with `<|end_of_msg|>`. You do not write these tags yourself when you host the model.

Both vLLM and SGLang have parsers for this format. In vLLM, the files are [`kimi_k3_tool_parser.py`](https://github.com/vllm-project/vllm/blob/main/vllm/tool_parsers/kimi_k3_tool_parser.py) and [`kimi_k3_reasoning_parser.py`](https://github.com/vllm-project/vllm/blob/main/vllm/reasoning/kimi_k3_reasoning_parser.py). In SGLang, the file is [`kimik3_detector.py`](https://github.com/sgl-project/sglang/blob/main/python/sglang/srt/function_call/kimik3_detector.py). Start the server with `--tool-call-parser kimi_k3 --reasoning-parser kimi_k3 --enable-auto-tool-choice`. The response then has OpenAI-style `tool_calls` and a separate `reasoning_content` field.

One detail can cost you an afternoon. On November 14, 2026, the general vLLM [tool calling guide](https://docs.vllm.ai/en/latest/features/tool_calling.html) still listed only `kimi_k2` for Kimi models. The SGLang [tool parser list](https://docs.sglang.io/advanced_features/tool_parser.html) was the same. If you follow those general pages, you can pick the wrong parser. The raw tags then stay in the answer text. For K3.1 Pro, use the [vLLM recipe](https://recipes.vllm.ai/moonshotai/Kimi-K3.1-Pro) and the [SGLang cookbook](https://docs.sglang.io/cookbook/autoregressive/Moonshotai/Kimi-K3.1-Pro).

The commit history of the vLLM parser shows a change on November 4, 2026. That change scans streamed tool calls incrementally for speed. If your container image is older, upgrade it before you test tool calls. Also keep the image tag in your run log, so that you can explain a change in behaviour later.

The official API rules are in the [K3.1 Pro quickstart](https://platform.kimi.ai/docs/guide/kimi-k3-1-pro-quickstart). Four of them matter for labels. First, the model always reasons before it answers. The `reasoning_effort` field accepts low, high or max, and the default is max ([reasoning effort guide](https://platform.kimi.ai/docs/guide/use-reasoning-effort)). Low is enough for labels, and each higher level adds output tokens and cost.

Second, the sampling settings are fixed at temperature 1.0 and top_p 0.95. Other values have no effect. So the same review can get a different label in a second run. Third, structured output supports `response_format` with a `json_schema`. With `strict` set to true, the output follows the schema ([structured output guide](https://platform.kimi.ai/docs/guide/response_format)). Parse only `content` and ignore `reasoning_content` in your parser.

Fourth, in a multi-turn tool loop, send back the full assistant message from the previous turn. That includes `reasoning_content` and `tool_calls`. If one part is missing, the next turn can fail or drift. For a self-hosted server, the vLLM pages on [structured outputs](https://docs.vllm.ai/en/latest/features/structured_outputs.html) and [reasoning outputs](https://docs.vllm.ai/en/latest/features/reasoning_outputs.html) describe the same fields.

We checked one more point in [BL-67](/en/blog/kimi-k3-1-pro-sentiment-earnings-research). None of the three hosted Kimi APIs accepts a remote MCP server. A self-hosted vLLM server does not run tools either. The model only emits the call, and your code runs it.

So the tool definition below is for interactive use, where the model decides when to fetch reviews. For batch labels, fetch the reviews first and then send them to the model. This is cheaper: it removes one request round and one copy of the tool definition.

## Kimi K3.1 Pro local: how big a machine do you need?

We searched YouTube for "kimi k3.1 pro open weights". The fifth result is a video from November 6, 2026. Its title says that K3.1 gave away frontier-level AI for free. The transcript says that the download is free and that you only pay for power.

It does not estimate the hardware. The comments are more concrete. One viewer wrote that a home setup costs about $1 million, against $7,200 for three years of a paid plan. Another wrote that it needs 2 TB of video memory.

On Reddit, a LocalLLaMA thread on the [open-weights release](https://www.reddit.com/r/LocalLLaMA/comments/1xnrhvb/kimik31pro_is_now_openweights/) had another estimate. One commenter priced four Mac Studio machines with 512 GB each at about $89,200. These claims are far apart. So we checked them against the file sizes and the official guides.

The official path uses data-center GPUs only. The vLLM recipe gives one 8-GPU tensor-parallel group as the simplest NVIDIA setup. That is one 8x B300 node or two GB300 trays. On AMD, it is one 8x MI355X node. On Google TPU, it is 16 v7x chips.

The SGLang cookbook gives "balanced" layouts for older GPUs. B200 needs TP16, which is two 8-GPU nodes. H200 needs TP16 with EP16. H100 needs TP32 with EP32, which is four 8-GPU nodes.

The 1,561.3 GB of weights must fit in GPU memory, with room left for the KV cache. The vLLM recipe gives the KV room on 8x B300 at TP8 with an FP8 KV cache. It holds about 2.6 million tokens. NVIDIA published an [NVFP4 version](https://huggingface.co/nvidia/Kimi-K3.1-Pro-NVFP4) for Blackwell on November 11, 2026. Its files add up to 1,605.2 GB, which is slightly larger than the official weights. So NVFP4 is for Blackwell kernels, not for less memory.

The "local" path moved forward in the second week of November. On November 6, 2026, llama.cpp merged [K3.1 Pro text model support](https://github.com/ggml-org/llama.cpp/pull/31779). The pull request says that the author tested an MXFP4 conversion and a 2-bit to 3-bit quant. On November 10, llama.cpp merged the [vision part](https://github.com/ggml-org/llama.cpp/pull/31980). Community members then uploaded GGUF files to Hugging Face.

At 00:47 Singapore time on November 14, we used the [Hugging Face model search API](https://huggingface.co/api/models?search=Kimi-K3.1-Pro). We added up the file sizes of the most downloaded GGUF repositories. The smallest IQ1_S quant is about 580 to 593 GB. IQ2_XXS is about 710 GB.

Q2_K is about 1,009 GB. MXFP4_MOE is about 1,509 GB. Some repositories have "12GB" in their names. That size cannot hold a 2.8T model, so do not download them.

A Reddit thread in the unsloth community asked about [K3.1 quants](https://www.reddit.com/r/unsloth/comments/1xq9wgo/any_plans_for_kimi_k31/). When we wrote this, the official Moonshot account had no GGUF release. All GGUF files were community uploads with no published quality tests.

| Setup | File size | Machine | Fit for review labels |
| --- | --- | --- | --- |
| Official MXFP4, vLLM or SGLang | 1,561.3 GB | 8x B300, two GB300 trays, 8x MI355X or 16 TPU v7x chips | Yes. Official path with tool and JSON parsers |
| NVIDIA NVFP4 | 1,605.2 GB | One Blackwell TP8 group | Yes. Kernels for Blackwell |
| Community GGUF IQ1_S | About 580–593 GB | Several machines with more than 600 GB memory in total | 1-bit quant, no public quality or tool-call tests |
| Community GGUF Q2_K | About 1,009 GB | Two or more machines with 512 GB memory each | Same, and speed depends on memory bandwidth |
| Official API or Batch | No download | None | Yes, with per-token prices |

For labels, most of the memory goes to the weights. The KV cache stays almost empty. In our batches, one request has about 1,500 input tokens. With output and reasoning, that is two or three thousand tokens.

Even 64 requests at the same time use less than 200,000 tokens of KV cache. That is far below the 2.6 million tokens that 8x B300 can hold at TP8. So plain TP8 is enough, and more concurrency gives more speed.

The Spheron test log on the same setup shows 248,210 of 275,040 MiB in use per GPU. Of that, 34.74 GiB per GPU is KV cache, which holds 2,151,492 tokens for the node. That is less than the recipe figure, but it is still large for review labels. In the low scenario, one request is about 2,344 tokens (1,469 input and 875 output). 64 concurrent requests use about 150,000 tokens.

On the API side, K3.1 Pro Highspeed costs twice the standard price: $4 input and $20 output per 1M tokens. Batch labelling is not urgent, so use the standard model or the Batch API.

A home computer or a single workstation cannot hold K3.1 Pro. Even the smallest 1-bit quant is larger than one 512 GB Mac Studio. To host it yourself, you rent a cloud node such as 8x B300, or you own a similar server room. The cost model below uses rented nodes.

![Explanatory illustration: on the left, a tall server rack with eight GPU trays and a few lights on; on the right, a balance scale with only one small review card on one pan, which shows that reviews are light and the model machine is heavy.](https://everyinfra.com/articles/kimi-k3-1-pro-open-weights-review-classification/img-explain-rack.webp)

The reviews are small and the machine is large, so the cost depends on how many reviews you label in one run.

## Where the reviews come from: App Store, Google Play and Trustpilot

We used three EveryInfra capabilities for the English-language data: [App Store reviews](/en/app-store-api/reviews), [Google Play reviews](/en/google-play-api/reviews) and [Trustpilot reviews](/en/trustpilot-api/reviews). For a Chinese comparison, we also used [Xiaohongshu comments](/en/xiaohongshu-api/comments). All requests go to `POST https://api.everyinfra.com/api/v1/social`. The `platform` and `action` fields select the capability. The key goes in a request header, and the [quickstart](/en/docs#quickstart) shows the setup.

We chose Duolingo as the sample app. It has many public reviews in the US, Mexico and China stores and on Trustpilot. Many reviews talk about the paid plan and the in-app "energy" system. That gives clear test cases for billing and pressure labels.

Each store also has an official review API, but it only covers your own app. Apple's [App Store Connect API](https://developer.apple.com/documentation/appstoreconnectapi/get-v1-apps-_id_-customerreviews) needs a developer account key. Google's [Reply to Reviews API](https://developers.google.com/android-publisher/reply-to-reviews) needs a service account, and reads are limited to 200 per hour. It returns only reviews with text. Trustpilot's [Business Units API](https://developers.trustpilot.com/business-units-api) is also for businesses that manage their own profile.

To compare competitors, or to put public reviews from several stores in one table, a pay-per-call API is simpler. The parameters and prices are in the daily catalog for [App Store](https://api.everyinfra.com/api/v1/social/catalog?platform=app_store), [Google Play](https://api.everyinfra.com/api/v1/social/catalog?platform=google_play) and [Trustpilot](https://api.everyinfra.com/api/v1/social/catalog?platform=trustpilot). The Chinese platform is in the [Xiaohongshu catalog](https://api.everyinfra.com/api/v1/social/catalog?platform=xiaohongshu).

```bash
# Fetch the newest 200 US App Store reviews for Duolingo. The key comes from an environment variable.
curl -s https://api.everyinfra.com/api/v1/social \
  -H "Authorization: Bearer $EVERYINFRA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"platform": "app_store", "action": "reviews",
       "params": {"app_id": "570060128", "country": "us", "sort": "newest", "limit": 200}}'
```

The first round ran on November 14, 2026, from 00:19 to 00:23 Singapore time. The nine calls in this round cost $0.025002 in total. Trustpilot returned a 429 error. That call was not billed, and a retry worked.

Each App Store, Google Play and Trustpilot call costs $0.001389. In this run, each App Store and Google Play call returned 200 reviews, and the Trustpilot call returned 100. A Xiaohongshu comment call costs $0.005556, and each page in this run returned 15 comments.

| Singapore time | Capability | Target | Rows | Time | Charge |
| --- | --- | --- | --- | --- | --- |
| 00:19 | `app_store/reviews` | US store, newest | 200 | 1.87 s | $0.001389 |
| 00:19 | `google_play/reviews` | English, newest | 200 | 6.81 s | $0.001389 |
| 00:21 | `trustpilot/reviews` | duolingo.com, newest | 100 | 18.67 s | $0.001389 |
| 00:21 | `xiaohongshu/comments` | One Duolingo warning note, page 1, most liked | 15 | 2.56 s | $0.005556 |
| 00:22 | `app_store/reviews` | China store, newest | 200 | 1.62 s | $0.001389 |
| 00:22 | `app_store/reviews` | Mexico store, newest | 200 | 1.58 s | $0.001389 |
| 00:23 | `google_play/reviews` | Spanish, newest | 200 | 9.30 s | $0.001389 |

![Screenshot: a real request and response in terminal style. The request is app_store/reviews sent at 00:22:38 Singapore time on 2026-11-14, with app_id 570060128, country cn, sort newest and limit 200; the response is HTTP 200 in 1.62 seconds. Teal boxes mark 200 results, the rating and posted_at of the first review, and charged_usd 0.001389 at the bottom; the author field is masked.](https://everyinfra.com/articles/kimi-k3-1-pro-open-weights-review-classification/img-shot-ei-response.webp)

A real App Store reviews response: 200 rows for one $0.001389 call [EveryInfra API response](https://api.everyinfra.com/api/v1/social)

The review speed differs a lot between stores. That speed sets how often you must fetch. We used the oldest and newest timestamps in each batch of 200 newest reviews. Google Play in English covered only 3.21 hours, which is about 1,493 reviews per day. Google Play in Spanish covered 12.14 hours, or about 395 per day. The US App Store covered 17.88 hours, or about 269 per day.

The China App Store covered 74.02 hours, or about 65 per day. The Mexico App Store covered 158.08 hours, or about 30 per day. So Google Play in English needs a fetch every two or three hours to miss nothing. For the Mexico store, one fetch per day is more than enough.

![Horizontal bar chart of the hours covered by the newest 200 reviews: Google Play English 3.2 hours (about 1,493 per day, teal), Google Play Spanish 12.1 hours (about 395 per day), App Store US 17.9 hours (about 269), App Store China 74.0 hours (about 65), App Store Mexico 158.1 hours (about 30).](https://everyinfra.com/articles/kimi-k3-1-pro-open-weights-review-classification/fig-06-review-window-bar.en.light.svg)

## Field differences and a table design for labels

The stores return slightly different fields, so map them before you store anything. App Store reviews have an id, title, text, rating, author, app version, helpful count and post time. Google Play reviews have no title. Their rating field is `score`, and they add thumbs-up counts and the app version. Trustpilot adds the review language, the country, a verified flag and the experience date. Its title is often the first half of the text with an ellipsis.

The model only needs three fields: the id, the stars and the text. If the stars are missing, send a null value. The prompt does not need a special rule for it.

The app version field also helps. A rise in bug labels after a release often shows up before the star rating drops.

## How many tokens is one review? We used the K3.1 Pro tokenizer

To price the model side, you need token counts. A word count gives only a rough guess. The K3.1 Pro repository has its own tokenizer file, `tiktoken.model`, and its split rule in `tokenization_kimi.py`. We loaded both with [tiktoken](https://github.com/openai/tiktoken) (the [PyPI package](https://pypi.org/project/tiktoken/)). Then we counted the title and text of all 1,130 reviews. This step needs no GPU and takes less than one second.

Google Play English reviews have a median of 7 tokens and a 90th percentile of 26. US App Store reviews have a median of 27.5 tokens and a 90th percentile of 86. Mexico App Store reviews have a median of 26. Trustpilot reviews are the longest, with a median of 77 tokens and a 90th percentile of 179. Many Trustpilot users write there to complain, and they give the full story.

English text averages 4.12 characters per token. Chinese App Store text averages 1.76 characters per token. So the same number of characters costs more than twice as many tokens in Chinese.

![Horizontal bar chart of tokens per review on the Kimi K3.1 Pro tokenizer; dark bars show the median and light bars extend to the 90th percentile: App Store US 27.5 / 86, App Store China 15 / 40, App Store Mexico 26 / 77, Google Play English 7 / 26, Google Play Spanish 11.5 / 39, Trustpilot 77 / 179, Xiaohongshu comments 10 / 24; a vertical line marks 33.8 tokens for the label JSON of one review.](https://everyinfra.com/articles/kimi-k3-1-pro-open-weights-review-classification/fig-03-review-tokens-bar.en.light.svg)

| Source | Rows | Median tokens | 90th percentile | Characters per token | Fetch cost per 1,000 |
| --- | --- | --- | --- | --- | --- |
| App Store US | 200 | 27.5 | 86 | 4.12 | $0.0069 |
| App Store China | 200 | 15 | 40 | 1.76 | $0.0069 |
| App Store Mexico | 200 | 26 | 77 | 3.11 | $0.0069 |
| Google Play English | 200 | 7 | 26 | — | $0.0069 |
| Google Play Spanish | 200 | 11.5 | 39 | — | $0.0069 |
| Trustpilot | 100 | 77 | 179 | — | $0.0139 |
| Xiaohongshu comments | 30 | 10 | 24 | 1.54 | $0.3704 |

Next, we counted the tokens inside a real request. We used batches of 20 reviews and sent only the id, stars and text. The user message then averages 54.15 tokens per review. The system prompt is 223 tokens and the JSON schema is 163 tokens.

So each request carries a fixed 386 tokens. The label JSON averages 33.76 tokens per review. That is longer than a typical Google Play English review.

So output tokens make up most of the model cost. [BL-116](/en/blog/deepseek-v4-1-flash-review-cleaning-tagging) ran DeepSeek V4.1 Flash on real reviews and found the same pattern.

## The labelling prompt, the JSON schema and the tool definition

We set eight labels from the problems that Duolingo reviews actually mention. The labels are billing, ads, pressure, content, ai, bug, praise and other. "Pressure" covers energy, hearts, gems, streaks, leaderboards and upsell screens. Each review gets one primary label, up to two secondary labels, a sentiment value and a reply flag.

| Label | What it covers | Example from the US sample |
| --- | --- | --- |
| billing | Charges, renewals, refunds, trials that became payments, plan prices | An extra $135 charge every month after an upgrade |
| ads | Ads inside the app | Ads for the paid plan inside lessons |
| pressure | Energy, hearts, gems, streaks, leaderboards, upsell screens | "Everything costs energy" |
| content | Course quality, accuracy, missing languages or levels | Many new vocabulary-only lessons in a row |
| ai | Content that the reviewer says is AI-made | Wrong AI pronunciation in Japanese lessons |
| bug | Crashes, freezes, login, sync, lost progress | None among the 19 keyword hits |
| praise | Positive review with no specific problem | Short five-star praise |
| other | Anything else | Feedback on the social feed |

The prompt is in English because the input mixes English, Spanish and Chinese. English label names are also easier to store. The output does not repeat the review text. That saves tokens and keeps user words out of the label table.

```text
You label app-store and community reviews for a product team.
For every review in the user message, return one object in "items", in the same order.
Use only these labels:
- billing: charges, renewals, refunds, free trials that turned into payments, subscription plans and their price
- ads: advertising inside the product
- pressure: energy, hearts, gems, streaks, leaderboards, upsell screens or notifications that push the user to pay or come back
- content: course or content quality, accuracy, missing languages or levels
- ai: features or content that the reviewer says is made by AI
- bug: crashes, freezes, login, sync, lost progress, performance
- praise: positive review with no specific problem
- other: anything else
Pick one primary label and up to two secondary labels.
sentiment: -1 negative, 0 mixed or neutral, 1 positive.
reply_needed: true only when the reviewer reports money lost, data lost, or a blocked account.
Copy the review id exactly. Do not translate or quote the review text.
```

A strict JSON schema in `response_format` controls the output. The enum keeps the model inside the eight labels. It cannot invent a new label such as "subscription_issue". The `additionalProperties: false` setting blocks extra fields. The official API and vLLM both accept this form. See the Kimi [structured output guide](https://platform.kimi.ai/docs/guide/response_format) and the OpenAI [function calling guide](https://platform.openai.com/docs/guides/function-calling).

```text
{
  "type": "json_schema",
  "json_schema": {
    "name": "review_labels",
    "strict": true,
    "schema": {
      "type": "object",
      "additionalProperties": false,
      "required": ["items"],
      "properties": {
        "items": {
          "type": "array",
          "items": {
            "type": "object",
            "additionalProperties": false,
            "required": ["id", "primary", "secondary", "sentiment", "reply_needed"],
            "properties": {
              "id": {"type": "string"},
              "primary": {"type": "string", "enum": ["billing", "ads", "pressure", "content", "ai", "bug", "praise", "other"]},
              "secondary": {"type": "array", "maxItems": 2, "items": {"type": "string", "enum": ["billing", "ads", "pressure", "content", "ai", "bug", "praise", "other"]}},
              "sentiment": {"type": "integer", "enum": [-1, 0, 1]},
              "reply_needed": {"type": "boolean"}
            }
          }
        }
      }
    }
  }
}
```

For interactive use, give the model a tool. An example is a chat bot that answers "What do this week's US Duolingo complaints say?". The model then decides which store and which country to fetch. The tool definition uses the OpenAI function format.

The `limit` field is an integer with a minimum and a maximum. The `source` field is an enum. These limits stop the model from sending a number as a string or naming a store that does not exist. On the K3.1 Pro tokenizer, this definition is 203 tokens, and every request in the loop carries it.

```text
{
  "type": "function",
  "function": {
    "name": "fetch_reviews",
    "description": "Fetch the newest public reviews of one app or company from EveryInfra. Returns a JSON list with id, rating, text, posted_at. Billed per call by EveryInfra; failed or empty calls are not billed.",
    "parameters": {
      "type": "object",
      "additionalProperties": false,
      "required": ["source", "target"],
      "properties": {
        "source": {"type": "string", "enum": ["app_store", "google_play", "trustpilot"], "description": "Where the reviews come from."},
        "target": {"type": "string", "description": "App Store numeric id, Google Play package name, or company domain for Trustpilot."},
        "country": {"type": "string", "description": "Two-letter store country for app_store, for example us, cn, mx."},
        "language": {"type": "string", "description": "Two-letter review language for google_play and trustpilot."},
        "limit": {"type": "integer", "minimum": 1, "maximum": 200, "description": "Number of reviews."}
      }
    }
  }
}
```

Batch labelling does not need this tool. In a tool loop, each batch of 20 reviews needs one extra round trip. Each round also carries the 203-token tool definition and one more reasoning step. Your code already knows which store and how many reviews to fetch. If the model makes that choice, you pay more and get more errors.

![Explanatory illustration: a conveyor belt carries review cards into a sorting machine, which drops them into eight small trays, one per label; one extra tray holds the cards that need a human reply.](https://everyinfra.com/articles/kimi-k3-1-pro-open-weights-review-classification/img-explain-sort.webp)

Each review gets one primary label and up to two secondary labels, and the enum keeps the labels to eight.

## Why keyword rules fail on billing: we read every hit

Before the model runs, a keyword rule costs almost nothing and gives a first estimate. Our billing rule covers English, Spanish and Chinese words, such as subscri, charg, refund, trial, suscrip, cobr and reembols. Google Play English had only 2 hits in 200 reviews (1.0%). Google Play Spanish had 5 hits.

The Mexico App Store had 7 and the US App Store had 19. Trustpilot had 46 hits in 100 reviews. On Trustpilot, 87% of the reviews have one or two stars, because people go there to complain.

![Horizontal bar chart of the share of reviews that match billing keywords: Google Play English 1.0%, Google Play Spanish 2.5%, App Store Mexico 3.5%, App Store US 9.5%, App Store China 12.5%, Trustpilot 46%, Xiaohongshu comments 50% (30 comments, teal); each row also shows the share of low-star reviews.](https://everyinfra.com/articles/kimi-k3-1-pro-open-weights-review-classification/fig-07-billing-share-bar.en.light.svg)

A keyword hit is not a billing complaint. We read the original text of all 51 hits in the US, China and Mexico stores. Then we sorted each hit into the four groups in the table below. No second person checked this sort.

The sample has limits. It is the keyword hits in the newest 200 reviews of each store, from one fetch on November 14. That is 19 hits in the US, 25 in China and 7 in Mexico. The review ids are in our raw files, and the shares below describe this batch only.

In the US store, only 4 of the 19 hits talk about a charge, a refund or a trial that renewed into a paid plan. One reviewer wrote that an upgrade to Max added a $135 charge every month. Another got a charge after a trial and could not get a refund.

Eight of the 19 hits are about the energy and gem system, which we label as pressure. Reviewers wrote that each lesson costs energy and that "everything costs energy". Some called it "pay to win". The other 7 hits have nothing to do with billing.

"Battery charge thing" is the in-app energy bar. "Get the money" is about a user who saves money for a trip to France. One reviewer praised the family plan as "not too much money".

| Store | Keyword hits | Billing complaints (after reading) | Pressure or price | Not billing |
| --- | --- | --- | --- | --- |
| App Store US | 19 | 4 | 8 | 7 |
| App Store China | 25 | 14 | 4 | 7 |
| App Store Mexico | 7 | 0 | 6 | 1 |

In these 51 hits, only about 21% of the US keyword hits (4 of 19) are real billing complaints. A support team that routes all keyword hits as billing tickets will get mostly false alarms. In the China store, 14 of 25 hits are real billing complaints. The Mexico store had no billing complaint among its 7 hits. A model can make this semantic split, but you must measure its accuracy on your own sample.

Use this method. Label 200 reviews by hand, then run the model twice on them. Count how often the two model runs agree. Then count how often the model agrees with the human labels. K3.1 Pro uses a fixed temperature of 1.0, so the run-to-run agreement is a number you must measure.

This batch also shows a way to cut cost: send only 1-star to 3-star reviews to the model. The 18 billing complaints that we found (4 in the US, 14 in China) all have 1 to 3 stars, and 17 of them have one star. Low-star reviews are also longer. On the K3.1 Pro tokenizer, the median US review with 1 to 3 stars has 54 tokens. The median with 4 or 5 stars has 23.

In the US sample, 50 of 200 reviews have 1 to 3 stars. That is 25% of the reviews but 45.6% of the review tokens. In the low scenario, the reasoning tokens, the fixed prompt and the label JSON follow the review count, so multiply them by 25%. The input cost of the review text follows the tokens, so multiply it by 45.6% at most. Together, the US cost per 1,000 raw reviews drops from $0.5498 to below $0.16.

The cost of this filter is that you do not see issues in high-star reviews. In the US, 2 of the 8 pressure hits have 4 or 5 stars. On Google Play Spanish, one 4-star review says that the app charged the user after a free trial week. We tested this rule on this batch only, so check it again for another app or another time period.

| Source | Reviews with 1 to 3 stars | Share of reviews | Share of text tokens | Median tokens (1–3 stars) | Median tokens (4–5 stars) |
| --- | --- | --- | --- | --- | --- |
| App Store US | 50 / 200 | 25.0% | 45.6% | 54 | 23 |
| App Store China | 76 / 200 | 38.0% | 52.1% | 20.5 | 11 |
| App Store Mexico | 35 / 200 | 17.5% | 28.7% | 46 | 23 |
| Google Play English | 25 / 200 | 12.5% | 14.6% | 11 | 7 |
| Google Play Spanish | 12 / 200 | 6.0% | 19.5% | 43 | 11 |
| Trustpilot | 96 / 100 | 96.0% | 98.5% | 77.5 | 21.5 |

We counted the title and text of each review with the K3.1 Pro tokenizer. Google Play Spanish has only 6% low-star reviews, but they hold almost a fifth of the text tokens, so the filter saves the most there. On Trustpilot, 96 of 100 reviews have 1 to 3 stars, so the filter saves almost nothing.

## What US reviewers and Trustpilot users complain about

The US App Store and Trustpilot samples tell different stories about the same app. In the US App Store sample, 19.5% of the 200 reviews have one or two stars. Most complaints are about the energy system. Each lesson uses energy, and an empty bar means you wait, watch an ad or pay. Several reviewers also wrote that more lessons now feel AI-made and that the pronunciation is wrong.

Trustpilot is different. Of the 100 reviews, 87% have one or two stars and 70 have one star. The median review is 77 tokens long. Reviewers describe the refund and cancel steps in detail.

One reviewer got a charge of almost 150 euros after a free trial and still had no refund. Another found the trial hard to cancel. These long reviews are good candidates for the `reply_needed` flag.

| Store | Low-star share | Main complaint in the sample |
| --- | --- | --- |
| App Store US | 19.5% | Energy system and paywalls |
| Trustpilot | 87% | Refunds, trial charges and cancel steps |
| Google Play English | 8.5% | Few complaints, many one-line five-star reviews |

Google Play English is the quietest source for billing. Only 1.0% of its 200 newest reviews match the billing keywords, and only 8.5% have low stars. Many reviews there are one short line of praise. So a single store is a poor guide to the whole picture. Fetch the stores where your users write, and label them with the same prompt.

## The batch script: from fetch to labels

The script below has three steps. It fetches reviews from EveryInfra and removes duplicates by id. It builds batches of 20. Then it sends each batch to an OpenAI-compatible endpoint for labels.

We ran the fetch part. The labelling part is a configuration example. Set `LLM_BASE_URL` to your vLLM server or to the Kimi API, and the code stays the same.

The script uses [`concurrent.futures`](https://docs.python.org/3/library/concurrent.futures.html) from the Python standard library for concurrency. It also checks that the ids in the answer match the ids in the batch. That check catches a missing or swapped review.

```text
import json, os, concurrent.futures as cf
import requests

EI = "https://api.everyinfra.com/api/v1/social"
EI_KEY = os.environ["EVERYINFRA_API_KEY"]
BASE_URL = os.environ.get("LLM_BASE_URL", "http://localhost:8000/v1")  # self-hosted vLLM, or the Kimi API URL
LLM_KEY = os.environ.get("LLM_API_KEY", "EMPTY")
MODEL = os.environ.get("LLM_MODEL", "moonshotai/Kimi-K3.1-Pro")
SYSTEM = open("prompt_system.txt").read()
SCHEMA = json.load(open("schema.json"))

def fetch(platform, params):
    r = requests.post(EI, json={"platform": platform, "action": "reviews", "params": params},
                      headers={"Authorization": f"Bearer {EI_KEY}"}, timeout=120)
    r.raise_for_status()
    return r.json()["results"]

def label(batch):
    user = json.dumps([{"id": str(x["id"]), "stars": x.get("rating") or x.get("score"),
                        "text": ((x.get("title") or "") + " " + (x.get("text") or "")).strip()}
                       for x in batch], ensure_ascii=False, separators=(",", ":"))
    body = {"model": MODEL, "reasoning_effort": "low", "response_format": SCHEMA,
            "messages": [{"role": "system", "content": SYSTEM}, {"role": "user", "content": user}]}
    r = requests.post(f"{BASE_URL}/chat/completions", json=body,
                      headers={"Authorization": f"Bearer {LLM_KEY}"}, timeout=600)
    r.raise_for_status()
    items = json.loads(r.json()["choices"][0]["message"]["content"])["items"]  # parse content only
    want, got = [str(x["id"]) for x in batch], [i["id"] for i in items]
    if want != got:
        raise ValueError(f"id mismatch: missing {set(want) - set(got)}, extra {set(got) - set(want)}")
    return items

seen, rows = set(), []
for platform, params in [("app_store", {"app_id": "570060128", "country": "us", "sort": "newest", "limit": 200}),
                         ("google_play", {"app_id": "com.duolingo", "language": "en", "sort": "newest", "limit": 200}),
                         ("trustpilot", {"company": "duolingo.com", "sort": "newest", "limit": 100})]:
    for x in fetch(platform, params):
        key = (platform, str(x["id"]))
        if key not in seen:
            seen.add(key)
            rows.append(x)

batches = [rows[i:i + 20] for i in range(0, len(rows), 20)]
with cf.ThreadPoolExecutor(max_workers=16) as pool, open("labels.jsonl", "w") as out:
    for items in pool.map(label, batches):
        for it in items:
            out.write(json.dumps(it, ensure_ascii=False) + "\n")
```

For self-hosting, the start command below uses the flags from the vLLM recipe. It is a configuration example for one 8x B300 node. The `--max-num-seqs 256` flag comes from the Spheron test log of November 8, 2026. The vLLM default of 1,024 sequences is more than the 603 slots of the K3.1 Pro linear-attention cache on B300. With the default, the server fails at graph capture during start-up. Use the command that the recipe page generates for your hardware, and use an image with K3.1 support. The source code is in [vllm-project/vllm](https://github.com/vllm-project/vllm) and [sgl-project/sglang](https://github.com/sgl-project/sglang).

```bash
# Configuration example: one 8x B300 node, tensor parallel 8, kimi_k3 parsers for tool calls and reasoning
vllm serve moonshotai/Kimi-K3.1-Pro \
  --tensor-parallel-size 8 \
  --tool-call-parser kimi_k3 \
  --reasoning-parser kimi_k3 \
  --enable-auto-tool-choice \
  --max-num-seqs 256
```

If you can wait for results, the official API has a cheaper path: the [Batch API](https://platform.kimi.ai/docs/guide/use-batch-api). Write each request body as one JSONL line, upload the file and set `completion_window` to 24h. The [Batch pricing page](https://platform.kimi.ai/docs/pricing/batch) gives 60% of the standard price. Cache hits in Batch are best-effort only, so our cost model assumes no cache.

The format matches the OpenAI [Batch API](https://platform.openai.com/docs/guides/batch). Each line has a `custom_id`, and you use it to join the labels back to the reviews. For account limits on concurrency and requests, see the Kimi [rate limits page](https://platform.kimi.ai/docs/pricing/limits).

## Two bills: the data fetch and the labels

The first bill is the data API. We calculate the cost per 1,000 from the rows that each call returned in this run: charge ÷ rows × 1,000. App Store and Google Play calls cost $0.001389 and returned 200 reviews, which is $0.0069 per 1,000. The Trustpilot call has the same price and returned 100 reviews, which is $0.0139 per 1,000. A Xiaohongshu comment call costs $0.005556 and returned a page of 15, which is $0.3704 per 1,000. Failed calls and empty results are not billed.

The second bill is the model. The Kimi [chat pricing page](https://platform.kimi.ai/docs/pricing/chat) gives these prices per 1M tokens: $2.00 input, $10.00 output and $0.20 for cached input. The page says the rates were checked on October 24, 2026, and we read it on November 14. We priced the fixed 386 prompt tokens at the cached rate. Review text uses the input rate. Label JSON and reasoning tokens use the output rate.

We cannot measure reasoning tokens without a GPU or a paid run. So we used three assumptions per batch of 20 reviews: 200, 800 and 2,000 reasoning tokens.

| Reasoning per batch of 20 | Standard API per 1,000 | Batch per 1,000 | Spot node per 1,000 (no start fee) | AWS Capacity Block per 1,000 |
| --- | --- | --- | --- | --- |
| Low: 200 tokens | $0.5498 | $0.3507 | $0.2909 | $0.7574 |
| Mid: 800 tokens | $0.8498 | $0.5307 | $0.4903 | $1.2766 |
| High: 2,000 tokens | $1.4498 | $0.8907 | $0.8891 | $2.3150 |

In the low scenario, the standard API costs $0.5498 per 1,000 reviews. Input is only 20.4% of that cost, and the rest is output. So cut output first. Replace long ids with a short index inside the batch, and use one- or two-letter field names. The label JSON then drops from 33.76 to 22.15 tokens per review. That saves 21.1%, 13.7% and 8.0% in the low, mid and high scenarios.

Longer reasoning makes this saving smaller. Compare the label cost with the fetch cost as well. Fetching 1,000 App Store reviews for $0.0069 is only 1.3% of the low-scenario label cost.

![Horizontal bar chart on a log scale of cost per 1,000 reviews in US dollars: fetch App Store US $0.0069, Google Play English $0.0069, Trustpilot $0.0139, Xiaohongshu comments $0.3704; labels with the Kimi K3.1 Pro standard API $0.5498 (teal) and Batch $0.3507. A note says reasoning is 200 tokens per batch of 20 reviews.](https://everyinfra.com/articles/kimi-k3-1-pro-open-weights-review-classification/fig-04-fetch-vs-label-cost-bar.en.light.svg)

## How many reviews before self-hosting pays off?

We priced self-hosting with one rented 8x B300 node. The throughput comes from a public [Spheron test](https://www.spheron.network/blog/kimi-k3-1-vs-deepseek-v4-1-b300/) of November 8, 2026. On a spot 8x B300 node with vLLM and 64 concurrent requests, the node produced 2,073.2 output tokens per second. One request alone got 101.1 tokens per second.

The spot price was $49.61 per hour. The setup time, from boot and a 1,561.3 GB download to the first request, cost $34.07 in rent. That figure is from the second start in the test.

For on-demand rental, we used AWS. On the [EC2 Capacity Blocks price list](https://aws.amazon.com/ec2/capacityblocks/pricing/), p6-b300.48xlarge (8x B300) costs $129.168 per hour in most regions. That is $16.146 per GPU-hour. You book Capacity Blocks for a fixed time in advance, as the [AWS guide](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-capacity-blocks.html) explains.

For a long-term lease, we used the [FlexiCloud](https://flexicloud.co/gpu) price of $36 per hour for a full node, with a one-year minimum. At 730 hours per month, that is $26,280. The Akash Network [B300 price summary](https://akash.network/the-bid/b300-rental-price-2026-cost-per-hour/) gives a median of $8.31 per GPU-hour, or about $66.48 per node-hour. That falls between spot and Capacity Blocks. We read all cloud prices on November 14, 2026.

With the measured throughput, one node labels about 170,545 reviews per hour in the low scenario. It labels about 101,183 in the mid scenario and 55,797 in the high scenario. The formula is node output tokens per second × 3,600 ÷ output tokens per review. In the low scenario, one review needs the 33.76 tokens of its label JSON plus 10 reasoning tokens (200 per batch of 20).

Each spot start costs $34.07 first. After that, the marginal cost per 1,000 reviews is lower than the standard API. In the low scenario it is $49.61 ÷ 170,545 × 1,000 = $0.2909. So a break-even point exists: the start fee divided by the saving per review, or $34.07 ÷ ($0.5498 − $0.2909) × 1,000.

In the low scenario, a spot node beats the standard API above 131,595 reviews per run. The mid scenario needs 94,773 reviews and the high scenario needs 60,767. Batch is much harder to beat. The low scenario needs 569,464 reviews and the mid scenario needs 842,936. The high scenario needs 21.3 million, because spot at $0.8891 per 1,000 almost equals Batch at $0.8907. AWS Capacity Blocks cost more than both the API and Batch in all three scenarios.

This line leaves out a prefill difference. In the Spheron test, each request had 1,000 input tokens and 1,000 output tokens. In the low scenario, one of our batches has about 1,469 input tokens (386 fixed prompt tokens plus 20 reviews) and about 875 output tokens (20 label JSON objects plus 200 reasoning tokens). Our share of input is higher. So the node can produce fewer than 2,073.2 output tokens per second, and the break-even point moves up. The vLLM prefix cache can skip the fixed 386 tokens and make the gap smaller.

The spot price also moves. During the 2 hours 49 minutes of the Spheron test, the spot price moved from $56.20 to $46.71 per hour. The $49.61 is the average price paid. The setup time is about 41 minutes ($34.07 ÷ $49.61 per hour). With that time and the low-scenario throughput, the break-even point is about 116,000 reviews at $46.71 and about 175,000 reviews at $56.20.

A run of 131,595 reviews needs only about 46 minutes of node time. So the setup time and the run time are almost equal, and a smaller job pays a larger share for setup. In that test, the first start failed with the default flags. With that failed start, the time from the first launch to the first answer was 39 minutes 35 seconds. If the spot node stops and you start again, you pay the setup cost again.

![Log-log line chart; the x-axis is reviews per job from 10,000 to 10 million and the y-axis is total cost in US dollars. Four lines: standard API, Batch API (60%), self-hosted 8x B300 spot at $49.61 per hour including a $34.07 start fee (teal), and self-hosted AWS Capacity Block at $129.168 per hour. The spot line crosses the standard API line at 131,595 reviews, which is marked, and the Batch line at 569,464 reviews. Reasoning is 200 tokens per batch of 20.](https://everyinfra.com/articles/kimi-k3-1-pro-open-weights-review-classification/fig-05-breakeven-line.en.light.svg)

Now compare these numbers with the real Duolingo volume. The five store samples add up to about 2,252 reviews per day. You need about 58 days to collect 131,595 reviews. One month brings about 67,560 reviews. With the standard API, the model cost is about $37 in the low scenario and about $98 in the high scenario. The fetch cost is about $0.47 at 200 reviews per call.

A one-year lease costs $26,280 per month. To match the standard API, you must label about 47.8 million reviews per month in the low scenario. That is about 700 times Duolingo's monthly volume in these five stores. One node can label at most about 124 million reviews per month in the low scenario. For most brands, the API or Batch is cheaper for review labels.

The good reasons to self-host are elsewhere. Your data must stay in your own server room. Or you want to fine-tune the model. Or the GPUs already run other work, and review labels fill idle time.

| Option | Fixed cost per month | Low scenario: beats standard API above | Low scenario: beats Batch above | Max reviews per node per month |
| --- | --- | --- | --- | --- |
| Spot node, start and stop | None, $34.07 per start | 131,595 reviews per run | 569,464 reviews per run | Depends on rented hours |
| One-year lease ($36 per hour) | $26,280 | 47.8 million reviews per month | 74.9 million reviews per month | 124 million (low scenario) |
| AWS Capacity Block ($129.168 per hour) | Depends on booked hours | Never | Never | Depends on booked hours |

The model has three simplifications. First, the throughput comes from another team's test. That test used a different ratio of input to output tokens. The prefill paragraph above gives the direction of this error.

Second, the model leaves out storage, egress traffic and engineer time. Third, the reasoning token counts are assumptions. Our cost script is a short Python file. Change a few constants and it uses your own volumes and prices.

## The monthly bill at four volumes

The table below uses the low reasoning scenario and one run per month. The spot node includes one $34.07 start. The fetch cost uses App Store calls of 200 reviews at $0.001389.

| Reviews per month | Fetch cost | Standard API | Batch | Spot node (one start) | Node run time |
| --- | --- | --- | --- | --- | --- |
| 10,000 | $0.07 | $5.50 | $3.51 | $36.98 | About 0.06 hours |
| 100,000 | $0.69 | $54.98 | $35.07 | $63.16 | About 0.6 hours |
| 1,000,000 | $6.95 | $549.80 | $350.70 | $324.97 | About 5.9 hours |
| 10,000,000 | $69.45 | $5,498 | $3,507 | $2,943 | About 58.6 hours |

At 10,000 reviews per month, the start fee alone is larger than a month of API cost. At 100,000 reviews, Batch is still the cheapest option. At 1 million reviews, the spot node wins for the first time, with about 5.9 hours of run time. At 10 million reviews, the spot node costs $2,943 against $3,507 for Batch. The fetch cost stays a small part of the bill at every volume.

Batch size also changes the bill. We used 20 reviews per batch as a compromise. The fixed 386 prompt tokens then add about 19 tokens per review. Larger batches spread the prompt more thinly. But each answer must then align more ids, and the risk of a missing or swapped review goes up. A failed batch also costs more to run again.

With the standard API, the prompt uses the cached price, so batch size has little effect. With Batch, the cache is best-effort, so larger batches save a little input cost. On a self-hosted node, concurrency drives throughput. The Spheron test reached 2,073.2 output tokens per second at 64 concurrent requests, but one request got only 101.1. So run many requests in parallel to use the full node.

At 64 concurrent requests, each request got only 34.2 output tokens per second, and the first token took 1.6 seconds on average. One low-scenario batch has about 875 output tokens, so it takes about 26 seconds. Write results per batch, so that a failure only repeats one batch.

## Incremental fetches: dedupe by review id, cut by timestamp

A full fetch every day pulls the same review many times, and the model labels it many times. An incremental fetch uses two keys: the review id and the post timestamp. The example below uses three real Google Play English responses. We sent the requests at 00:19, 00:44 and 00:58 Singapore time on November 14, 2026.

In the first response, the newest review has the `posted_at` value `2026-11-12T15:48:41.180000+00:00`. Store it as the watermark. In the second response, 36 reviews are newer than the watermark. The oldest of them is at `15:52:15` and the newest is at `16:29:55`. A compare by id gives the same 36 new reviews, so both methods agree.

The third request uses `16:29:55.686` from the second response as the watermark. 27 reviews are newer, and a compare by id also gives 27. The second and third responses still share 164 and 173 reviews with the response before them. So no review was lost between the requests.

Use both keys together. A timestamp is fast for a cut such as "everything after the last fetch". But two reviews can have the same second. So compare with "greater than or equal", then remove the ids that you already have.

Timestamps also use different formats per store. App Store values carry an offset such as `-07:00`, and Google Play values are in UTC. Convert all of them to UTC before you compare. For example, the newest US review in the first response is `2026-11-12T04:31:43-07:00`, which is 11:31:43 UTC.

The id is the last dedupe step. Use "platform plus id" as the key. App Store ids are numbers, Google Play ids are UUIDs and Trustpilot ids are 24 hex characters. They do not collide, but the platform name makes a mixed table safer.

Set the fetch interval from the review speed, with a margin of one half. One response holds 200 reviews. The time that it covers is 200 divided by the reviews per hour. Fetch at half that time. Then a sudden rise in reviews still leaves some ids that match the last fetch.

| Store | Hours covered by 200 newest | Reviews per day | Suggested fetch interval |
| --- | --- | --- | --- |
| Google Play English | 3.21 | About 1,493 | Every 1.5 hours |
| Google Play Spanish | 12.14 | About 395 | Every 6 hours |
| App Store US | 17.88 | About 269 | Every 8 hours |
| App Store China | 74.02 | About 65 | Once per day |
| App Store Mexico | 158.08 | About 30 | Once per day |

After you start, watch how many ids each response shares with the last one. If the overlap drops to 0, the interval is too long and you can lose reviews, so fetch more often. If the overlap stays above 190, you can make the interval longer. Across the three rounds, 11 review calls to the stores and Trustpilot cost $0.015279.

Send only new ids to the model, and do not label old reviews again. For scheduled jobs and calls that retry, use the REST API and send an `Idempotency-Key` header. [Our article on the Idempotency-Key header](/en/blog/api-idempotency-key-ai-agent-retry) shows the method.

## How to test before go-live

Write your pass criteria before you run anything on your own server or the API. We suggest three steps, and each uses a check from outside the model. First, take 200 reviews from each store and have two people label them on their own. Use the labels that both people agree on as the reference. Discuss the others and fix the label definitions.

Second, run the model twice on the same batch. K3.1 Pro has fixed sampling settings, so you cannot lower the temperature. The share of reviews with the same primary label in both runs shows how stable the prompt is. If that share is too low, make the label definitions tighter or add a few examples.

Third, read the usage field in each response. Record the input, output and reasoning tokens per batch. Put the real numbers into the cost script and see where the break-even point moves.

| Check | How to measure it | Who sets the limit |
| --- | --- | --- |
| Agreement with the human reference | Primary label matches on the 200-review sample | Product or support lead |
| Missed billing complaints | Billing reviews without `reply_needed` set | Support lead |
| Run-to-run agreement | Same primary label in two model runs | Data owner |
| Real cost per 1,000 reviews | Usage tokens times the price list | Budget owner |

Make the criteria concrete. Set a limit for agreement with the human reference and for missed billing complaints in `reply_needed`. Also set a limit for run-to-run disagreement and for the real cost per 1,000 reviews. Check each one after the run.

A missed billing complaint costs the most, so you can require a high billing recall. A mix-up between praise and other costs little.

To measure recall, you need enough billing complaints in the reference. In this batch, we found 4 in 200 US reviews (2%) and 14 in 200 China reviews (7%). For 30 billing complaints in the reference, label about 1,500 US reviews or about 430 China reviews. You can also use the keyword rule to pick more candidates for labels. Then label a separate sample of non-hits to see how many complaints the keywords miss.

## Limits and things to watch

First, we did not measure model labels. There is no GPU here. The prompt, schema, start command and labelling script are configuration examples. Measure accuracy on your own setup with a human-labelled sample.

Second, the reasoning token counts are assumptions. K3.1 Pro always reasons first. Run a few batches with `reasoning_effort` set to low, then put the real counts into the cost script.

Third, throughput and cloud prices change often. A spot node can stop at any time. Long jobs must restart from the last saved batch. Write results per batch and skip batches whose ids are already done.

Fourth, the data is public. The reviews come from public store pages, and our API does not log in or read private content. Drop author names and avatars before storage, because the labels do not need them.

Fifth, the two license thresholds target public model services and very large products. The first section gives them. Before commercial use, check your use against the original text. Sixth, Xiaohongshu comments come in pages of 15, and the price per row is about 53 times the App Store price. That sample suits a qualitative comparison, not a full count.

Seventh, pin your versions. The weights repository changed an encoding file on November 5, 2026. The vLLM parser changed its streaming scan on November 4. Use the vLLM `--revision` flag to pin the commit that you tested. Also pin the image tag, and store both values in the label table. When the model repository changes, run the human sample again before you switch.

More reading: [Kimi hosted API for Weibo comments and earnings](/en/blog/kimi-k3-1-pro-sentiment-earnings-research) came out before the weights release. [DeepSeek V4.1 Flash for review cleaning and tags](/en/blog/deepseek-v4-1-flash-review-cleaning-tagging) ran a hosted API on 10,000 reviews. Compare its bill with this self-hosting model. [Tencent Hy4 function calling](/en/blog/tencent-hy4-function-calling-social-data) covers tool call formats and parameter types. [VS Code Copilot labels app store complaints every day](/en/blog/vs-code-copilot-cloud-automations-app-review-triage) runs a similar job on a schedule. The platform pages are [App Store API](/en/app-store-api), [Google Play API](/en/google-play-api) and [Trustpilot API](/en/trustpilot-api).

## FAQ

### Can I use the Kimi K3.1 Pro open weights commercially?

This comes from the Reddit LocalLLaMA thread "Kimi-K3.1-Pro is now open-weights", where the license got the most debate. The base is MIT with two added conditions. A model-as-a-service business with more than $20 million revenue in 12 months needs a separate agreement. A product with more than 100 million monthly users or $20 million monthly revenue must show "Kimi K3.1-Pro". To know if a use falls inside these conditions, read the [LICENSE file](https://huggingface.co/moonshotai/Kimi-K3.1-Pro/blob/main/LICENSE) and ask your legal team.

### Is there a vLLM example for Kimi K3.1 Pro?

This comes from the Google autocomplete phrase "kimi k3.1 pro vllm example". The vLLM [recipe](https://recipes.vllm.ai/moonshotai/Kimi-K3.1-Pro) gives flags and presets per hardware type. The vLLM [blog post of November 4, 2026](https://vllm.ai/blog/2026-11-04-kimi-k31-pro) covers day-zero support. Use the `kimi_k3` parsers, because the general tool calling guide still lists only `kimi_k2`. You can adapt the start command and the batch script above. Change the model name and the endpoint URL.

### Will there be a GGUF for Kimi K3.1?

This comes from the Reddit unsloth thread "Any plans for kimi K3.1?". When we wrote this, Hugging Face had several community GGUF uploads, from IQ1_S to MXFP4_MOE. They range from about 580 GB to about 1,509 GB. llama.cpp merged the text model on November 6, 2026 and the vision part on November 10. The official account had no GGUF yet. Repositories with "12GB" in the name do not match the parameter count, so do not download them.

## References

1. Kimi K3.1 Pro technical blog, Kimi, 2026-10-23, https://www.kimi.ai/blog/kimi-k3-1-pro
1. moonshotai/Kimi-K3.1-Pro model card, Hugging Face, last commit 2026-11-05, https://huggingface.co/moonshotai/Kimi-K3.1-Pro
1. Kimi K3.1-Pro License, Hugging Face, https://huggingface.co/moonshotai/Kimi-K3.1-Pro/blob/main/LICENSE
1. encoding_k3.py, Hugging Face, 2026-11-05, https://huggingface.co/moonshotai/Kimi-K3.1-Pro/blob/main/encoding_k3.py
1. Kimi K3.1 Pro quickstart, Kimi platform, https://platform.kimi.ai/docs/guide/kimi-k3-1-pro-quickstart
1. Chat model pricing, Kimi platform, https://platform.kimi.ai/docs/pricing/chat
1. Kimi K3.1 Pro pricing, Kimi, https://www.kimi.ai/resources/kimi-k3-1-pro-pricing
1. Batch pricing, Kimi platform, https://platform.kimi.ai/docs/pricing/batch
1. Use the Batch API, Kimi platform, https://platform.kimi.ai/docs/guide/use-batch-api
1. Structured output (response_format), Kimi platform, https://platform.kimi.ai/docs/guide/response_format
1. Reasoning effort, Kimi platform, https://platform.kimi.ai/docs/guide/use-reasoning-effort
1. Rate limits, Kimi platform, https://platform.kimi.ai/docs/pricing/limits
1. moonshotai/Kimi-K3.1-Pro recipe, vLLM Recipes, https://recipes.vllm.ai/moonshotai/Kimi-K3.1-Pro
1. Kimi K3.1 Pro day-zero support, vLLM blog, 2026-11-04, https://vllm.ai/blog/2026-11-04-kimi-k31-pro
1. Kimi-K3.1-Pro cookbook, SGLang docs, https://docs.sglang.io/cookbook/autoregressive/Moonshotai/Kimi-K3.1-Pro
1. Tool parser, SGLang docs, https://docs.sglang.io/advanced_features/tool_parser.html
1. Tool calling, vLLM docs, https://docs.vllm.ai/en/latest/features/tool_calling.html
1. Structured outputs, vLLM docs, https://docs.vllm.ai/en/latest/features/structured_outputs.html
1. Reasoning outputs, vLLM docs, https://docs.vllm.ai/en/latest/features/reasoning_outputs.html
1. kimi_k3_tool_parser.py, vLLM, commit of 2026-11-04, https://github.com/vllm-project/vllm/blob/main/vllm/tool_parsers/kimi_k3_tool_parser.py
1. kimi_k3_reasoning_parser.py, vLLM, https://github.com/vllm-project/vllm/blob/main/vllm/reasoning/kimi_k3_reasoning_parser.py
1. kimik3_detector.py, SGLang, https://github.com/sgl-project/sglang/blob/main/python/sglang/srt/function_call/kimik3_detector.py
1. vllm-project/vllm, GitHub, https://github.com/vllm-project/vllm
1. sgl-project/sglang, GitHub, https://github.com/sgl-project/sglang
1. model: add Kimi-K3.1-Pro text model (#31779), llama.cpp, merged 2026-11-06, https://github.com/ggml-org/llama.cpp/pull/31779
1. mtmd: add kimi K3.1 vision (#31980), llama.cpp, merged 2026-11-10, https://github.com/ggml-org/llama.cpp/pull/31980
1. Hugging Face model search API (Kimi-K3.1-Pro), read 2026-11-14, https://huggingface.co/api/models?search=Kimi-K3.1-Pro
1. nvidia/Kimi-K3.1-Pro-NVFP4, Hugging Face, 2026-11-11, https://huggingface.co/nvidia/Kimi-K3.1-Pro-NVFP4
1. Introducing NVFP4 for efficient low-precision inference, NVIDIA developer blog, https://developer.nvidia.com/blog/introducing-nvfp4-for-efficient-and-accurate-low-precision-inference/
1. Microscaling Data Formats for Deep Learning, arXiv, 2023, https://arxiv.org/abs/2310.10537
1. tiktoken, OpenAI, GitHub, https://github.com/openai/tiktoken
1. tiktoken, PyPI, https://pypi.org/project/tiktoken/
1. Kimi K3.1 vs DeepSeek V4.1 on B300, Spheron, 2026-11-08, https://www.spheron.network/blog/kimi-k3-1-vs-deepseek-v4-1-b300/
1. Amazon EC2 Capacity Blocks for ML pricing, AWS, read 2026-11-14, https://aws.amazon.com/ec2/capacityblocks/pricing/
1. Capacity Blocks for ML, AWS EC2 guide, https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-capacity-blocks.html
1. GPU pricing, FlexiCloud, read 2026-11-14, https://flexicloud.co/gpu
1. B300 rental price 2026, Akash Network, https://akash.network/the-bid/b300-rental-price-2026-cost-per-hour/
1. Kimi K3.1 Pro launch benchmarks, Artificial Analysis, https://artificialanalysis.ai/articles/kimi-k3-1-pro-launch-benchmarks
1. Introducing Kimi K3.1 Pro on Amazon Bedrock, AWS Machine Learning blog, 2026-11-10, https://aws.amazon.com/blogs/machine-learning/introducing-kimi-k3-1-pro-on-amazon-bedrock/
1. Kimi-K3.1-Pro is now open-weights, Reddit r/LocalLLaMA, https://www.reddit.com/r/LocalLLaMA/comments/1xnrhvb/kimik31pro_is_now_openweights/
1. Any plans for kimi K3.1?, Reddit r/unsloth, https://www.reddit.com/r/unsloth/comments/1xq9wgo/any_plans_for_kimi_k31/
1. Video: Kimi K3.1 Just Gave Away Opus-Level AI for Free, YouTube, 2026-11-06, https://www.youtube.com/watch?v=o7HxqdTm9Jo
1. List all customer reviews for an app, Apple developer docs, https://developer.apple.com/documentation/appstoreconnectapi/get-v1-apps-_id_-customerreviews
1. Reply to Reviews, Google Play developer docs, https://developers.google.com/android-publisher/reply-to-reviews
1. Business Units API, Trustpilot developer docs, https://developers.trustpilot.com/business-units-api
1. Function calling, OpenAI platform docs, https://platform.openai.com/docs/guides/function-calling
1. Batch API, OpenAI platform docs, https://platform.openai.com/docs/guides/batch
1. concurrent.futures, Python docs, https://docs.python.org/3/library/concurrent.futures.html
1. App Store capability catalog, EveryInfra, read 2026-11-14, https://api.everyinfra.com/api/v1/social/catalog?platform=app_store
1. Google Play capability catalog, EveryInfra, read 2026-11-14, https://api.everyinfra.com/api/v1/social/catalog?platform=google_play
1. Trustpilot capability catalog, EveryInfra, read 2026-11-14, https://api.everyinfra.com/api/v1/social/catalog?platform=trustpilot
1. Xiaohongshu capability catalog, EveryInfra, read 2026-11-14, https://api.everyinfra.com/api/v1/social/catalog?platform=xiaohongshu
1. EveryInfra social data API, https://api.everyinfra.com/api/v1/social
