qwen3.8-max Integration Guide
curl --request POST \
--url https://api.apimart.ai/v1/chat/completionsimport requests
url = "https://api.apimart.ai/v1/chat/completions"
response = requests.post(url)
print(response.text)const options = {method: 'POST'};
fetch('https://api.apimart.ai/v1/chat/completions', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.apimart.ai/v1/chat/completions",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.apimart.ai/v1/chat/completions"
req, _ := http.NewRequest("POST", url, nil)
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.apimart.ai/v1/chat/completions")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.apimart.ai/v1/chat/completions")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
response = http.request(request)
puts response.read_bodyqwen3.8-max
qwen3.8-max Integration Guide
- OpenAI-compatible: chat/completions and Responses
- Built-in tools only on Responses; thinking cannot be disabled
- Implicit/explicit context cache; PDF on chat endpoint only
- Billing = token fees + per-call tool fees
POST
/
v1
/
chat
/
completions
qwen3.8-max Integration Guide
curl --request POST \
--url https://api.apimart.ai/v1/chat/completionsimport requests
url = "https://api.apimart.ai/v1/chat/completions"
response = requests.post(url)
print(response.text)const options = {method: 'POST'};
fetch('https://api.apimart.ai/v1/chat/completions', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.apimart.ai/v1/chat/completions",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.apimart.ai/v1/chat/completions"
req, _ := http.NewRequest("POST", url, nil)
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.apimart.ai/v1/chat/completions")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.apimart.ai/v1/chat/completions")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
response = http.request(request)
puts response.read_bodyOpenAI SDK compatible: swap
Related APIs:
1.
This is opposite to OpenAI’s default (omit = non-stream). Easy to miss when porting SDK code.
2. Built-in tools work only on
Typical error text:
Tools declared but not invoked are free — they do not appear in
Response distinction:
Implicit hits typically only set
Fetch unit prices via the pricing API:
Read
① Use
Declare it together with
Tool calls (especially
No. Cap with
base_url and api_key. Model name is fixed qwen3.8-max.
from openai import OpenAI
client = OpenAI(
api_key="<your APIMart key>",
base_url="https://api.apimart.ai/v1",
)
resp = client.chat.completions.create(
model="qwen3.8-max",
messages=[{"role": "user", "content": "Describe Hangzhou in one sentence"}],
stream=False, # must set explicitly — see below
)
print(resp.choices[0].message.content)
Three easy pitfalls
1. /v1/chat/completions defaults to streaming if stream is omitted
# Omitting stream → SSE; parsing as non-stream fails
client.chat.completions.create(model="qwen3.8-max", messages=[...])
# Non-stream requires explicit False
client.chat.completions.create(
model="qwen3.8-max",
messages=[...],
stream=False,
)
2. Built-in tools work only on /v1/responses
/v1/chat/completions does not support built-in tools (they are ignored with no error). For web search, code interpreter, text-to-image search, or image-to-image search, use the Responses API.
3. Wrong tool names fail silently
Invalid tool names are accepted quietly.t2i_search / i2i_search on pricing pages are not valid tool names:
| Name you may see | Actual tool type |
|---|---|
t2i_search (pricing UI) | web_search_image |
i2i_search (pricing UI) | image_search |
Built-in tools (Responses)
Declare in/v1/responses tools as {"type": "<name>"}.
| Tool | Purpose | Constraints |
|---|---|---|
web_search | Search the web and cite | — |
web_extractor | Fetch page body by URL | Must be declared with web_search; alone → 400 |
code_interpreter | Run code in a sandbox | — |
web_search_image | Text→image search (existing web images, not generation) | — |
image_search | Image→image search | Provide an image in input; slower — prefer streaming |
Web search
resp = client.responses.create(
model="qwen3.8-max",
input="One sentence about Shanghai weather tomorrow",
tools=[{"type": "web_search"}],
max_output_tokens=600,
)
Web extract
resp = client.responses.create(
model="qwen3.8-max",
input="Fetch https://example.com and summarize the first screen",
tools=[
{"type": "web_search"},
{"type": "web_extractor"}, # missing web_search → 400
],
)
The web_extractor tool must be executed with web_search tool.
Image search
resp = client.responses.create(
model="qwen3.8-max",
input=[{
"role": "user",
"content": [
{"type": "input_image", "image_url": "https://example.com/your.jpg"},
{"type": "input_text", "text": "Find similar images"},
],
}],
tools=[{"type": "image_search"}],
stream=True, # recommended: this tool is noticeably slower
)
Confirm a tool was called
Trustusage.x_tools (billing source of truth):
{
"output": [
{ "type": "reasoning" },
{ "type": "web_search_call" },
{
"type": "message",
"content": [{ "type": "output_text", "text": "..." }]
}
],
"usage": {
"input_tokens": 2528,
"output_tokens": 362,
"output_tokens_details": { "reasoning_tokens": 280 },
"x_tools": { "web_search": { "count": 1 } }
}
}
x_tools.
Thinking (reasoning)
Thinking cannot be turned off. Every request reasons before answering:- Thinking tokens bill as output, via
output_tokens_details.reasoning_tokens - Responses:
reasoningitems inoutput; chat stream:delta.reasoning_content enable_thinking: falseis ignored in stream mode; non-stream is degraded — not recommended
max_output_tokens.
Context cache
Reuse long prompts to cut input cost.Implicit cache (automatic)
Repeated requests with the same prefix may hit cache from the second call; hits bill at cache rates (~1/8 of normal input). Hit size is block-rounded; full hit is not guaranteed.Explicit cache
Mark content withcache_control:
client.chat.completions.create(
model="qwen3.8-max",
messages=[
{
"role": "system",
"content": [
{
"type": "text",
"text": "<long fixed context>",
"cache_control": {"type": "ephemeral"},
}
],
},
{"role": "user", "content": "Answer based on the context above"},
],
stream=False,
)
| Item | Notes |
|---|---|
| Min cache content | ≥ 1024 tokens |
| TTL | 5 minutes |
| Cache create | Slightly more expensive than normal input |
| Explicit hit | Usually cheaper than implicit hit |
// create
"prompt_tokens_details": {
"cache_creation_input_tokens": 1482,
"cache_type": "ephemeral",
"cached_tokens": 0
}
// hit
"prompt_tokens_details": {
"cache_creation_input_tokens": 0,
"cache_type": "ephemeral",
"cached_tokens": 1482
}
cached_tokens (may omit cache_type: "ephemeral").
PDF understanding
Supported only on/v1/chat/completions. Responses silently ignores PDFs (no error).
client.chat.completions.create(
model="qwen3.8-max",
messages=[{
"role": "user",
"content": [
{
"type": "file",
"file": {"file_url": "https://example.com/doc.pdf"},
},
# or base64:
# {"type": "file", "file": {"file_data": "data:application/pdf;base64,...", "filename": "doc.pdf"}}
{"type": "text", "text": "What is this document about?"},
],
}],
stream=False,
)
- PDFs are understood as images and bill as image input tokens (~letter ~2082 tok/page, A4 ~2147 tok/page order of magnitude); no extra parse fee
fileid://is invalid here (other-model mechanism) and treated as plain text
Billing model
Total = token fees + per-call tool fees (independent).| Item | How billed |
|---|---|
| Input | Per token; cache hits use cache rates |
| Output | Per token; thinking counts as output |
| Built-in tools | Actual calls in usage.x_tools (often priced per 1000 calls) |
GET /api/pricing/model?model=qwen3.8-max
data.pricing.effective_rates (includes group discount) and data.pricing.extras.tools.
Limits
| Item | Value |
|---|---|
| Max input | 983,616 tokens (~1M) |
| Max output | 131,072 tokens |
| Cache read / write | Supported / supported |
| Multimodal input | Images, video, etc. (e.g. input_image) |
FAQ
Q: I passedtools but nothing ran?① Use
/v1/responses; ② correct tool names (not t2i_search); ③ check usage.x_tools — missing means not invoked and not billed.
Q: web_extractor returns 400?Declare it together with
web_search.
Q: Non-stream is slow?Tool calls (especially
image_search) take longer. Prefer streaming when timeouts matter.
Q: Can I disable thinking to save money?No. Cap with
max_output_tokens, or switch to a model that allows disabling thinking.
Q: How do I check usage?
GET /v1/dashboard/billing/usage
GET /v1/dashboard/billing/subscription