# Qwen3.8 Flash — /chat/completions

Qwen3.8 Flash chat API (Alibaba Cloud), the light, fast Qwen3.8 for batch jobs and high concurrency (classification, extraction, summaries, OCR). It always thinks before answering; tool calls, JSON output and image input. OpenAI SDK compatible.

**Endpoint:** `POST https://kovapi.com/v1/chat/completions`

## Quick start

Two steps: send a request, then stream the answer. Replace YOUR_API_KEY with your key and the commands below run as they are. The endpoint matches OpenAI Chat Completions, so existing OpenAI code only needs a new `base_url` and `model`.

**Step 1 · Send a request**

```
curl https://kovapi.com/v1/chat/completions \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model": "qwen3.8-flash", "messages": [{"role": "user", "content": "Explain photosynthesis in one sentence"}]}'
```

Response (HTTP 200)

```
{
  "id": "chatcmpl-7f3c9a1e2b4d6f8a",
  "object": "chat.completion",
  "created": 1791281400,
  "model": "qwen3.8-flash",
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": "Photosynthesis is how plants use sunlight to turn carbon dioxide and water into sugar, releasing oxygen.",
        "reasoning_content": "..."
      },
      "finish_reason": "stop"
    }
  ],
  "usage": {
    "prompt_tokens": 18,
    "completion_tokens": 186,
    "total_tokens": 204,
    "prompt_tokens_details": {"cached_tokens": 0},
    "completion_tokens_details": {"reasoning_tokens": 144}
  }
}
```

`message.content` is the answer and `message.reasoning_content` the thinking. `usage` holds the tokens this request is billed for: thinking tokens are part of `completion_tokens` and bill at the output price.

**Step 2 · Stream the answer (recommended)**

```
curl -N https://kovapi.com/v1/chat/completions \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model": "qwen3.8-flash", "stream": true, "messages": [{"role": "user", "content": "Explain photosynthesis in one sentence"}]}'
```

The stream is standard SSE: each chunk carries text in `choices[0].delta.content`, the last chunk carries `usage`, and it ends with `data: [DONE]`. Stream long answers and thinking models: a non-streaming request is cut off after about 10 minutes.

**Python (OpenAI SDK)**

```
from openai import OpenAI

client = OpenAI(base_url="https://kovapi.com/v1", api_key="YOUR_API_KEY")

# 1. One request, one answer
resp = client.chat.completions.create(
    model="qwen3.8-flash",
    messages=[{"role": "user", "content": "Explain photosynthesis in one sentence"}],
)
print(resp.choices[0].message.content)

# 2. Streaming
stream = client.chat.completions.create(
    model="qwen3.8-flash",
    messages=[{"role": "user", "content": "Explain photosynthesis in one sentence"}],
    stream=True,
)
for chunk in stream:
    if chunk.choices and chunk.choices[0].delta.content:
        print(chunk.choices[0].delta.content, end="", flush=True)
```

## Authentication
```
Authorization: Bearer YOUR_API_KEY
Content-Type: application/json
```

## At a glance

| Input | Text, images |
| --- | --- |
| Thinking | Always thinks before answering; the thinking comes back in `reasoning_content` |
| Tool calling | Yes (OpenAI `tools` format) |
| JSON output | Yes (`response_format`) |
| Web search | Yes (`web_search: true`; the search itself is free) |
| Endpoints | /v1/chat/completions |
| Streaming | Yes (`stream: true`, SSE) |
| Billing | Per token: input, output (thinking included) and cache hits each have a price; failures are free |

## Request parameters

The JSON body of `POST /v1/chat/completions`, the same as OpenAI Chat Completions. The common parameters, and what each one does on this model:

| Parameter | Type | Required | Description |
| --- | --- | --- | --- |
| model | string | Yes | The model id: `qwen3.8-flash`. |
| messages | array | Yes | The conversation: each message has a `role` (`system`, `user`, `assistant` or `tool`) and `content`. `content` is a string, or an array of `text` and `image_url` parts (image input, see below). For multi-turn chat, send the whole history in order. |
| stream | boolean | No | `true` streams the answer as SSE, with `usage` in the last chunk. Defaults to `false`. |
| max_tokens | integer | No | Accepted, but it may not hold the output down: the thinking is not limited by it, and billing follows the actual `usage`. Ask for a short answer in the prompt instead. |
| temperature / top_p | number | No | Sampling parameters, passed to the model as sent (`temperature` from 0 to 2). Thinking models may not follow them. |
| reasoning_effort | string | No | Has no effect: this model always thinks and decides how much. |
| tools / tool_choice | array / string | No | Function calling in the standard OpenAI format. When the model calls a function it returns `finish_reason: "tool_calls"` with `tool_calls`; see below. |
| response_format | object | No | `{"type": "json_object"}` makes the model return valid JSON only; also say in the prompt that you want JSON and which fields. |
| web_search | boolean | No | `true` runs a web search first and hands the results to the model as reference; see below. |

Other OpenAI parameters (such as `stop`, `seed`, `user`) are passed to the model as sent; whether they take effect is up to the model.

## Price

Billed per token at the live rates below (per million tokens). Only successful requests are billed, on actual usage: failures, timeouts and refusals are free. 1 credit = $0.005.

| Type | USD / 1M tokens | Credits / 1M tokens |
| --- | --- | --- |
| Input | $0.10 | 20 |
| Output (thinking included) | $0.33 | 66 |
| Cache hit | $0.012 | 2.4 |

For example, a request with 2,000 input tokens and 1,000 output tokens (thinking included) costs about $0.0005.

## Thinking

This model always thinks before it answers; the thinking comes back in `message.reasoning_content` (`delta.reasoning_content` when streaming). It cannot be turned off, and `reasoning_effort` and `thinking` have no effect.

Thinking tokens are part of `usage.completion_tokens` and bill at the output price (some models also break them out in `completion_tokens_details.reasoning_tokens`).

## Prompt caching

No parameter needed: a repeated long prefix (the same system prompt, the earlier turns of a conversation) may be cached by the model provider automatically, and the cached part bills at the lower cache price. Whether and how much is cached is up to the provider; keep the parts that never change at the start of the messages for the best hit rate.

The cached token count comes back in `usage.prompt_tokens_details.cached_tokens`; that part bills at the cache-hit price and the rest of the input at the input price.

## Image input

Put an `image_url` part in the `content` array. A base64 data URI (`data:image/png;base64,...`) is the recommended form and can always be read; a public image link has to be downloaded first, and occasionally it cannot be fetched or times out. Images count as input tokens by the model vendor rules.

```
curl https://kovapi.com/v1/chat/completions \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model": "qwen3.8-flash", "messages": [{"role": "user", "content": [{"type": "text", "text": "What is in this image"}, {"type": "image_url", "image_url": {"url": "data:image/png;base64,iVBORw0KGgo..."}}]}]}'
```

## Tool calling (function calling)

Describe your functions in `tools` with JSON Schema. When the model decides to call one, it returns `finish_reason: "tool_calls"` with the function name and arguments (a JSON string) in `message.tool_calls`.

```
curl https://kovapi.com/v1/chat/completions \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model": "qwen3.8-flash", "messages": [{"role": "user", "content": "What is the weather in Shanghai right now"}], "tools": [{"type": "function", "function": {"name": "get_weather", "description": "Current weather of a city", "parameters": {"type": "object", "properties": {"city": {"type": "string"}}, "required": ["city"]}}}]}'
```

Run the function, append the result as a `role: "tool"` message with the matching `tool_call_id`, and send the conversation again; the model then gives its final answer. `tool_choice` takes `auto`, `none`, `required` or a named function.

## Web search

Add `"web_search": true` to the request (or a `{"type": "web_search"}` entry in `tools`): the gateway first searches the live web based on the conversation, hands up to 5 results with their links to the model as reference, and the model answers and cites its sources. The search itself is free; the reference text bills as input tokens.

```
curl https://kovapi.com/v1/chat/completions \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model": "qwen3.8-flash", "web_search": true, "messages": [{"role": "user", "content": "What are the main tech news stories today"}]}'
```

## Reliability and automatic retries

- When a request errors, times out or hits a capacity limit, the gateway retries it automatically before anything is returned, so you need not retry and are never billed twice.

- Once a stream starts sending, it is no longer retried automatically: if it breaks partway, you are billed for the usage produced up to that point (see [Billing and refunds](https://docs.kovapi.com/en/guide/billing)).

- When this model cannot answer for a while, the gateway may have another model answer so the request does not fail: it bills at the lower of the two prices, and the call log says which model answered and which price applied.

- When the model is at capacity the gateway returns 429 with `Retry-After`; wait that many seconds and retry. Failed requests are not billed. Each model status is on [the status page](https://kovapi.com/en/status).

## Errors and refunds

Errors come back as an `error` object (`message`, `type`, `code`, `param`); decide by the HTTP status and `code`, never by matching the text of `message`. Failed requests are not billed; the full list is in [Error codes](https://docs.kovapi.com/en/guide/errors).

| HTTP | code | Cause | What to do |
| --- | --- | --- | --- |
| 400 | invalid_messages / invalid_role / invalid_max_tokens | A chat request's `messages` is missing, empty or malformed, a `role` is not one of the allowed values, or `max_tokens` is not a positive integer. `param` names the field. | Fix the field named in `param`. |
| 400 | model_not_found | The model id is misspelled, `model` is missing, or the model cannot be used on this endpoint (a chat model on the image endpoint, for example). | Use a model id from `GET /v1/models` or the [API reference](https://docs.kovapi.com/en/api). |
| 400 | max_tokens_too_small | A reasoning model spent all of `max_tokens` thinking and wrote no answer (non-streaming chat). | Raise `max_tokens` and retry. |
| 4xx | invalid_request | The model provider rejected the request (an over-long context or an unsupported parameter, for example); the status and `message` are passed on as the provider sent them, and `code` is the provider's value. | Change the request as `message` says. |
| 401 | invalid_api_key | The key does not exist, was deleted or is disabled, or the account is disabled. | Check the key on the API Keys page of the console. |
| 402 | insufficient_quota | Not enough balance to hold this request. The gateway holds the highest possible cost of the request first, so a burst of concurrent calls can trigger it too. | After a burst, wait a few seconds and retry; otherwise top up. |
| 403 | permission_denied | The model is not on the key's model allowlist. | Use a model on the allowlist, or edit the allowlist in the console. |
| 429 | rate_limit_exceeded | Over the requests-per-minute limit set on this key (counted over the last 60 seconds). No `Retry-After` header. | Wait a few seconds and retry, or raise the key's limit in the console. |
| 429 | rate_limit_exceeded | The model is at capacity and the request was not sent. Chat endpoints only: image and video jobs are never refused but queue instead. The OpenAI-compatible endpoints add `Retry-After`. | Wait the seconds in `Retry-After`, then retry. |
| 502 | upstream_timeout | The upstream timed out, including taking too long to send the first token. | Retry; use `stream: true` for long answers. |
| 502 | upstream_unavailable | The upstream failed or is unavailable for the moment. | Retry shortly. |
| 502 | upstream_busy | The model provider is rate-limiting. A provider's 429 is never passed on as a 429; you get this 502 instead. | Retry shortly. |
| 502 | empty_completion | The model provider returned empty content. This is not a judgement of your content. | Retry. |
| 503 | model_maintenance | The model is under maintenance and takes no requests; no other model answers in its place meanwhile. | Retry when maintenance ends; see [Status](https://kovapi.com/en/status). |

If a stream fails after it started sending, the status code stays as it was: the error arrives as a data line carrying `error`, or the stream just ends (without `data: [DONE]`). Treat it as interrupted.
