概述

快速入門

TokSpan 與 OpenAI 相容——只需更改基礎地址即可立即存取 200+ 模型。同時支援 Claude Code / Anthropic 原生與 OpenAI Responses API,Doubao Seedance 影片生成也共用同一把金鑰,詳見下方。

1

註冊並登入

以 Email 在註冊頁面建立帳戶。

2

建立 API Key

在儀表板的 API Keys 頁面上生成 sk- 金鑰。

3

替換 Base URL

將 SDK 的基礎地址指向下方任一基礎地址,並使用您的金鑰呼叫。

4

更新/新增 Model ID

前往 Model Plaza 取得 Model ID,然後更新。

API 基礎地址

TokSpan 支援多種 API 協定。選擇您的 SDK 已在使用的格式 — 所有格式都接受相同的 API Key,並可存取全部 200+ 模型。

OpenAI 相容
https://api.tokspan.com/v1

OpenAI SDK 已含 /v1,會自動拼接 /chat/completions。

Anthropic 原生
https://api.tokspan.com

使用主機根路徑——Claude Code / Anthropic SDK 會自動拼接 /v1/messages。

Video Generation API
https://api.tokspan.com/v1/videos

POST /v1/videos 會建立任務並回傳其 id;請輪詢直到影片就緒,再取得內容。

Responses API
https://api.tokspan.com/v1

複用 OpenAI 相容基礎地址,呼叫 /v1/responses。

OpenAI 相容

python
from openai import OpenAI

client = OpenAI(
    api_key="sk-your-api-key",
    base_url="https://api.tokspan.com/v1",  # ← OpenAI-compatible base URL
)

response = client.chat.completions.create(
    model="MODEL_NAME",  # Replace MODEL_NAME with any model from the catalog
    messages=[{"role": "user", "content": "Hello!"}],
)

print(response.choices[0].message.content)

Anthropic 原生

python
from anthropic import Anthropic

client = Anthropic(
    api_key="sk-your-api-key",
    base_url="https://api.tokspan.com",  # ← Native Anthropic base URL (host root; SDK appends /v1/messages)
)

response = client.messages.create(
    model="MODEL_NAME",  # Replace MODEL_NAME with any Claude model from the catalog
    max_tokens=1024,
    messages=[{"role": "user", "content": "Hello!"}],
)

print(response.content[0].text)

Video Generation API

python
"""
Doubao Seedance video generation - Python example.

Video generation is asynchronous: submit a task, poll it, then download the
result. Requires `pip install requests`.
"""

import time
from typing import Any, Dict, Optional

import requests

# ---------------------------------------------------------------------------
# Configuration
# ---------------------------------------------------------------------------
# Site origin only - no trailing slash and no "/v1/videos" (the paths below are
# appended by the code, e.g. f"{BASE_URL}/v1/videos").
BASE_URL = "https://api.tokspan.com"
API_KEY = "YOUR_API_KEY"               # user token from Console -> Tokens
MODEL = "volcengine/doubao-seedance-2-5-v2"

PROMPT = "A ginger cat walking through a sunlit garden, cinematic, shallow depth of field"
SECONDS = 5             # 1-3600; upstream default is about 10s
RESOLUTION = "1080p"    # 480p / 720p / 1080p / 4k; upstream default is 720p
IMAGE_URL = None        # reference image URL for image-to-video; None = text-to-video

POLL_INTERVAL = 10      # seconds between status checks
POLL_TIMEOUT = 15 * 60  # give up after this many seconds

# A trailing slash would produce "https://host//v1/videos", which the gateway
# answers with its HTML front page (HTTP 200), so normalize it away.
BASE_URL = BASE_URL.rstrip("/")

# Available models, with list price per 1M tokens (billed on actual usage):
#   volcengine/doubao-seedance-2-5-v2            $10.19   best quality
#   volcengine/doubao-seedance-2-0-260128         $6.00
#   volcengine/doubao-seedance-2-0-fast-260128    $4.35   fastest, cheapest


def _headers() -> Dict[str, str]:
    """Auth header. `requests` sets Content-Type automatically for json= bodies."""
    return {"Authorization": f"Bearer {API_KEY}"}


def _check(resp: requests.Response, action: str) -> None:
    """Raise a readable error for any non-2xx response.

    Error codes you may hit:
        pre_consume_token_quota_failed  token balance below the pre-charge
        model_not_found                 no channel serves this model
        get_channel_failed              channel selection failed
        fail_to_fetch_task              upstream error (details inside `message`)
    """
    if resp.ok:
        return
    try:
        body = resp.json()
    except ValueError:
        body = {"message": resp.text[:200]}
    err = body.get("error", body) if isinstance(body, dict) else {"message": str(body)}
    raise RuntimeError(
        f"{action} failed: HTTP {resp.status_code} "
        f"[{err.get('code', '')}] {err.get('message', '')}"
    )


def create_video(
    prompt: str,
    model: str = MODEL,
    seconds: Optional[int] = None,
    resolution: Optional[str] = None,
    image_url: Optional[str] = None,
) -> str:
    """Submit a generation task and return its task id."""
    payload: Dict[str, Any] = {"model": model, "prompt": prompt}
    if seconds is not None:
        payload["seconds"] = seconds
    if resolution is not None:
        payload["resolution"] = resolution
    if image_url:
        payload["input_reference"] = image_url   # "image" is an accepted alias

    resp = requests.post(f"{BASE_URL}/v1/videos", headers=_headers(), json=payload, timeout=60)
    _check(resp, "Submit")

    task_id = resp.json()["id"]
    print(f"Submitted task: {task_id}")
    return task_id


def wait_for_video(task_id: str) -> Dict[str, Any]:
    """Poll until the task finishes: queued -> in_progress -> completed / failed."""
    deadline = time.time() + POLL_TIMEOUT
    while time.time() < deadline:
        resp = requests.get(f"{BASE_URL}/v1/videos/{task_id}", headers=_headers(), timeout=60)
        _check(resp, "Poll")

        data = resp.json()
        status = str(data.get("status", "")).lower()
        print(f"  status={status} progress={data.get('progress', 0)}%")

        if status == "completed":
            return data
        if status in {"failed", "cancelled", "expired"}:
            reason = (data.get("error") or {}).get("message", "unknown reason")
            raise RuntimeError(f"Generation failed: {reason}")

        time.sleep(POLL_INTERVAL)

    raise TimeoutError(f"Timed out after {POLL_TIMEOUT}s, task {task_id} still running")


def download_video(task_id: str, save_path: str) -> str:
    """Download the finished video to `save_path`."""
    resp = requests.get(
        f"{BASE_URL}/v1/videos/{task_id}/content",
        headers=_headers(),
        stream=True,
        timeout=300,
    )
    _check(resp, "Download")

    with open(save_path, "wb") as fh:
        for chunk in resp.iter_content(chunk_size=1 << 16):
            fh.write(chunk)

    print(f"Saved video: {save_path}")
    return save_path


if __name__ == "__main__":
    tid = create_video(PROMPT, seconds=SECONDS, resolution=RESOLUTION, image_url=IMAGE_URL)
    wait_for_video(tid)
    download_video(tid, f"{tid}.mp4")

計費:提交時會預先扣抵配額(seconds × resolution),並於完成後依實際用量結算。5 秒 720p ≈ $1.10、1080p ≈ $2.48、4k ≈ $9.90。餘額低於預扣金額的 Token 會在任務建立前被拒絕。

Responses API

python
from openai import OpenAI

client = OpenAI(
    api_key="sk-your-api-key",
    base_url="https://api.tokspan.com/v1",  # ← Same base URL as OpenAI-compatible
)

response = client.responses.create(
    model="MODEL_NAME",  # Replace MODEL_NAME with any model from the catalog
    input="Hello!",
)

print(response.output_text)
一個金鑰,適用所有格式。所有格式都使用相同的 TokSpan API Key。餘額與用量共用 — 只需呼叫想要的模型名稱。

使用 TokSpan API

無需 SDK——直接以 HTTP 向 OpenAI 相容端點發送請求:

所有請求使用相同格式:POST https://api.tokspan.com/v1/chat/completions,並在 Authorization 標頭中附上您的 API 金鑰。

python
import requests

def chat(prompt: str, model: str = "MODEL_NAME"):  # Replace MODEL_NAME with any model from the catalog
    response = requests.post(
        "https://api.tokspan.com/v1/chat/completions",
        headers={
            "Authorization": "Bearer sk-your-api-key",
            "Content-Type": "application/json",
        },
        json={
            "model": model,
            "messages": [{"role": "user", "content": prompt}],
        },
    )
    return response.json()["choices"][0]["message"]["content"]

print(chat("Hello! What models are available?"))
typescript
const chat = async (prompt: string, model = 'MODEL_NAME') => {  // Replace MODEL_NAME with any model from the catalog
  const res = await fetch('https://api.tokspan.com/v1/chat/completions', {
    method: 'POST',
    headers: {
      'Authorization': 'Bearer sk-your-api-key',
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      model,
      messages: [{ role: 'user', content: prompt }],
    }),
  });
  const data = await res.json();
  return data.choices[0].message.content;
};
shell
curl -X POST "https://api.tokspan.com/v1/chat/completions" \
  -H "Authorization: Bearer sk-your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "MODEL_NAME",
    "messages": [{"role": "user", "content": "Hello!"}]
  }'

跨格式轉換

TokSpan 在背景自動轉換不同的 API 格式。您始終以OpenAI Chat Completions 格式發送請求 — TokSpan 處理到上游供應商原生格式的轉換:

  • OpenAI → Claude Messages API — 系統提示詞、多輪對話及工具定義自動重新映射
  • OpenAI → Gemini API — 內容部件、安全設定及生成配置透明轉換
  • Claude → OpenAI 格式 — Claude 的原生回應被正規化為標準 Chat Completions 回應結構

這意味著您可以使用 OpenAI Python/Node SDK、LangChain 或任何 OpenAI 相容的函式庫來呼叫任何模型 — 無論其原生 API 格式為何。

金鑰範圍限制與權限

每個 API 金鑰可設定特定權限範圍:

  • 模型:限制金鑰可存取的模型
  • 消費上限:設定硬性額度限制 — 額度用盡時金鑰會自動停用
  • IP 白名單:僅允許來自特定 IP 範圍的請求
安全性提醒:絕不在用戶端程式碼中暴露您的 API 金鑰。請使用環境變數或安全的後端代理。所有發送至 TokSpan 的請求均以 TLS 1.3 加密。

接下來做什麼?