LLM API Integration Guide
Last updated: October 17, 2026 (Asia/Shanghai)
Related: Help Center · Image Generation · Video Generation · Full Reference
This guide explains how to connect TokenExplore's text chat, image understanding, tool calling, streaming output, and reasoning to your application. To generate images or videos, see Image Generation and Video Generation, respectively.
1. Choose an Endpoint
On TokenExplore, new projects should prefer OpenAI Chat Completions (/v1/chat/completions): it offers the best compatibility with Claude, Gemini, Kimi, DeepSeek, GLM, Qwen, and other models, and has the widest third-party tool support.
| Endpoint | Path | Use case |
|---|---|---|
| OpenAI Chat Completions (recommended first choice) | POST https://ai.afp.net/v1/chat/completions | Most applications, SDKs, and third-party clients |
| OpenAI Responses | POST https://ai.afp.net/v1/responses | When the target model is confirmed to support Responses; this is a newer protocol, so run a minimal test before integrating |
| Anthropic Messages | POST https://ai.afp.net/v1/messages | Existing Anthropic SDK / Claude Code |
| Gemini native | POST https://ai.afp.net/v1beta/models/{model}:generateContent | Existing Google GenAI SDK / Gemini CLI; also recommended for image generation |
| Gemini streaming | POST https://ai.afp.net/v1beta/models/{model}:streamGenerateContent?alt=sse | Gemini native streaming |
| Model list | GET https://ai.afp.net/v1/models | List models available to the current token |
All protocols share the same API key; only the request headers differ:
| Protocol | Recommended header |
|---|---|
| OpenAI-compatible | Authorization: Bearer sk-xxx |
| Anthropic | x-api-key: sk-xxx + anthropic-version: 2023-06-01 (Bearer also works) |
| Gemini | x-goog-api-key: sk-xxx (?key= or Bearer also works) |
Warning
API keys are created under "Token Management" in the console. Never put them in frontend code, screenshots, logs, or public repositories.
How to set the Base URL:
| Value | Typical users |
|---|---|
https://ai.afp.net | Anthropic SDK, Claude Code, Google GenAI, Gemini CLI, Cherry Studio |
https://ai.afp.net/v1 | Official OpenAI SDK, Cursor, LangChain, most "OpenAI Compatible" clients |
https://ai.afp.net/v1/chat/completions | A few tools that require the full path |
If one form returns a 404, try the other two in turn—an extra or missing /v1 is the most common cause.
2. List Models
export CIYUAN_BASE_URL="https://ai.afp.net"
export CIYUAN_API_KEY="sk-xxxxxxxx"
curl "$CIYUAN_BASE_URL/v1/models" \
-H "Authorization: Bearer $CIYUAN_API_KEY"The response is an OpenAI-format list. Results are limited by the token's available models and token group; rely on what the API actually returns. The Gemini SDK can also use GET https://ai.afp.net/v1beta/models (x-goog-api-key).
Current Models at a Glance (Snapshot)
For pricing, refer to "Model Marketplace / Pricing" in the console; this guide does not list unit prices.
Claude
| Model ID | Notes |
|---|---|
claude-opus-5-5 / claude-opus-5 | Opus flagship tier |
claude-opus-4-8 / claude-opus-4-7 / claude-opus-4-6 | Opus 4.x |
claude-opus-4-6-thinking | Extended thinking enabled by default |
claude-sonnet-5 / claude-sonnet-4-6 | Sonnet; claude-sonnet-4-6 is commonly used for day-to-day development |
claude-sonnet-4-5-20250929 | Dated fixed snapshot |
claude-haiku-4-5 / claude-haiku-4-5-20251001 | Low latency, low cost |
claude-fable-5 / claude-fable-5-1 | Fable series |
Gemini
| Model ID | Notes |
|---|---|
gemini-3.1-pro / gemini-3.1-pro-preview | Pro tier |
gemini-3.7-flash / gemini-3.6-flash / gemini-3.5-flash | Flash tier |
gemini-3.5-flash-lite / gemini-3.1-flash-lite / gemini-2.5-flash-lite | Lite tier |
gemini-2.5-pro | 2.5 Pro |
gemini-3-pro-image / gemini-3.1-flash-image | Image generation / editing, see the Image Generation guide |
Other Text Models
| Family | Model IDs |
|---|---|
| Kimi | kimi-k3, kimi-k3-usd (billing/channel variants; see the console for differences) |
| DeepSeek | deepseek-v4-pro-0813, deepseek-v4-flash, deepseek-v4-1-flash-260910 |
| GLM | glm-5.3, glm-5.3-flash, glm-5.2 |
| Qwen | qwen3.8-max, qwen3.8-flash |
For video models, see the Video Generation guide. Not every model supports vision, tools, reasoning, or Responses—send one minimal request to the target model before integrating.
3. OpenAI Chat Completions (Recommended)
POST https://ai.afp.net/v1/chat/completions
Text 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
}'In the response, choices[0].message.content is the answer and usage reports token usage.
Image Input (Understanding, Not Generation)
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"}}
]
}]
}'For local images, you can use a Data URL: data:image/jpeg;base64,.... The main Claude and Gemini text models generally support vision; for Kimi / Qwen / GLM, refer to the labels in Model Marketplace.
Tool Calling
# pip install -U openai
import os, json
from openai import OpenAI
client = OpenAI(
api_key=os.environ["CIYUAN_API_KEY"],
base_url=os.environ["CIYUAN_BASE_URL"] + "/v1",
)
tools = [{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get the current weather for a given city",
"parameters": {
"type": "object",
"properties": {
"city": {"type": "string", "description": "City name, e.g. Beijing"}
},
"required": ["city"],
},
},
}]
messages = [{"role": "user", "content": "What's the weather in Beijing right now?"}]
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 a real implementation
messages.append({
"role": "tool",
"tool_call_id": call.id,
"content": json.dumps(result, ensure_ascii=False),
})
final = client.chat.completions.create(
model="claude-sonnet-4-6", messages=messages, tools=tools
)
print(final.choices[0].message.content)tool_choice: "auto" / "none" / "required", or force a specific function name.
Streaming Output
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 five-character quatrain about autumn"}]
}'Returns an SSE stream: data: {...} lines, terminated by data: [DONE]. Python:
stream = client.chat.completions.create(
model="deepseek-v4-flash",
messages=[{"role": "user", "content": "Write a five-character quatrain 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)Common Fields
| Field | Notes |
|---|---|
model | Required |
messages | Required; role can be system / user / assistant / tool |
stream / stream_options | Streaming; include_usage returns usage in the final chunk |
max_tokens / max_completion_tokens | Output limit; reasoning also counts against it |
temperature / top_p | Sampling; not supported by some reasoning models |
tools / tool_choice | Tool calling |
response_format | e.g. {"type":"json_object"}, depending on the model |
reasoning_effort | low / medium / high, supported by some models |
finish_reason: stop means normal completion; length means the output limit was reached; tool_calls means a tool call is required; content_filter means the content was filtered.
4. OpenAI Responses
POST https://ai.afp.net/v1/responses
The request body uses input instead of messages, and instructions instead of system.
INFO
Whether a model can use Responses depends on the platform's channel configuration. When calling non-OpenAI models such as Claude / Gemini / Kimi, prefer Chat Completions or the vendor's native protocol unless there is a specific reason not to. If you get a 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": "Introduce TokenExplore in three sentences",
"max_output_tokens": 1024
}'resp = client.responses.create(
model="claude-sonnet-4-6",
input="Introduce TokenExplore in three sentences",
)
print(resp.output_text)| Field | Notes |
|---|---|
model / input | Required |
instructions | System instructions |
stream | Streaming (semantic SSE events) |
max_output_tokens | Output limit |
reasoning | e.g. {"effort":"high"} |
tools | Tools in Responses format (flat fields) |
Check status for completion: completed is normal; for incomplete, see incomplete_details.reason.
Image input (Responses): use {"type":"input_image","image_url":"..."} in input[].content[].
5. Anthropic Messages
POST https://ai.afp.net/v1/messages
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 troubleshoot a goroutine leak?"}
]
}'| Item | Anthropic | Chat Completions |
|---|---|---|
max_tokens | Required | Optional |
| System prompt | Top-level system | role: "system" |
| Images | type: "image" + source | type: "image_url" |
| Tool schema | input_schema | function.parameters |
| Stop reason | stop_reason | finish_reason |
Image URL example: {"type":"image","source":{"type":"url","url":"https://example.com/a.png"}}; for Base64, use source.type = "base64" with media_type / data.
Tools: when stop_reason: "tool_use", take the tool_use block from content; send tool_result back in the next user message.
If the channel supports prompt caching, add "cache_control": {"type":"ephemeral"} to blocks; on a cache hit, cache-related fields appear in usage.
6. Gemini Native API
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 the sea salty?"}]}
],
"generationConfig": {"temperature": 0.7, "maxOutputTokens": 2048}
}'# 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"]), # Do not include /v1beta
)
resp = client.models.generate_content(
model="gemini-3.1-pro",
contents="Why is the sea salty?",
)
print(resp.text)Image input: put {"inlineData":{"mimeType":"image/png","data":"<base64>"}} in parts. The platform does not yet support :countTokens (it returns 404); refer to usageMetadata in the response instead.
7. Reasoning / Thinking
Thinking consumes tokens (usually billed as output) and counts against max_tokens.
Claude native:
{
"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_tokens, and generally ≥ 1024- Once enabled, do not set sampling parameters such as
temperature/top_k - You can also use
claude-opus-4-6-thinkingdirectly; with Chat Completions, reasoning usually appears inreasoning_content
Gemini: With Chat Completions you can pass reasoning_effort; natively you can use generationConfig.thinkingConfig (e.g. thinkingBudget, includeThoughts; the Gemini 3 series also has thinkingLevel).
DeepSeek / Kimi / GLM / Qwen: via Chat Completions; if there is reasoning, it usually appears in reasoning_content.
Common pitfalls: empty content with finish_reason: length → max_tokens is too small and was entirely consumed by reasoning; parameter errors → remove sampling parameters first; slow first token → use streaming.
8. SDK Quick Reference
# OpenAI SDK — Chat Completions
from openai import OpenAI
client = OpenAI(api_key="sk-xxxxxxxx", base_url="https://ai.afp.net/v1")
print(client.chat.completions.create(
model="kimi-k3",
messages=[{"role": "user", "content": "Hello"}],
).choices[0].message.content)# Anthropic SDK — note: base_url must not include /v1
import anthropic
claude = anthropic.Anthropic(api_key="sk-xxxxxxxx", base_url="https://ai.afp.net")
msg = claude.messages.create(
model="claude-sonnet-4-6", max_tokens=1024,
messages=[{"role": "user", "content": "Hello"}],
)
print(msg.content[0].text)# LangChain
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)Environment variables:
export OPENAI_API_KEY="sk-xxxxxxxx"
export OPENAI_BASE_URL="https://ai.afp.net/v1"
export ANTHROPIC_API_KEY="sk-xxxxxxxx"
export ANTHROPIC_BASE_URL="https://ai.afp.net"9. Errors and Troubleshooting (Summary)
| Status code | Common cause |
|---|---|
| 400 | JSON/parameter error; Claude is missing max_tokens; the model does not support a parameter |
| 401 | Wrong key, missing Bearer , or the token was deleted |
| 403 | Insufficient balance/token quota, model not authorized, or IP allowlist |
| 404 | Extra or missing /v1 in the Base URL, or the path does not exist |
| 429 | Rate limited → retry with exponential backoff |
| 502/503 | No available channel upstream or in the current group |
| 504 | Timeout → switch to streaming or increase the timeout |
Troubleshooting order: minimal curl → GET /v1/models to verify the model name → remove optional parameters → check "Usage Logs" in the console. When contacting support, provide the time (specify Asia/Shanghai), the model, the path, and the error message; do not send the full key.
For more error codes and details about tokens/groups, see the Full Reference.
10. FAQ for This Page
Should a new project use /v1/chat/completions or /v1/responses?
On TokenExplore, prefer Chat Completions. Use Responses only after confirming the model supports it and you need Responses features.
Can I call Claude / Gemini with the OpenAI SDK?
Yes, just set model to the corresponding ID. To use capabilities unique to Claude caching, Gemini native image generation, etc., switch to the native protocol.
How do I pass image parameters?
Responses: input_image; Chat Completions: image_url; Anthropic: image + source; Gemini: inlineData. Either a public URL or a Data URL works.
Why does my image request have no effect?
Confirm the model supports vision; make sure the URL is publicly accessible (otherwise use a Data URL); don't stuff image-generation API parameters into the chat endpoint.
Why does a reasoning model report an unsupported parameter?
First remove temperature, top_p, presence_penalty, frequency_penalty, logprobs, etc., then add them back one by one.
Why can't I JSON.parse a streaming response directly?
It's an SSE event stream; read data: lines one by one and stop at [DONE].
The console shows a balance, but the API says the quota is insufficient?
Check whether the token's own quota limit is exhausted; account balance and token quota are two different things.
Are file uploads, fine-tuning, and Assistants supported?
No; /v1/files, /v1/fine-tunes, etc. may return 501. Extract document content as text or pass it as images.
Previous: Help Center · Next: Image Generation and Editing