Docs

API Documentation

Integration guide for the IWKey unified gateway: available models, quick start, endpoints, code examples, SDK setup, and FAQ.

Text call · cURL
curl https://iwkey.com/v1/chat/completions \ -H "Authorization: Bearer $IWKEY_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "claude-sonnet-4-6", "messages": [ {"role": "user", "content": "Hello"} ] }'
Image generation · cURL
curl https://iwkey.com/v1/images/generations \ -H "Authorization: Bearer $IWKEY_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-image-2", "prompt": "A watercolor fox" }'
Visual model task · cURL
curl https://iwkey.com/videos/v1/videos/generations \ -H "Authorization: Bearer $IWKEY_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "doubao-seedance-2.0", "prompt": "A cinematic product shot", "duration": 5, "resolution": "480p", "aspect_ratio": "16:9" }'

01One key for text, image, and visual models

One place for Claude, ChatGPT, image, and visual model endpoints, model ID lookup, and status; see the models page for the full catalog.

API definition: /docs/openapi.json. Model details follow live /v1/models and the model market.

curl · List models
curl https://iwkey.com/v1/models \ -H "Authorization: Bearer $IWKEY_KEY"

02Three steps to integrate

STEP 01

Request an API key

Log in to the console to create an API key. Bank transfer and VAT invoice supported.

STEP 02

Choose an endpoint

Text models swap the SDK base URL; image generation uses /v1/images/generations; visual models submit tasks through /videos/v1/videos/generations.

STEP 03

Review results and usage

Text and image requests return in real time; visual tasks are polled until complete, then results can be downloaded and roll up into usage records.

03Endpoints

GET

/v1/models

Query current callable model IDs before integrating. ChatGPT model IDs follow this list.

POST

/v1/chat/completions

OpenAI-compatible endpoint. After swapping the base URL, the OpenAI SDK, Cursor, Codex CLI, opencode, and LangChain can all call through here.

POST

/v1/messages

Anthropic Messages-native endpoint. Claude Code and the Anthropic SDK can switch directly to the IWKey base URL.

POST

/v1/images/generations

OpenAI-compatible image generation endpoint. gpt-image-2 offers two fidelity tiers, billed per image; output size is fixed (the size parameter has no effect), and fidelity is fixed by the selected tier.

POST

/videos/v1/videos/generations

Visual model async task endpoint. Submit a prompt, duration, resolution, and aspect ratio to receive a job_id and poll_url.

GET

/videos/v1/videos/jobs/{job_id}

Prefer the poll_url returned by submission; it provides task status, completed output, failure details, and the video download URL.

04Advanced scenario examples

Advanced text capabilities, image generation, and visual model async task examples — each scenario shown in cURL / Python / JavaScript.

cURL · streaming response (SSE)
curl -N https://iwkey.com/v1/chat/completions \ -H "Authorization: Bearer $IWKEY_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "claude-sonnet-4-6", "messages": [{"role": "user", "content": "Write a short poem about clouds"}], "stream": true }'
Python · streaming response
from openai import OpenAI client = OpenAI(base_url="https://iwkey.com/v1", api_key="$IWKEY_KEY") stream = client.chat.completions.create( model="claude-sonnet-4-6", messages=[{"role": "user", "content": "Write a short poem about clouds"}], stream=True, ) for chunk in stream: print(chunk.choices[0].delta.content or "", end="")
JavaScript · streaming response
import OpenAI from "openai"; const client = new OpenAI({ baseURL: "https://iwkey.com/v1", apiKey: process.env.IWKEY_KEY, }); const stream = await client.chat.completions.create({ model: "claude-sonnet-4-6", messages: [{ role: "user", content: "Write a short poem about clouds" }], stream: true, }); for await (const chunk of stream) { process.stdout.write(chunk.choices[0].delta.content ?? ""); }
cURL · visual model async task
# 1. Submit job and capture the returned poll_url POLL_URL=$(curl -sS https://iwkey.com/videos/v1/videos/generations \ -H "Authorization: Bearer $IWKEY_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "doubao-seedance-2.0", "prompt": "A cinematic product shot on a clean studio desk", "duration": 5, "resolution": "480p", "aspect_ratio": "16:9" }' | jq -r '.poll_url') # 2. Poll the exact URL returned by the API curl "$POLL_URL" \ -H "Authorization: Bearer $IWKEY_KEY"
Python · visual model async task
import os import time import requests headers = { "Authorization": f"Bearer {os.environ['IWKEY_KEY']}", "Content-Type": "application/json", } job = requests.post( "https://iwkey.com/videos/v1/videos/generations", headers=headers, json={ "model": "doubao-seedance-2.0", "prompt": "A cinematic product shot on a clean studio desk", "duration": 5, "resolution": "480p", "aspect_ratio": "16:9", }, ) job.raise_for_status() poll_url = job.json()["poll_url"] while True: result = requests.get(poll_url, headers=headers) result.raise_for_status() data = result.json() if data["status"] in {"completed", "failed", "cancelled", "timeout"}: print(data) break time.sleep(5)
JavaScript · visual model async task
const headers = { Authorization: `Bearer ${process.env.IWKEY_KEY}`, "Content-Type": "application/json", }; const submit = await fetch("https://iwkey.com/videos/v1/videos/generations", { method: "POST", headers, body: JSON.stringify({ model: "doubao-seedance-2.0", prompt: "A cinematic product shot on a clean studio desk", duration: 5, resolution: "480p", aspect_ratio: "16:9", }), }); const job = await submit.json(); while (true) { const res = await fetch(job.poll_url, { headers }); const result = await res.json(); if (["completed", "failed", "cancelled", "timeout"].includes(result.status)) { console.log(result); break; } await new Promise((resolve) => setTimeout(resolve, 5000)); }
Video task parameter reference
Parameter Type Description
model string doubao-seedance-2.0 or doubao-seedance-2.0-fast
prompt string Text prompt, required
duration integer Duration in seconds, range 4–15
resolution string 480p / 720p / 1080p (fast does not support 1080p)
image_urls array ≤9 Reference images. Each item is either a URL string (default role = first_frame, image-to-video) or an object {"url":"…","role":"first_frame|last_frame|reference_image"}
video_urls array ≤3 Reference videos
audio_urls array ≤3 Reference audio; cannot be used alone — must pair with a role:"reference_image" image or video_urls; first/last-frame images cannot be combined with audio
generate_audio boolean Whether output includes audio, default false
cURL · with reference image and audio
curl https://iwkey.com/videos/v1/videos/generations \ -H "Authorization: Bearer $IWKEY_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "doubao-seedance-2.0", "prompt": "Product showcase video, soft lighting, slow push-in", "duration": 5, "resolution": "720p", "image_urls": [ {"url": "https://example.com/product.jpg", "role": "reference_image"} ], "audio_urls": ["https://example.com/bgm.mp3"], "generate_audio": true }'

Billing: settled per task; on success, reconciled against upstream usage.total_tokens with a credit or charge adjustment; failed tasks are fully refunded.

cURL · image generation
curl https://iwkey.com/v1/images/generations \ -H "Authorization: Bearer $IWKEY_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-image-2", "prompt": "A watercolor fox in a misty forest" }'
Python · image generation
from openai import OpenAI client = OpenAI(base_url="https://iwkey.com/v1", api_key="$IWKEY_KEY") image = client.images.generate( model="gpt-image-2", prompt="A watercolor fox in a misty forest", ) print(image.data[0])
JavaScript · image generation
import OpenAI from "openai"; const client = new OpenAI({ baseURL: "https://iwkey.com/v1", apiKey: process.env.IWKEY_KEY, }); const image = await client.images.generate({ model: "gpt-image-2", prompt: "A watercolor fox in a misty forest", }); console.log(image.data[0]);
Image generation parameters
Parameter Type Description
model string gpt-image-2, required
prompt string Text description, required
size string Optional, accepted only for OpenAI SDK compatibility — currently has no effect on the output. Both tiers produce the same output size, approximately 1536×1024px.
quality string Optional, accepts low/medium/high — fidelity is fixed by the selected tier (Standard = medium, Fine = high); a value sent in the request does not override the tier.

Billing: billed per image, Standard / Fine fidelity tiers; synchronous request, no streaming; full pricing on the model market · gpt-image-2.

cURL · tool calling (Function Calling)
curl https://iwkey.com/v1/chat/completions \ -H "Authorization: Bearer $IWKEY_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "claude-sonnet-4-6", "messages": [{"role": "user", "content": "What's the weather like in Hangzhou?"}], "tools": [{ "type": "function", "function": { "name": "get_weather", "parameters": { "type": "object", "properties": {"city": {"type": "string"}} } } }] }'
Python · tool calling
from openai import OpenAI client = OpenAI(base_url="https://iwkey.com/v1", api_key="$IWKEY_KEY") resp = client.chat.completions.create( model="claude-sonnet-4-6", messages=[{"role": "user", "content": "What's the weather like in Hangzhou?"}], tools=[{ "type": "function", "function": { "name": "get_weather", "parameters": { "type": "object", "properties": {"city": {"type": "string"}}, }, }, }], ) print(resp.choices[0].message.tool_calls)
JavaScript · tool calling
import OpenAI from "openai"; const client = new OpenAI({ baseURL: "https://iwkey.com/v1", apiKey: process.env.IWKEY_KEY, }); const resp = await client.chat.completions.create({ model: "claude-sonnet-4-6", messages: [{ role: "user", content: "What's the weather like in Hangzhou?" }], tools: [{ type: "function", function: { name: "get_weather", parameters: { type: "object", properties: { city: { type: "string" } }, }, }, }], }); console.log(resp.choices[0].message.tool_calls);

The platform provides only official standard model IDs — suffix variants like -thinking/-high/-low are not offered. Reasoning is controlled via API parameters — Anthropic uses the thinking object, OpenAI uses reasoning_effort.

Anthropic Claude · Enable Extended Thinking
curl https://iwkey.com/v1/messages \ -H "Authorization: Bearer $IWKEY_KEY" \ -H "Content-Type: application/json" \ -H "anthropic-version: 2023-06-01" \ -d '{ "model": "claude-opus-4-7", "max_tokens": 16000, "thinking": { "type": "enabled", "budget_tokens": 10000 }, "messages": [{ "role": "user", "content": "Explain the mechanism of quantum entanglement" }] }' # thinking.budget_tokens controls reasoning depth (1024 ~ 100000) # max_tokens must be > budget_tokens, to leave room for output # Supported: claude-opus-4-7, claude-opus-4-8, and other models with extended thinking
Python · Anthropic SDK
import anthropic client = anthropic.Anthropic( base_url="https://iwkey.com", api_key="$IWKEY_KEY", ) resp = client.messages.create( model="claude-opus-4-7", max_tokens=16000, thinking={ "type": "enabled", "budget_tokens": 10000, }, messages=[{"role": "user", "content": "Explain the mechanism of quantum entanglement"}], ) for block in resp.content: if block.type == "thinking": print("[thinking]", block.thinking[:200], "...") elif block.type == "text": print("[answer]", block.text)
OpenAI · reasoning_effort parameter
curl https://iwkey.com/v1/chat/completions \ -H "Authorization: Bearer $IWKEY_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-5.5", "reasoning_effort": "high", "messages": [{ "role": "user", "content": "Explain the mechanism of quantum entanglement" }] }' # reasoning_effort: "low" | "medium" | "high" (default: medium) # Applies to gpt-5.5 and other reasoning models; check /v1/models for current IDs
Python · OpenAI SDK
from openai import OpenAI client = OpenAI(base_url="https://iwkey.com/v1", api_key="$IWKEY_KEY") resp = client.chat.completions.create( model="gpt-5.5", reasoning_effort="high", messages=[{"role": "user", "content": "Explain the mechanism of quantum entanglement"}], ) print(resp.choices[0].message.content)

05Client SDK configuration examples

Claude Code

All Claude models
ANTHROPIC_BASE_URL must stop at the hostname https://iwkey.com — do not add /v1 (unlike OpenAI; adding it would form /v1/v1/messages → 404)
# Set environment variables, then launch directly export ANTHROPIC_BASE_URL=https://iwkey.com export ANTHROPIC_API_KEY=your-iwkey-key claude

Cursor

Claude + ChatGPT
⚠ Base URL must include /v1 (Cursor appends /chat/completions directly to this OpenAI-protocol endpoint — unlike Claude Code's ANTHROPIC_BASE_URL, which does not take /v1)
# Settings → Models → Override OpenAI Base URL Base URL: https://iwkey.com/v1 API Key: your-iwkey-key # Claude: use claude-sonnet-4-6 directly; ChatGPT model IDs: check /v1/models first

Anthropic Python SDK

All Claude models
import anthropic client = anthropic.Anthropic( base_url="https://iwkey.com", api_key="your-iwkey-key", ) msg = client.messages.create( model="claude-sonnet-4-6", max_tokens=1024, messages=[{"role": "user", "content": "Hello"}], )

OpenAI Python SDK

Claude + ChatGPT
# Swap providers by changing the model name from openai import OpenAI client = OpenAI( base_url="https://iwkey.com/v1", api_key="your-iwkey-key", ) # Claude resp = client.chat.completions.create( model="claude-sonnet-4-6", messages=[{"role": "user", "content": "Hello"}], ) # ChatGPT — same client; check /v1/models for the current model ID resp = client.chat.completions.create( model="gpt-5.5", messages=[{"role": "user", "content": "Hello"}], )

Visual model API

Seedance / Video tasks
# Standard HTTP call: submit a task, then poll poll_url curl https://iwkey.com/videos/v1/videos/generations \ -H "Authorization: Bearer your-iwkey-key" \ -H "Content-Type: application/json" \ -d '{"model":"doubao-seedance-2.0","prompt":"A product video","duration":5,"resolution":"480p","aspect_ratio":"16:9"}'

Image generation API

gpt-image-2 / per image
# synchronous request, billed per image across two fidelity tiers curl https://iwkey.com/v1/images/generations \ -H "Authorization: Bearer your-iwkey-key" \ -H "Content-Type: application/json" \ -d '{"model":"gpt-image-2","prompt":"A watercolor fox"}'

Codex CLI

ChatGPT series (Responses API)
base_url must stop at /v1 — do not add /chat/completions, or Codex will build a bad path and return 404
⚠ Use model gpt-5.5. Codex uses the Responses API (not chat/completions) — declare wire_api = "responses" in config.toml
# ~/.codex/config.toml model = "gpt-5.5" model_provider = "iwkey" [model_providers.iwkey] name = "IWKey" base_url = "https://iwkey.com/v1" wire_api = "responses" env_key = "IWKEY_KEY"
export IWKEY_KEY="sk-your-api-key" codex

opencode

Claude + ChatGPT
options.baseURL must include /v1@ai-sdk/openai-compatible appends /chat/completions directly and does not add /v1 for you
# ~/.config/opencode/opencode.json { "$schema": "https://opencode.ai/config.json", "provider": { "iwkey": { "npm": "@ai-sdk/openai-compatible", "name": "IWKey", "options": { "baseURL": "https://iwkey.com/v1", "apiKey": "{env:IWKEY_KEY}" }, "models": { "claude-sonnet-4-6": { "name": "Claude Sonnet 4.6" }, "gpt-5.5": { "name": "GPT-5.5" } } } } }
# Set the env var, then run /connect to store the credential under provider id "iwkey" export IWKEY_KEY="your-iwkey-key" opencode # In the TUI: /connect → Other → paste your key under provider id "iwkey" # Then /models to pick one of the models configured above

LangChain

All Claude models
from langchain_anthropic import ChatAnthropic llm = ChatAnthropic( base_url="https://iwkey.com", api_key="your-iwkey-key", model="claude-sonnet-4-6", )

06Frequently asked questions

How do I verify I'm using the real model?
Every response carries the upstream native fields (Anthropic's request_id, OpenAI's id and system_fingerprint), identical to a direct upstream call. You can also visit the transparency page to inspect the database schema and confirm we do not store any prompt or response content.
How is billing handled?
Text models are billed by token usage; images are billed per image (Standard / Fine fidelity tiers); visual models are billed by task, duration, and upstream model. Log in to the console to see real-time balance, usage detail, and exports grouped by model / date / key. Bank transfer and VAT invoice are supported.
Do you store my prompts and responses?
The text and image channels do not persist prompt, response, or generated-image bodies in the regular database layer. Visual models are async tasks — task parameters, status, cost, result URL, and failure reason are kept for task tracking and billing. See the transparency page for the full boundary.
Do you support streaming?
Text models support Anthropic-native SSE streaming and OpenAI-compatible streaming — set "stream": true in the request. Image generation is a synchronous request and does not stream. Visual models are not streaming responses; after submission, prefer the response's poll_url. Its current canonical path is /videos/v1/videos/jobs/{job_id}.
How do I call visual models?
Use the same API key to call POST /videos/v1/videos/generations. The response returns job_id and poll_url. After the task completes, check the polling response or "Visual model usage detail" for the result, download URL, and failure reason.
How do I enable Extended Thinking or reasoning_effort?
The platform provides only official standard model IDs — suffix variants like -thinking/-high/-low are not offered. Reasoning depth is controlled via API parameters:
  • Anthropic Claude: add "thinking": {"type": "enabled", "budget_tokens": 10000} to the request body (budget: 1024–100000 tokens). max_tokens must exceed budget_tokens. Supported models: claude-opus-4-7, claude-opus-4-8, and others.
  • OpenAI (reasoning models): add "reasoning_effort": "high" to the request body (options: low/medium/high). Supported models: gpt-5.5 and others — check GET /v1/models to confirm.
See the full example under the Code Examples → Extended Thinking tab.
How do I enable Prompt Caching?
💡 Prompt Caching: Claude models support prompt caching, saving up to ~90% on input cost for repeated long context (system prompts, agent loops, codebase context, etc). To enable: use the Anthropic-native /v1/messages format and mark cache_control breakpoints in the messages or system prompt. Note: the OpenAI-compatible /v1/chat/completions format does not support Claude prompt caching — every request is billed at full input price. Caching is unnecessary for one-off or always-different requests.
How is this different from calling Anthropic / OpenAI directly?
The text and image channels keep the Anthropic / OpenAI compatible protocol surface, including streaming and tool calling. Visual models are unified across multiple video upstreams via the IWKey async task endpoint. The difference: one key, balance, and usage record across both, plus CNY bank transfer and VAT invoice support.