tokenexplore API Developer Documentation
TIP
The help center has been split into multiple pages: for day-to-day use, start from the help center home and open the topic-specific docs (LLM API, image generation, video generation, clients, and CLI). This file is kept as a complete single-page reference for full-text search.
Last updated: October 23, 2026
tokenexplore is an LLM API aggregation platform. All you need is one API Key to call Claude, Gemini, Kimi, DeepSeek, GLM, Qwen, Doubao Seedance, and other models through three compatible protocols: OpenAI / Anthropic (Claude) / Google Gemini.
Important
API Gateway Address
The API gateway address used in every example in this document is https://ai.afp.net (the same as in the console). If your environment uses a custom domain, use the address shown in the console.
Likewise, sk-xxxxxxxx only indicates the format of an API Key; replace it with a token you created yourself.
Table of Contents
- Quick Start
- Authentication
- Base URL and Compatible Protocols
- Model List
- OpenAI Chat Completions
- OpenAI Responses
- Anthropic Messages (Claude Native)
- Gemini Native API
- Streaming Output (SSE)
- Multimodal: Image Input
- Tool Calling (Function Calling)
- Reasoning / Thinking
- Image Generation
- Video Generation (Doubao Seedance)
- Embeddings
- SDK Examples Summary
- Common Client Setup
- Error Codes and Troubleshooting
- Tokens, Groups, and Rate Limits
- Billing
- FAQ
1. Quick Start
Four steps are all it takes to send your first request.
1.1 Sign Up and Log In
Open the tokenexplore console at https://ai.afp.net, then sign up and log in.
1.2 Top Up
Go to the "Wallet" or "Top Up" page and choose online payment, or redeem a code. Your balance is shown as "credits". For per-model pricing, see the "Model Plaza / Pricing" page in the console, and §20 Billing.
1.3 Create a Token (API Key)
- Go to "Token Management" and click "Add Token".
- Set the name, expiration time, credit limit, token group, available model scope, and IP allowlist as needed. If you are unsure, keep the defaults.
- After saving, click "Copy" to get a key that looks like
sk-xxxxxxxxxxxxxxxx.
Warning
The API Key is your payment credential. Never put it in frontend code, mobile app packages, screenshots, logs, or public repositories. If you suspect it has leaked, delete or disable it in "Token Management" immediately and create a new one.
1.4 Send Your First Request
export CIYUAN_BASE_URL="https://ai.afp.net" # gateway address
export CIYUAN_API_KEY="sk-xxxxxxxx" # replace with your token
curl "$CIYUAN_BASE_URL/v1/chat/completions" \
-H "Authorization: Bearer $CIYUAN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "claude-sonnet-4-6",
"messages": [
{"role": "user", "content": "Describe yourself in three sentences"}
]
}'choices[0].message.content in the returned JSON is the model's answer. The examples that follow assume you have already set the CIYUAN_BASE_URL and CIYUAN_API_KEY environment variables.
Python version:
# pip install -U openai
import os
from openai import OpenAI
client = OpenAI(
api_key=os.environ["CIYUAN_API_KEY"],
base_url=os.environ["CIYUAN_BASE_URL"] + "/v1",
)
resp = client.chat.completions.create(
model="claude-sonnet-4-6",
messages=[{"role": "user", "content": "Describe yourself in three sentences"}],
)
print(resp.choices[0].message.content)2. Authentication
All protocols share the same API Key; only the request header differs:
| Protocol | Recommended header | Alternative |
|---|---|---|
OpenAI compatible (/v1/chat/completions, /v1/responses, /v1/images/*, /v1/videos, etc.) | Authorization: Bearer sk-xxx | — |
Anthropic Messages (/v1/messages) | x-api-key: sk-xxx, plus anthropic-version: 2023-06-01 | Authorization: Bearer sk-xxx |
Gemini native (/v1beta/models/...) | x-goog-api-key: sk-xxx | URL parameter ?key=sk-xxx, or Authorization: Bearer sk-xxx |
Notes:
- Each token can have its own credit limit, expiration time, available models, IP allowlist, and group. When any of these limits is triggered, the request is rejected; see §18.
- The console session and the backend "system access token" are only for management endpoints (
/api/*) and must not be used to call models. Use a token that starts withsk-to call models.
3. Base URL and Compatible Protocols
| Protocol | Method and path | Use case |
|---|---|---|
| OpenAI Chat Completions | POST https://ai.afp.net/v1/chat/completions | Best compatibility; recommended as the default. Works with every text model, and most third-party tools support only this endpoint |
| OpenAI Responses | POST https://ai.afp.net/v1/responses | The new unified OpenAI endpoint. Availability depends on the model and channel; test before integrating |
| Anthropic Messages | POST https://ai.afp.net/v1/messages | Suited to existing Anthropic SDK code, Claude Code, and similar. Recommended only for Claude-series models |
| Gemini generateContent | POST https://ai.afp.net/v1beta/models/{model}:generateContent | Suited to existing Google GenAI SDK code, Gemini CLI, and Gemini image generation |
| Gemini streaming | POST https://ai.afp.net/v1beta/models/{model}:streamGenerateContent?alt=sse | Gemini native streaming output |
| Model list | GET https://ai.afp.net/v1/models | Query the models available to the current token |
| Image generation | POST https://ai.afp.net/v1/images/generations | OpenAI image generation format |
| Video generation (video compatible protocol) | POST https://ai.afp.net/v1/video/generations, GET https://ai.afp.net/v1/video/generations/{task_id}; the OpenAI Videos style POST /v1/videos is also supported | Compatible with existing OpenAI video tools; download results with GET /v1/videos/{task_id}/content |
| Video generation (Seedance official protocol) | POST https://ai.afp.net/api/v3/contents/generations/tasks | When you need the full feature set: all-modality references, 2.5 editing/extension, the assets library, signed callbacks, and more |
| Video assets library (official Assets API) | POST https://ai.afp.net/api/v3/assets?Action=<Action>&Version=2024-01-01 | Import and manage reference image / video / audio assets |
| Embeddings | POST https://ai.afp.net/v1/embeddings | Text vectorization; requires an embedding model available on the platform |
How to Fill In the Base URL for Different Clients
Different SDKs and tools interpret "Base URL" differently. The three most common forms are:
| Form | Typically used by |
|---|---|
https://ai.afp.net | Anthropic SDK, Claude Code, Google GenAI SDK, Gemini CLI, Cherry Studio |
https://ai.afp.net/v1 | Official OpenAI SDK (Python / Node), LobeChat, Cline (OpenAI Compatible), Cursor, LangChain |
https://ai.afp.net/v1/chat/completions | A few tools that require the full endpoint URL |
TIP
If one form returns 404, switch to the other two in turn. A single extra or missing /v1 will produce an incorrect path.
Can I Mix Models and Protocols?
Yes. The platform converts formats between protocols, so you can call Claude or Gemini through the OpenAI format, and you can call some non-Claude models through the Claude format. However, the conversion is not lossless for every field:
- Claude series: prefer
/v1/messagesor/v1/chat/completions. - Gemini series: prefer
/v1/chat/completionsor the Gemini native API. For image generation, use the Gemini native API. - Kimi / DeepSeek / GLM / Qwen: use
/v1/chat/completions. - Seedance video: only the video task endpoints work; see §14.
4. Model List
4.1 Query Endpoint
curl "$CIYUAN_BASE_URL/v1/models" \
-H "Authorization: Bearer $CIYUAN_API_KEY"The response is an OpenAI-format list:
{
"object": "list",
"data": [
{"id": "claude-sonnet-4-6", "object": "model", "owned_by": "..."},
{"id": "gemini-3.1-pro", "object": "model", "owned_by": "..."}
]
}The models returned are limited by the token's available model scope and the token's group. Treat the actual API response as authoritative.
When using the Gemini SDK, you can also query with the native format: GET https://ai.afp.net/v1beta/models (with the x-goog-api-key header).
4.2 Current Models at a Glance
Models are added and retired continuously, so the table below is only a snapshot as of the last update of this document. For pricing, refer to the "Model Plaza / Pricing" page in the console; this document does not list prices.
Claude (Anthropic)
| Model ID | Description |
|---|---|
claude-opus-5-5 | Opus-series flagship |
claude-opus-5 | Opus 5 |
claude-opus-4-8 | Opus 4.8 |
claude-opus-4-7 | Opus 4.7 |
claude-opus-4-6 | Opus 4.6 |
claude-opus-4-6-thinking | Opus 4.6 with extended thinking enabled by default; see §12 |
claude-sonnet-5 | Sonnet 5 |
claude-sonnet-4-6 | Sonnet 4.6, recommended for everyday development |
claude-sonnet-4-5-20250929 | Sonnet 4.5 (dated pinned version) |
claude-haiku-4-5 | Haiku 4.5, low latency and low cost |
claude-haiku-4-5-20251001 | Haiku 4.5 (dated pinned version) |
claude-fable-5 | Fable 5 |
claude-fable-5-1 | Fable 5.1 |
Gemini (Google)
| Model ID | Description |
|---|---|
gemini-3.1-pro | Gemini 3.1 Pro |
gemini-3.1-pro-preview | Gemini 3.1 Pro preview |
gemini-3.7-flash | Gemini 3.7 Flash |
gemini-3.6-flash | Gemini 3.6 Flash |
gemini-3.5-flash | Gemini 3.5 Flash |
gemini-3.5-flash-lite | Gemini 3.5 Flash-Lite |
gemini-3.1-flash-lite | Gemini 3.1 Flash-Lite |
gemini-2.5-pro | Gemini 2.5 Pro |
gemini-2.5-flash-lite | Gemini 2.5 Flash-Lite |
gemini-3-pro-image | Image generation / editing; see §13 |
gemini-3.1-flash-image | Image generation / editing (Flash tier) |
Kimi (Moonshot AI)
| Model ID | Description |
|---|---|
kimi-k3 | Kimi K3 |
kimi-k3-usd | A separate billing / channel variant of Kimi K3; see the console for details |
DeepSeek
| Model ID | Description |
|---|---|
deepseek-v4-pro-0813 | DeepSeek V4 Pro |
deepseek-v4-flash | DeepSeek V4 Flash |
deepseek-v4-1-flash-260910 | DeepSeek V4.1 Flash |
GLM (Zhipu)
| Model ID | Description |
|---|---|
glm-5.3 | GLM-5.3 |
glm-5.3-flash | GLM-5.3 Flash |
glm-5.2 | GLM-5.2 |
Qwen (Tongyi)
| Model ID | Description |
|---|---|
qwen3.8-max | Qwen3.8 Max |
qwen3.8-flash | Qwen3.8 Flash |
Doubao Seedance (Video Generation)
| Model ID | Description |
|---|---|
doubao-seedance-2-5-260628 | Seedance 2.5 |
doubao-seedance-2-0-260128 | Seedance 2.0 standard |
doubao-seedance-2-0-fast-260128 | Seedance 2.0 Fast (faster, lower resolution cap) |
doubao-seedance-2-0-mini-260615 | Seedance 2.0 Mini (lighter, lower resolution cap) |
INFO
Not every model supports image input, tool calling, reasoning parameters, or /v1/responses. Before integrating for real, send one minimal request to the target model. The "Model Plaza" page in the console usually indicates which endpoint types each model supports.
5. OpenAI Chat Completions
POST https://ai.afp.net/v1/chat/completions
5.1 Basic Request
curl "$CIYUAN_BASE_URL/v1/chat/completions" \
-H "Authorization: Bearer $CIYUAN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gemini-3.5-flash",
"messages": [
{"role": "system", "content": "You are a concise technical assistant."},
{"role": "user", "content": "Explain what SSE is"}
],
"temperature": 0.7,
"max_tokens": 1024
}'5.2 Example Response
{
"id": "chatcmpl-xxxx",
"object": "chat.completion",
"created": 1792000000,
"model": "gemini-3.5-flash",
"choices": [
{
"index": 0,
"message": {"role": "assistant", "content": "SSE (Server-Sent Events) is ..."},
"finish_reason": "stop"
}
],
"usage": {"prompt_tokens": 25, "completion_tokens": 180, "total_tokens": 205}
}5.3 Common Fields
| Field | Description |
|---|---|
model | Required. Model ID. |
messages | Required. Array of chat messages; role can be system / user / assistant / tool. |
stream | Whether to enable streaming output; see §9. |
stream_options | When set to {"include_usage": true}, an extra chunk containing usage is returned before the stream ends. |
max_tokens / max_completion_tokens | Maximum number of output tokens. For reasoning models, the thinking process also consumes this budget. |
temperature / top_p | Sampling parameters. Not supported by some reasoning models. |
tools / tool_choice | Tool calling; see §11. |
response_format | Structured output, for example {"type": "json_object"}. Support depends on the model. |
reasoning_effort | Reasoning effort: low / medium / high. Supported by only some models; see §12. |
stop / seed / n / presence_penalty / frequency_penalty | Standard OpenAI parameters. Parameters unsupported upstream may be ignored or may raise an error. |
5.4 Meaning of finish_reason
| Value | Meaning |
|---|---|
stop | Ended normally |
length | Hit the max_tokens limit. When a reasoning model returns empty content, this is usually why. |
tool_calls | The model has requested a tool call |
content_filter | Content was filtered by a safety policy |
6. OpenAI Responses
POST https://ai.afp.net/v1/responses
The platform supports the OpenAI Responses protocol. The request body uses input instead of messages, and instructions instead of a system message.
INFO
Responses is OpenAI's newer protocol. Whether a given model can be called through Responses depends on the channel the platform has configured for it. If you are calling a non-OpenAI model such as Claude, Gemini, or Kimi and have no particular reason to use Responses, prefer /v1/chat/completions or the vendor's native protocol. Run one minimal test with the target model before integrating; if it returns 4xx, switch back to Chat Completions.
curl "$CIYUAN_BASE_URL/v1/responses" \
-H "Authorization: Bearer $CIYUAN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "claude-sonnet-4-6",
"instructions": "You are a concise technical assistant.",
"input": "Describe tokenexplore in three sentences",
"max_output_tokens": 1024
}'resp = client.responses.create(
model="claude-sonnet-4-6",
input="Describe tokenexplore in three sentences",
)
print(resp.output_text)| Field | Description |
|---|---|
model | Required |
input | Required. Can be a string or a structured message array. |
instructions | System-level instructions |
stream | Streaming output; returns semantic SSE events such as response.output_text.delta |
max_output_tokens | Maximum number of output tokens |
reasoning | For example {"effort": "high"}; depends on the model |
tools | Tool definitions (Responses format: fields are flat, not nested under function) |
To determine whether the response finished, check the returned status. completed means it ended normally; incomplete means it stopped early, with the reason in incomplete_details.reason — a common value is max_output_tokens.
7. Anthropic Messages (Claude Native)
POST https://ai.afp.net/v1/messages
Suited to existing Anthropic SDK code, Claude Code, and various agent frameworks. The request body follows the Anthropic Messages API exactly.
curl "$CIYUAN_BASE_URL/v1/messages" \
-H "x-api-key: $CIYUAN_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "Content-Type: application/json" \
-d '{
"model": "claude-opus-4-8",
"max_tokens": 1024,
"system": "You are a senior Go engineer.",
"messages": [
{"role": "user", "content": "How do you usually diagnose a goroutine leak?"}
]
}'Example response:
{
"id": "msg_xxxx",
"type": "message",
"role": "assistant",
"model": "claude-opus-4-8",
"content": [{"type": "text", "text": "Diagnosing a goroutine leak starts with ..."}],
"stop_reason": "end_turn",
"usage": {"input_tokens": 30, "output_tokens": 420}
}Key Differences Between the Anthropic and OpenAI Formats
| Item | Anthropic Messages | OpenAI Chat Completions |
|---|---|---|
max_tokens | Required | Optional |
| System prompt | Top-level system field | A message with role: "system" |
| Images | {"type":"image","source":{...}} | {"type":"image_url","image_url":{...}} |
| Tool schema | input_schema | function.parameters |
| Stop reason | stop_reason (end_turn / max_tokens / tool_use) | finish_reason |
| Authentication | x-api-key + anthropic-version | Authorization: Bearer |
Prompt caching: if the model and channel support it, you can add "cache_control": {"type": "ephemeral"} to system or to a message block. On a cache hit, cache_read_input_tokens / cache_creation_input_tokens appear in usage, and billing changes accordingly; see §20.
8. Gemini Native API
The Base URL is https://ai.afp.net, and the paths match the Google Gemini API:
- Non-streaming:
POST https://ai.afp.net/v1beta/models/{model}:generateContent - Streaming:
POST https://ai.afp.net/v1beta/models/{model}:streamGenerateContent?alt=sse
curl "$CIYUAN_BASE_URL/v1beta/models/gemini-3.1-pro:generateContent" \
-H "x-goog-api-key: $CIYUAN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"systemInstruction": {"parts": [{"text": "You are a concise assistant."}]},
"contents": [
{"role": "user", "parts": [{"text": "Why is seawater salty?"}]}
],
"generationConfig": {"temperature": 0.7, "maxOutputTokens": 2048}
}'Python (Google GenAI SDK):
# pip install -U google-genai
import os
from google import genai
from google.genai import types
client = genai.Client(
api_key=os.environ["CIYUAN_API_KEY"],
http_options=types.HttpOptions(base_url=os.environ["CIYUAN_BASE_URL"]),
)
resp = client.models.generate_content(
model="gemini-3.1-pro",
contents="Why is seawater salty?",
)
print(resp.text)INFO
The platform does not currently support :countTokens; calls return 404. To estimate tokens, refer to usageMetadata in the response.
9. Streaming Output (SSE)
With stream: true, the endpoint returns a Server-Sent Events stream instead of a single complete JSON document. The client must read events starting with data: line by line.
9.1 OpenAI Chat Completions Streaming
curl -N "$CIYUAN_BASE_URL/v1/chat/completions" \
-H "Authorization: Bearer $CIYUAN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "deepseek-v4-flash",
"stream": true,
"stream_options": {"include_usage": true},
"messages": [{"role": "user", "content": "Write a short poem about autumn"}]
}'Event format:
data: {"id":"chatcmpl-xxx","choices":[{"index":0,"delta":{"role":"assistant","content":"Aut"}}]}
data: {"id":"chatcmpl-xxx","choices":[{"index":0,"delta":{"content":"umn"}}]}
data: {"id":"chatcmpl-xxx","choices":[],"usage":{"prompt_tokens":20,"completion_tokens":28,"total_tokens":48}}
data: [DONE]Python:
stream = client.chat.completions.create(
model="deepseek-v4-flash",
messages=[{"role": "user", "content": "Write a short poem about autumn"}],
stream=True,
stream_options={"include_usage": True},
)
for chunk in stream:
if chunk.choices and chunk.choices[0].delta.content:
print(chunk.choices[0].delta.content, end="", flush=True)
if chunk.usage: # final chunk
print("\nUsage:", chunk.usage)Node.js:
const stream = await client.chat.completions.create({
model: "deepseek-v4-flash",
messages: [{ role: "user", content: "Write a short poem about autumn" }],
stream: true,
});
for await (const chunk of stream) {
process.stdout.write(chunk.choices[0]?.delta?.content ?? "");
}9.2 Anthropic Streaming
Add "stream": true to the /v1/messages request body to receive the standard Anthropic events: message_start → content_block_start → multiple content_block_delta → content_block_stop → message_delta → message_stop. Reading them with the SDK is the easiest approach:
with claude.messages.stream(
model="claude-sonnet-4-6",
max_tokens=1024,
messages=[{"role": "user", "content": "Tell a programmer joke"}],
) as stream:
for text in stream.text_stream:
print(text, end="", flush=True)9.3 Gemini Streaming
Use :streamGenerateContent?alt=sse. Each data: payload is a partial GenerateContentResponse.
9.4 Notes
- Pass
-Nto curl to disable buffering. - Reverse proxies such as Nginx must have buffering disabled (
proxy_buffering off;), otherwise output is accumulated and flushed all at once. - During long reasoning runs, the stream may have no body text for a while. Set the client read timeout generously — at least 300 seconds is recommended.
10. Multimodal: Image Input
This section is about letting a model look at images, understand them, and extract text from them. To generate images, see §13.
The models that support image input are mainly the Claude and Gemini series (excluding the *-image generation models). For whether models such as Kimi, Qwen, and GLM support visual input, refer to the labels in Model Plaza.
The image field differs across the three protocols:
| Protocol | How to write it |
|---|---|
| Chat Completions | In messages[].content[], use {"type":"image_url","image_url":{"url":"..."}} |
| Responses | In input[].content[], use {"type":"input_image","image_url":"..."} |
| Anthropic Messages | In messages[].content[], use {"type":"image","source":{...}} |
| Gemini native | In parts[], use {"inlineData":{"mimeType":"image/png","data":"<base64>"}} |
10.1 Chat Completions
curl "$CIYUAN_BASE_URL/v1/chat/completions" \
-H "Authorization: Bearer $CIYUAN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gemini-3.5-flash",
"messages": [{
"role": "user",
"content": [
{"type": "text", "text": "Describe this image and extract the text in it"},
{"type": "image_url", "image_url": {"url": "https://example.com/image.png"}}
]
}]
}'You can also pass a local image as a Base64 Data URL:
{"type": "image_url", "image_url": {"url": "data:image/jpeg;base64,BASE64_IMAGE_DATA"}}Reading a local image in Python:
import base64
with open("photo.jpg", "rb") as f:
b64 = base64.b64encode(f.read()).decode()
resp = client.chat.completions.create(
model="claude-sonnet-4-6",
messages=[{
"role": "user",
"content": [
{"type": "text", "text": "What is in this picture?"},
{"type": "image_url", "image_url": {"url": f"data:image/jpeg;base64,{b64}"}},
],
}],
)10.2 Anthropic Messages
{
"model": "claude-sonnet-4-6",
"max_tokens": 1024,
"messages": [{
"role": "user",
"content": [
{"type": "text", "text": "Describe this image"},
{"type": "image", "source": {"type": "url", "url": "https://example.com/image.png"}}
]
}]
}Base64 form:
{"type": "image", "source": {"type": "base64", "media_type": "image/png", "data": "BASE64_IMAGE_DATA"}}10.3 Image Input Troubleshooting
- First confirm that the model supports visual input.
- The image URL must be directly reachable from the public internet, without a login or hotlink protection. If unsure, switch to a Data URL.
- An image that is too large can exceed the request body limit (413) or increase costs; compress it to a long edge of no more than 2000px first.
11. Tool Calling (Function Calling)
11.1 OpenAI Format (Complete Round Trip)
import json
tools = [{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get the current weather for a specified city",
"parameters": {
"type": "object",
"properties": {
"city": {"type": "string", "description": "City name, for example Beijing"}
},
"required": ["city"],
},
},
}]
messages = [{"role": "user", "content": "What is the weather in Beijing right now?"}]
# First round: the model decides to call a tool
resp = client.chat.completions.create(
model="claude-sonnet-4-6", messages=messages, tools=tools, tool_choice="auto"
)
msg = resp.choices[0].message
messages.append(msg)
if msg.tool_calls:
for call in msg.tool_calls:
args = json.loads(call.function.arguments)
result = {"city": args["city"], "weather": "sunny", "temp_c": 18} # replace with your real implementation
messages.append({
"role": "tool",
"tool_call_id": call.id,
"content": json.dumps(result, ensure_ascii=False),
})
# Second round: hand the tool results to the model to produce the final answer
final = client.chat.completions.create(
model="claude-sonnet-4-6", messages=messages, tools=tools
)
print(final.choices[0].message.content)Possible values for tool_choice: "auto" (default), "none", "required", or {"type":"function","function":{"name":"get_weather"}} (force a call to a specific function).
11.2 Anthropic Format
curl "$CIYUAN_BASE_URL/v1/messages" \
-H "x-api-key: $CIYUAN_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "Content-Type: application/json" \
-d '{
"model": "claude-opus-4-8",
"max_tokens": 1024,
"tools": [{
"name": "get_weather",
"description": "Get the current weather for a specified city",
"input_schema": {
"type": "object",
"properties": {"city": {"type": "string"}},
"required": ["city"]
}
}],
"messages": [{"role": "user", "content": "What is the weather in Beijing right now?"}]
}'When the response has stop_reason: "tool_use", take the type: "tool_use" block from content (it contains id, name, and input). After running the tool, send the result back in a user message in the next round:
{"role": "user", "content": [
{"type": "tool_result", "tool_use_id": "toolu_xxx", "content": "{\"weather\":\"sunny\",\"temp_c\":18}"}
]}11.3 Tips
- Tool calling support varies by model. The flagship models of Claude, Gemini, Kimi, DeepSeek, GLM, and Qwen generally support it.
parameters/input_schemamust be a valid JSON Schema. When there are no parameters, write{"type":"object","properties":{}}.- With parallel tool calls,
tool_callsmay contain several calls at once, and each result must be sent back individually.
12. Reasoning / Thinking
Reasoning models think before they answer. The thinking process consumes tokens, is generally billed as output, and counts against the max_tokens budget.
12.1 Claude Extended Thinking
Option 1: Native Anthropic parameters (recommended)
{
"model": "claude-opus-4-8",
"max_tokens": 16000,
"thinking": {"type": "enabled", "budget_tokens": 8000},
"messages": [{"role": "user", "content": "Prove that the square root of 2 is irrational"}]
}budget_tokensmust be less thanmax_tokensand no less than 1024.- The returned
contentfirst contains atype: "thinking"block, followed by atype: "text"block. - Once thinking is enabled, do not set sampling parameters such as
temperatureortop_k.
Option 2: Use a -thinking model directly
claude-opus-4-6-thinking has thinking enabled by default and can be called through any protocol.
curl "$CIYUAN_BASE_URL/v1/chat/completions" \
-H "Authorization: Bearer $CIYUAN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "claude-opus-4-6-thinking",
"max_tokens": 16000,
"messages": [{"role": "user", "content": "Prove that the square root of 2 is irrational"}]
}'When calling through Chat Completions, the thinking content is generally placed in choices[0].message.reasoning_content, and in delta.reasoning_content when streaming. The answer itself is still in content.
12.2 Gemini Thinking
The Gemini 2.5 / 3.x series think by default.
- Chat Completions: you can pass
"reasoning_effort": "low" | "medium" | "high", and the platform converts it into Gemini's thinking configuration. The exact mapping depends on the model. - Gemini native: control it through
generationConfig.thinkingConfig:
{
"contents": [{"role": "user", "parts": [{"text": "Calculate 37 × 43 step by step"}]}],
"generationConfig": {
"thinkingConfig": {"thinkingBudget": 2048, "includeThoughts": true}
}
}The Gemini 3 series also supports "thinkingLevel": "low" | "high". The supported fields are determined by Google's official documentation and the actual response.
12.3 DeepSeek / Kimi / GLM / Qwen
These models are called through /v1/chat/completions. If the model has a thinking process, it is generally returned in the reasoning_content field. Whether reasoning_effort or parameters that toggle thinking are supported depends on each model's documentation.
12.4 Common Pitfalls with Reasoning Models
- Empty content: most likely
max_tokenswas set too low and the entire budget was consumed by the thinking process, in which casefinish_reasonislength. Increasemax_tokensor drop the parameter. - Parameter errors: reasoning models are stricter about parameters such as
temperature,top_p,presence_penalty,frequency_penalty, andlogprobs. If you get an error, remove these sampling parameters first and keep only the model, messages, and token limit. - Slow first token: thinking takes time. A non-streaming request waits until thinking finishes before returning; use streaming where responsiveness matters.
13. Image Generation
13.1 Gemini Image Generation (Native API Recommended)
gemini-3-pro-image and gemini-3.1-flash-image are both Gemini native "text + image" multimodal output models. Calling them through the native Gemini generateContent is recommended:
curl "$CIYUAN_BASE_URL/v1beta/models/gemini-3-pro-image:generateContent" \
-H "x-goog-api-key: $CIYUAN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"contents": [{"role": "user", "parts": [{"text": "An orange tabby cat sitting by the window watching the rain, watercolor style"}]}],
"generationConfig": {
"responseModalities": ["TEXT", "IMAGE"],
"imageConfig": {"aspectRatio": "16:9"}
}
}'In the returned candidates[0].content.parts[], the part carrying inlineData (mimeType + Base64 data) is the image. Python example:
import base64
from google.genai import types
resp = client.models.generate_content(
model="gemini-3-pro-image",
contents="An orange tabby cat sitting by the window watching the rain, watercolor style",
config=types.GenerateContentConfig(response_modalities=["TEXT", "IMAGE"]),
)
for part in resp.candidates[0].content.parts:
if part.inline_data:
with open("cat.png", "wb") as f:
f.write(part.inline_data.data) # the SDK has already decoded the Base64
elif part.text:
print(part.text)Image editing / reference images: put an inlineData image and a text instruction in parts together (for example, "change the background to a snowy scene").
INFO
You can also call these two models through /v1/chat/completions, but the form in which the generated image appears in content (for example, a Markdown image or a Data URL) depends on the platform version and channel configuration. For batch production, use the native API.
13.2 OpenAI Images API
The platform also provides OpenAI-format image endpoints:
- Generation:
POST https://ai.afp.net/v1/images/generations - Editing:
POST https://ai.afp.net/v1/images/edits(multipart/form-dataor JSON)
curl "$CIYUAN_BASE_URL/v1/images/generations" \
-H "Authorization: Bearer $CIYUAN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "<a model marked in the console as supporting images/generations>",
"prompt": "An orange tabby cat sitting by the window watching the rain, watercolor style",
"n": 1,
"size": "1024x1024",
"response_format": "b64_json"
}'The images API waits synchronously for generation within the request, and the result is returned only in that response; it cannot be queried afterwards, so save it promptly. If it takes too long, it may return 504 task_timeout. Which models can be used with this endpoint depends on the "supported endpoints" labels in Model Plaza.
14. Video Generation (Doubao Seedance)
Video generation is an asynchronous task: submit a task, get the task ID, poll the task status, and download the video when it is done. Task IDs are assigned centrally by the platform, and queries must use the account that owns the token.
The platform offers two integration protocols. They share the same API Key, account credits, and billing system, but their request fields, response structures, and status enums differ. Use the same protocol for creation, querying, and status checks:
| Protocol | Create | Query | Download | Use case |
|---|---|---|---|---|
| Video compatible protocol | POST /v1/video/generations (the OpenAI Videos style POST /v1/videos is also supported) | GET /v1/video/generations/{task_id} (use GET /v1/videos/{id} for the OpenAI Videos style) | GET /v1/videos/{task_id}/content | Existing OpenAI video clients that only need text-to-video or image-to-video |
| Seedance official protocol | POST /api/v3/contents/generations/tasks | GET /api/v3/contents/generations/tasks/{task_id} | content.video_url, or GET /v1/videos/{task_id}/content | All-modality references, 2.5 editing/extension, the assets library, signed callbacks |
Only the OpenAI Videos style endpoint uses
queued/in_progress/completed/failed, wherecompletedcorresponds tosucceededin the Seedance official protocol; queries on the legacy compatible endpoint return the platform's internal states (such asIN_PROGRESS/SUCCESS/FAILURE). Do not mix endpoints and response structures from the two protocols.
There is also the assets library (official Assets API): POST https://ai.afp.net/api/v3/assets?Action=<Action>&Version=2024-01-01, used to import and manage reference images / videos / audio and to reference them in the official protocol with asset://<asset ID>. See §14.3 and Video Generation · Assets Library for details.
14.1 Video Compatible Protocol
Submit a task
curl "$CIYUAN_BASE_URL/v1/video/generations" \
-H "Authorization: Bearer $CIYUAN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "doubao-seedance-2-5-260628",
"prompt": "Sunrise by the sea, the camera slowly pushes in, waves lapping against the rocks, cinematic",
"seconds": "4",
"metadata": {
"resolution": "480p",
"ratio": "16:9",
"generate_audio": false
}
}'A successful creation only means the task has been accepted; no video URL is returned. The legacy compatible endpoint returns:
{"id": "task_xxxxxxxxx", "task_id": "task_xxxxxxxxx", "status": "queued", "model": "doubao-seedance-2-5-260628", "created_at": 1787827200}The OpenAI Videos style endpoint (POST /v1/videos) returns a video object (without task_id):
{"id": "task_xxxxxxxxx", "object": "video", "model": "doubao-seedance-2-5-260628", "status": "queued", "progress": 0, "created_at": 1787827200}Request parameters
| Field | Description |
|---|---|
model | Required. Seedance model ID. |
prompt | Required. The prompt. |
images | Array of reference image URLs. The OpenAI Videos style path accepts only images; do not pass a single image. |
image | Single image URL supported by the legacy compatible endpoint (converted to images internally); new code should use images consistently. |
seconds | Duration as a string, such as "4" or "-1". Takes precedence when duration is also present. |
duration | Duration as an integer. It can replace seconds on the OpenAI Videos style endpoint; the legacy compatible endpoint reads only seconds and ignores duration. |
metadata | Pass-through official parameters: content, resolution, ratio, generate_audio, omni_reference_task_type, and so on. |
Parameters such as resolution, ratio, and generate_audio must be placed inside metadata; at the top level they are ignored. Put reference images/videos/audio in metadata.content, and always write the prompt in prompt.
Query a task
GET /v1/videos/{task_id} returns an OpenAI Video object with the status queued → in_progress → completed (or failed); the response does not contain a video URL:
curl "$CIYUAN_BASE_URL/v1/videos/task_xxxxxxxxx" \
-H "Authorization: Bearer $CIYUAN_API_KEY"GET /v1/video/generations/{task_id} returns the generic task structure, with the result URL in data.result_url (the field may be absent; the download endpoint is authoritative):
curl "$CIYUAN_BASE_URL/v1/video/generations/task_xxxxxxxxx" \
-H "Authorization: Bearer $CIYUAN_API_KEY"{"code": "success", "message": "", "data": {"task_id": "task_xxxxxxxxx", "status": "IN_PROGRESS", "progress": "60%"}}The
/v1/videos/{id}response has nometadata.url,task_id, or video URL. Always download results withGET /v1/videos/{task_id}/content.
Download the video (after the task completes)
curl -L "$CIYUAN_BASE_URL/v1/videos/<task_id>/content" \
-H "Authorization: Bearer $CIYUAN_API_KEY" \
-o output.mp4The download endpoint supports the Range request header, so segmented downloads and resumable transfers are possible.
14.2 Seedance Official Protocol (Most Complete Parameters)
Submit a task
curl "$CIYUAN_BASE_URL/api/v3/contents/generations/tasks" \
-H "Authorization: Bearer $CIYUAN_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: order-20261023-001" \
-d '{
"model": "doubao-seedance-2-5-260628",
"content": [
{"type": "text", "text": "A little girl running through a wheat field, wind stirring the wheat, backlit by the setting sun"},
{"type": "image_url", "image_url": {"url": "https://example.com/first_frame.png"}, "role": "first_frame"}
],
"duration": 4,
"resolution": "480p",
"ratio": "16:9",
"generate_audio": false
}'The response is the public task ID:
{"id": "task_xxxxxxxxx"}Query a task
curl "$CIYUAN_BASE_URL/api/v3/contents/generations/tasks/task_xxxxxxxxx" \
-H "Authorization: Bearer $CIYUAN_API_KEY"{
"id": "task_xxxxxxxxx",
"model": "doubao-seedance-2-5-260628",
"status": "succeeded",
"content": {"video_url": "https://example.com/result.mp4", "video_asset_id": "asset_xxxxxxxxx"},
"usage": {"completion_tokens": 38830, "total_tokens": 38830},
"created_at": 1787827200,
"updated_at": 1787827300
}The statuses are queued, running, succeeded, failed, cancelled, and expired. On failure, error contains code and message; the final charge is settled on usage.total_tokens, and failed tasks are refunded asynchronously after backend confirmation. When result archiving is enabled on the platform, a successful response may also contain content.video_asset_id.
Common parameters
| Field | Description |
|---|---|
model | Required. Seedance model ID; see §4.2. |
content | Required. An ordered array. Element types can be text (prompt), image_url (reference image / first frame / last frame, distinguished by role: first_frame / last_frame / reference_image), video_url (reference_video), audio_url (reference_audio), or draft_task. |
duration | 2.0: -1 or 4–15; 2.5: -1 or 4–30 |
resolution | For example 480p / 720p / 1080p. 2.0 Fast and 2.0 Mini max out at 720p; the standard and 2.5 versions support higher resolutions — refer to the model documentation for specifics. |
ratio | 16:9, 4:3, 1:1, 3:4, 9:16, 21:9, adaptive |
generate_audio | Whether to generate audio as well (depends on the model) |
omni_reference_task_type | 2.5 only: auto / reference / edit / extend; edit requires reference_video, ratio=adaptive, and duration=-1 |
callback_url | Callback address for the task's final state; requires the platform's callback capability to be enabled and must be a public http(s) URL. |
return_last_frame | Return the last frame; when result archiving is enabled on the platform, last_frame_url / last_frame_asset_id may appear in a successful response. |
execution_expires_after | Task execution expiry in seconds, range 3600–259200 |
seed / camera_fixed / watermark / draft / frames / tools / output_format / service_tier / priority | Advanced parameters; whether they take effect depends on the model and channel. |
The official protocol uses an ordered
contentarray to describe the prompt and reference assets, and you can integrate by changing only the Base URL of the Volcengine Ark SDK. For the complete parameter set, 2.5 editing examples, and signed callbacks, see Video Generation · Seedance Official Protocol.
14.3 Assets Library (Assets API)
The assets library uses the Action-style endpoint: POST https://ai.afp.net/api/v3/assets?Action=<Action>&Version=2024-01-01. Version must be 2024-01-01; the outer response layer is ResponseMetadata, business data is in Result, and errors are in ResponseMetadata.Error.
| Category | Actions |
|---|---|
| Groups | CreateAssetGroup, ListAssetGroups, GetAssetGroup, UpdateAssetGroup, DeleteAssetGroup |
| Assets | CreateAsset, ListAssets, GetAsset, UpdateAsset, DeleteAsset |
| Real-person verification | CreateVisualValidateSession, GetVisualValidateResult (returns CapabilityNotSupported when all parameters are valid) |
CreateAssetrequiresGroupId,Name,AssetType(Image/Video/Audio), and a publicURL;AssetTypemust match the actual media type of the URL.- Assets and groups are isolated by user +
ProjectName; assets are retained for 7 days by default (configurable for long-term retention). - After import, reference an asset in the official protocol's
contentwithasset://<asset ID>; the platform's asset reference capability must be enabled. - Pagination:
MaxResults(default 10, maximum 100) +NextToken;PageNumber+PageSizeis also supported (PageNumber × PageSizelimits: 20,000 for assets and 10,000 for groups; exceeding them returnsInvalidParameter.Pagination). - The console and internal tools also use REST endpoints (
/api/v3/assets,/api/media/assets, etc.) to manage assets; the Action endpoint is better suited to programmatic integration.
For complete examples, retention periods, and error codes, see Video Generation · Assets Library.
14.4 Python Polling Example (OpenAI Videos Style)
import os, time, requests
BASE = os.environ["CIYUAN_BASE_URL"]
H = {"Authorization": f"Bearer {os.environ['CIYUAN_API_KEY']}"}
task = requests.post(f"{BASE}/v1/videos", headers=H, json={
"model": "doubao-seedance-2-0-mini-260615",
"prompt": "A Shiba Inu rolling in the snow, slow motion",
"seconds": "4",
"metadata": {"resolution": "720p", "ratio": "16:9"},
}).json()
task_id = task["id"]
print("Task submitted:", task_id)
while True:
info = requests.get(f"{BASE}/v1/videos/{task_id}", headers=H).json()
status = info.get("status")
print("Status:", status, info.get("progress", ""))
if status == "completed":
break
if status == "failed":
raise RuntimeError(info.get("error"))
time.sleep(10)
with requests.get(f"{BASE}/v1/videos/{task_id}/content", headers=H, stream=True) as r:
r.raise_for_status()
with open("output.mp4", "wb") as f:
for chunk in r.iter_content(1 << 20):
f.write(chunk)
print("Saved output.mp4")14.5 Notes on Video Tasks
- Video generation usually takes tens of seconds to several minutes. A polling interval of 5–15 seconds is recommended; do not poll at high frequency.
- Video links returned upstream generally expire, so download or re-host them as soon as possible. Alternatively, download through the
/v1/videos/{task_id}/contentproxy for consistency. - Field mapping for the video compatible protocol is handled by a platform plugin. When you need first/last frames, reference video/audio, 2.5 editing/extension, the assets library, or signed callbacks, switch to the Seedance official protocol.
- Video is billed per generation or per second and may vary with resolution, duration, and whether audio is generated; see the pricing page in the console for details.
- For production systems, save
task_xxx, the model, key parameters, and error information. With the official protocol, send anIdempotency-Keyto avoid duplicate tasks and duplicate charges caused by client retries.
15. Embeddings
POST https://ai.afp.net/v1/embeddings (OpenAI format); the Gemini native format is :embedContent / :batchEmbedContents.
curl "$CIYUAN_BASE_URL/v1/embeddings" \
-H "Authorization: Bearer $CIYUAN_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model": "<embedding model ID>", "input": ["tokenexplore", "LLM API aggregation"]}'INFO
The current model list does not yet include a dedicated embedding model. The endpoint itself is usable and can be called once an embedding model is available on the platform. Refer to the /v1/models response.
16. SDK Examples Summary
16.1 Python · OpenAI SDK
# pip install -U openai
from openai import OpenAI
client = OpenAI(api_key="sk-xxxxxxxx", base_url="https://ai.afp.net/v1")
resp = client.chat.completions.create(
model="kimi-k3",
messages=[{"role": "user", "content": "Hello"}],
)
print(resp.choices[0].message.content)16.2 Node.js / TypeScript · OpenAI SDK
// npm i openai
import OpenAI from "openai";
const client = new OpenAI({
apiKey: process.env.CIYUAN_API_KEY,
baseURL: `${process.env.CIYUAN_BASE_URL}/v1`,
});
const resp = await client.chat.completions.create({
model: "glm-5.3",
messages: [{ role: "user", content: "Hello" }],
});
console.log(resp.choices[0].message.content);16.3 Python · Anthropic SDK
# pip install -U anthropic
import anthropic
claude = anthropic.Anthropic(
api_key="sk-xxxxxxxx",
base_url="https://ai.afp.net", # Note: do not include /v1; the SDK appends /v1/messages automatically
)
msg = claude.messages.create(
model="claude-sonnet-4-6",
max_tokens=1024,
messages=[{"role": "user", "content": "Hello"}],
)
print(msg.content[0].text)16.4 Node.js · Anthropic SDK
// npm i @anthropic-ai/sdk
import Anthropic from "@anthropic-ai/sdk";
const claude = new Anthropic({
apiKey: process.env.CIYUAN_API_KEY,
baseURL: process.env.CIYUAN_BASE_URL, // no /v1
});
const msg = await claude.messages.create({
model: "claude-haiku-4-5",
max_tokens: 1024,
messages: [{ role: "user", content: "Hello" }],
});
console.log(msg.content[0].text);16.5 Python · Google GenAI SDK
See §8. The key point is http_options=types.HttpOptions(base_url="https://ai.afp.net"), without /v1beta after it.
16.6 LangChain
# pip install -U langchain-openai
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(
model="qwen3.8-max",
api_key="sk-xxxxxxxx",
base_url="https://ai.afp.net/v1",
)
print(llm.invoke("Hello").content)16.7 Environment Variables
Many tools read the following environment variables automatically:
# OpenAI ecosystem
export OPENAI_API_KEY="sk-xxxxxxxx"
export OPENAI_BASE_URL="https://ai.afp.net/v1"
# Anthropic ecosystem
export ANTHROPIC_API_KEY="sk-xxxxxxxx"
export ANTHROPIC_BASE_URL="https://ai.afp.net"17. Common Client Setup
Where "API address" appears below, enter tokenexplore's real address, https://ai.afp.net; for "API Key", enter your sk- token.
17.1 Claude Code
npm install -g @anthropic-ai/claude-codeEdit ~/.claude/settings.json (on Windows the path is %USERPROFILE%\.claude\settings.json):
{
"env": {
"ANTHROPIC_BASE_URL": "https://ai.afp.net",
"ANTHROPIC_AUTH_TOKEN": "sk-xxxxxxxx",
"ANTHROPIC_MODEL": "claude-sonnet-4-6",
"ANTHROPIC_SMALL_FAST_MODEL": "claude-haiku-4-5",
"CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC": "1"
}
}Then run claude in your project directory. You can also switch models inside a session with /model, for example claude-opus-4-8.
If you run into errors such as
invalid beta flag, add the environment variable"CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS": "1".
17.2 Cherry Studio
- Open "Settings → Model Providers" and click "Add" at the bottom.
- Choose New API as the provider type (choose OpenAI if that option is not available).
- Set the API address to
https://ai.afp.netand the API key tosk-xxxxxxxx. - Click "Check" to test connectivity, then click "Manage" to fetch and select models.
If you want to use the native Claude protocol, add another provider of type Anthropic and use
https://ai.afp.netas the API address as well.
17.3 Cursor
- Open "Settings → Models".
- Enter
sk-xxxxxxxxunder OpenAI API Key, turn on Override OpenAI Base URL, and enterhttps://ai.afp.net/v1. - Click "Add model" and add model IDs manually, for example
claude-sonnet-4-6,gemini-3.1-pro, orkimi-k3, then click Verify.
Cursor's requests are sent from Cursor's servers, so
https://ai.afp.netmust be reachable from the public internet. Some features, such as Tab completion, always use Cursor's own models and do not go through your custom key.
17.4 Cline / Roo Code (VS Code)
Option A: OpenAI Compatible
- API Provider:
OpenAI Compatible - Base URL:
https://ai.afp.net/v1 - API Key:
sk-xxxxxxxx - Model ID:
claude-sonnet-4-6(or another model)
Option B: Anthropic (when using only Claude; natively supports prompt caching and thinking)
- API Provider:
Anthropic - Check "Use custom base URL" and enter
https://ai.afp.net - API Key:
sk-xxxxxxxx
17.5 LobeChat
"Settings → Language Models → OpenAI":
- API Key:
sk-xxxxxxxx - API proxy address:
https://ai.afp.net/v1 - Click "Fetch model list", select the models you need, and save.
17.6 Gemini CLI
npm install -g @google/gemini-cliAdd the following to ~/.gemini/.env or ~/.env:
GOOGLE_GEMINI_BASE_URL=https://ai.afp.net
GEMINI_API_KEY=sk-xxxxxxxx
GEMINI_MODEL=gemini-3.1-proThen run gemini.
17.7 Other OpenAI-Compatible Clients
Tools such as NextChat, Chatbox, ChatWise, Open WebUI, Dify, FastGPT, and Immersive Translate generally let you choose the "OpenAI / OpenAI compatible" provider type, then enter the Base URL and Key. For how to fill in the Base URL, see §3.
18. Error Codes and Troubleshooting
18.1 Error Response Format
OpenAI-compatible endpoints return the following on error:
{
"error": {
"message": "Error description",
"type": "new_api_error",
"code": "model_not_found",
"param": ""
}
}The Anthropic and Gemini native endpoints try to return errors in their own protocol formats. When troubleshooting, record the complete error.message and the request ID from the response headers, if present.
18.2 HTTP Status Codes
| Status code | Meaning | Common causes and handling |
|---|---|---|
| 400 | Invalid request parameters | Malformed JSON, a missing required field (such as max_tokens for Claude), or a parameter the model does not support. Remove optional parameters first and reproduce with a minimal request |
| 401 | Authentication failed | The key is wrong, the Bearer prefix is missing, or the token was deleted or disabled. Check how the request header is written |
| 403 | No permission or insufficient credits | Insufficient account balance, an exhausted or expired token credit limit, a token that is not allowed to use the model, or an IP outside the allowlist |
| 404 | Path not found | The Base URL has an extra or missing /v1, or the path is misspelled. It may also mean the endpoint is not supported by the platform (for example Gemini :countTokens) |
| 413 | Request body too large | The image or text is too large; compress or split it |
| 429 | Too many requests | The platform or upstream rate limit was triggered; retry with exponential backoff, see §19 |
| 500 | Internal server error | Retry later. If it persists, contact support with the time and model |
| 501 | Not implemented | The endpoint is not supported by the platform, such as /v1/files or /v1/fine-tunes |
| 502 / 503 | Upstream unavailable | The upstream channel is failing, or there is no available channel for the model in the current group. Retry later or switch groups |
| 504 | Timeout | Generation took too long, for example synchronous image generation or long reasoning. Switch to streaming or asynchronous processing; an image task may still be running in the background |
18.3 Typical Errors and Troubleshooting
| Error keyword (example) | Cause | Solution |
|---|---|---|
无效的令牌 / invalid token | The key is wrong or has been deleted | Copy the key again and check for extra spaces |
额度不足 / quota / insufficient | Insufficient account balance or token credits | Top up, or raise the token's credit limit |
当前分组 xxx 下对于模型 xxx 无可用渠道 / model_not_found | The model name is misspelled, or the token's group does not have this model | Verify the model name with /v1/models, or switch groups in the token settings |
该令牌无权使用模型 | The token has an "available models" restriction | Edit the token and allow the model |
Related to max_tokens | Claude is missing max_tokens, or thinking's budget_tokens ≥ max_tokens | Add the parameter, or increase max_tokens |
Empty content with finish_reason: length | The reasoning model spent the token budget on thinking | Increase max_tokens |
| Streaming output arrives all at once | The proxy or gateway buffers | Disable reverse proxy buffering; add -N to curl |
18.4 Troubleshooting Checklist
- First verify the key and Base URL with the minimal curl request from §1.4 in this document.
- Use
GET /v1/modelsto confirm that the model name matches exactly, paying attention to case and date suffixes. - Remove all optional parameters, then add them back one by one to find the incompatible one.
- Check the model, usage, latency, and error information for that request in the console's "Usage Logs".
- When contacting support, provide: the time (with time zone), model, endpoint path, full error message, and request ID. Do not provide the full key.
19. Tokens, Groups, and Rate Limits
19.1 Token (API Key) Settings
| Setting | Effect |
|---|---|
| Expiration time | The token stops working automatically after it expires |
| Credit limit / unlimited credits | Caps how much this token can spend; useful when distributing to team members or sub-projects |
| Available models | Only models on the allowlist can be called |
| IP allowlist | Only the specified IPs or subnets may use the token |
| Token group | Determines which channel group requests use and which billing multiplier applies |
We recommend creating a separate token for each application or environment and using credit limits to control risk.
19.2 Groups
The platform organizes available channels and billing multipliers into "groups" — for example, a default group, a high-availability group, or a discounted group. The console is authoritative.
- Different groups may differ in price multiplier, stability, and available models.
- A token can specify a group. If it does not, the account's default group is used.
- If a model has no available channel in the current group, you get a "no available channel" error; switching groups resolves it.
19.3 Rate Limits
- The platform may limit request rates by user, token, or model; upstream vendors also impose their own rate and concurrency limits.
- When a limit is exceeded, the API returns 429. Retry with exponential backoff plus random jitter, for example waiting 1s → 2s → 4s → 8s, for at most 5 attempts. Do not retry immediately and indefinitely.
- For high-concurrency or large batch workloads, estimate usage first. If you need a higher quota, contact support to have it enabled.
import random, time
from openai import OpenAI, RateLimitError, APIStatusError
def chat_with_retry(client: OpenAI, max_retries: int = 5, **kwargs):
for i in range(max_retries):
try:
return client.chat.completions.create(**kwargs)
except RateLimitError:
pass
except APIStatusError as e:
if e.status_code < 500:
raise # 4xx (other than 429) will not succeed on retry; re-raise
time.sleep(2 ** i + random.random())
raise RuntimeError("retry attempts exhausted")The official OpenAI SDK retries automatically; configure the count and timeout with
OpenAI(max_retries=3, timeout=600).
19.4 Usage Queries
- Console: "Usage Logs" shows the model, token usage, charges, and latency of each request; the "Dashboard" shows consumption trends.
- API: call
GET https://ai.afp.net/api/usage/token/with a model token (headerAuthorization: Bearer sk-xxx) to query that token's credit usage.
20. Billing
This document does not list specific prices. All prices are subject to the real-time data on the console's "Model Plaza / Pricing" page.
20.1 Billing Modes
| Mode | Typically used for | Description |
|---|---|---|
| Usage-based (token) billing | Text chat, reasoning, vision understanding | Input and output tokens are priced separately; output usually costs more per token |
| Per-request billing | Some image / video models | A fixed amount of credits is charged for each successful request |
| Per-second / per-spec billing | Video generation | Depends on duration, resolution, whether audio is generated, and other parameters |
20.2 Factors That Affect Cost
- Model unit price: prices vary widely between models. Flash / Lite / Haiku models are much cheaper than Pro / Opus models.
- Group multiplier: different groups have different multipliers; see §19.2.
- Caching: input tokens that hit the prompt cache usually get a discount. Writing to the cache (Claude) may be billed at a higher multiplier.
- Reasoning tokens: the thinking process is generally billed as output tokens.
- Multimodal input: images are converted to tokens for billing, and larger images consume more.
20.3 Deduction Flow
- When a request starts, the platform reserves some credits based on the estimated usage.
- When the request finishes, it settles against the actual
usagereturned upstream, refunding or charging the difference. - Failed requests (for example 4xx parameter errors or upstream errors) are generally not charged, or the reserved credits are returned. If a streaming request is interrupted midway, billing is based on the content already generated.
The charge details for every request can be found in "Usage Logs".
20.4 Money-Saving Tips
- Use
*-flash/*-lite/claude-haiku-4-5for simple tasks, and Pro / Opus only for complex ones. - Keep the context short and trim history messages promptly.
- For long, fixed system prompts, enable caching (add
cache_controlfor Claude). - Set a reasonable upper bound for
max_tokensto avoid runaway output. - Set credit limits on every token to limit the damage from a leaked key.
21. FAQ
Q: Should a new project use /v1/chat/completions or /v1/responses? A: On tokenexplore, prefer /v1/chat/completions, which has the best compatibility with all models. Use /v1/responses only after confirming that the target model supports Responses and that you genuinely need Responses features.
Q: Can I call Claude and Gemini with the OpenAI SDK? A: Yes. Just set model to a claude-* or gemini-* model, and the platform converts the protocol automatically. When you need features unique to Claude or Gemini (such as Claude's cache_control or Gemini's native image generation), use the native protocol.
Q: Why does the model's answer about "who are you" not match the model name? A: A model's sense of its own identity comes from its training data, which usually predates the model's release, so it may not know its official name. This does not mean you are calling the wrong model. If you need it to state its identity correctly, spell it out in the system prompt.
Q: The console shows a balance, so why does the API say credits are insufficient? A: Check whether the token's own credit limit has been exhausted. Account balance and token credits are two different things.
Q: Image input does not work? A: Confirm that the model supports visual input; that the image URL is publicly reachable (if unsure, switch to a Data URL); and that the field syntax matches the protocol (see §10). Do not pass image generation endpoint parameters to a chat endpoint.
Q: Why can't I JSON.parse a streaming response directly? A: A streaming response is an SSE event stream. Read the data: events line by line and parse each one separately, stopping at data: [DONE].
Q: I submitted a video task successfully, but the result never shows up? A: Video generation takes time, so keep polling patiently. If it stays in queued for a long time, upstream may be queuing. When it is failed, check error.message. Query with a token under the same account and use the task ID returned by the platform.
Q: What is the difference between claude-sonnet-4-5-20250929 and claude-sonnet-4-6? A: They are different model versions. A date suffix marks a pinned snapshot with more stable behavior, suitable for locking a version in production; a name without a date usually points to the recommended version of that series.
Q: Should I choose kimi-k3 or kimi-k3-usd? A: They are different billing or channel variants of the same model. Compare prices and descriptions on the console's pricing page before choosing.
Q: Are file uploads, fine-tuning, and Assistants supported? A: No. Endpoints such as /v1/files and /v1/fine-tunes return 501. To work with documents, extract their content as text or place it in a message as images.
Q: Is my data stored? A: The platform forwards requests and records the usage logs needed for billing (model, token counts, latency, and so on). For how data is handled, refer to the platform's Privacy Policy and Terms of Service.
Appendix: Endpoint Quick Reference
| Feature | Method | Path | Authentication |
|---|---|---|---|
| Model list | GET | /v1/models | Bearer |
| Chat completions | POST | /v1/chat/completions | Bearer |
| Responses | POST | /v1/responses | Bearer |
| Claude Messages | POST | /v1/messages | x-api-key / Bearer |
| Gemini generate | POST | /v1beta/models/{model}:generateContent | x-goog-api-key / ?key= |
| Gemini streaming | POST | /v1beta/models/{model}:streamGenerateContent?alt=sse | Same as above |
| Image generation | POST | /v1/images/generations | Bearer |
| Image editing | POST | /v1/images/edits | Bearer |
| Video submit (video compatible) | POST | /v1/video/generations | Bearer |
| Video query (video compatible) | GET | /v1/video/generations/{task_id} | Bearer |
| Video submit (OpenAI Videos) | POST | /v1/videos | Bearer |
| Video query (OpenAI Videos) | GET | /v1/videos/{task_id} | Bearer |
| Video download | GET | /v1/videos/{task_id}/content | Bearer |
| Video submit (Seedance official) | POST | /api/v3/contents/generations/tasks | Bearer |
| Video query (Seedance official) | GET | /api/v3/contents/generations/tasks/{task_id} | Bearer |
| Video task list (Seedance official) | GET | /api/v3/contents/generations/tasks | Bearer |
| Video task delete (Seedance official) | DELETE | /api/v3/contents/generations/tasks/{task_id} | Bearer |
| Assets library (Action) | POST | /api/v3/assets?Action=<Action>&Version=2024-01-01 | Bearer |
| Embeddings | POST | /v1/embeddings | Bearer |
| Token usage | GET | /api/usage/token/ | Bearer (model token) |
tokenexplore · If you have questions, contact support through the console.