Quick start
- Sign up, sign in, and add funds on Billing. Model requests need an account balance above zero.
- Create a key on API keys and choose a service group. The group decides which models the key can use, what they cost, and which clients are allowed. See Models & pricing for details.
- Configure your client as described in Client setup: Anthropic-style clients such as Claude Code use
https://api.thinkapi.org, and OpenAI-compatible clients such as Codex usehttps://api.thinkapi.org/v1. Once signed in, you can also check your settings in Connection setup. - After sending a request, save the
X-Request-Idresponse header. In the Portal, open/app/requests/{request_id}(replace{request_id}with that value) to confirm the outcome and cost, or find the request by time and model in Requests.
Base URLs and authentication
Every endpoint is served from one gateway at https://api.thinkapi.org and authenticated with an API key created in the Portal.
Base URLs
| Client type | Base URL | Paths the client appends |
|---|---|---|
| Anthropic-style clients and SDKs, such as Claude Code | https://api.thinkapi.org | /v1/messages |
| OpenAI-compatible clients and SDKs, such as Codex | https://api.thinkapi.org/v1 | /responses, /chat/completions, and so on |
Stick to these two forms. Keep /v1 for OpenAI-compatible clients: without it, /chat/completions, /responses, /images/generations, and GET /models still work, but /embeddings returns 404 unknown_route. Anthropic-style clients add /v1 on their own, so a base URL that already ends in /v1 doubles the segment and also returns 404.
Authentication
Use a key created on the API keys page; your Portal sign-in session does not work for API requests. Recommended headers by protocol:
| Protocol | Recommended headers |
|---|---|
| OpenAI (Chat Completions, Responses, Embeddings, Images) | Authorization: Bearer <key> |
| Anthropic Messages | x-api-key: <key>, plus anthropic-version: 2023-06-01 |
| Balance and model list | Authorization: Bearer <key> |
These are recommendations, not requirements: the gateway accepts the same set of authentication headers on every endpoint. With an official client, keep its default behavior.
- Only
Authorizationstrips aBearerprefix. AddingBearertox-api-keyor any other header returns 401invalid_api_key. - Do not put your key in URL query parameters; proxies and access logs record URLs.
- The gateway sends no CORS headers, and browser
OPTIONSpreflight requests get a 404. Call the API from your server, and never ship your key in front-end code. - Keys look like
sk-ThinkAPI_…, though older keys may use another format. You can usually copy a key again from the API keys page; some older keys cannot be revealed, so create a new key if you need the full value. Keep your keys private, and disable or delete a key right away if it leaks.
Check your balance
The two self-check commands in this section and the next are free and confirm that your key and base URL are correct. First store your key in the THINKAPI_KEY environment variable, as Connection setup does:
export THINKAPI_KEY='<your-api-key>'$env:THINKAPI_KEY = '<your-api-key>'GET /v1/usage returns the amount you can currently spend and this key's credit usage. It works even for a key that has reached its credit limit.
curl -sS "https://api.thinkapi.org/v1/usage" \
-H "Authorization: Bearer $THINKAPI_KEY"$headers = @{ Authorization = "Bearer $env:THINKAPI_KEY" }
Invoke-RestMethod -Uri "https://api.thinkapi.org/v1/usage" -Headers $headers | ConvertTo-Json -Depth 5{
"balance": 12.5,
"isValid": true,
"is_active": true,
"key": {
"limited": true,
"quota_micros": 20000000,
"remaining_micros": 12500000,
"used_micros": 7500000
},
"quota": {
"remaining": 12.5,
"unit": "USD"
},
"remaining": 12.5,
"unit": "USD"
}remaining,balance, andquota.remaininghold the same amount: what you can spend right now, in the currency given byunit(your account currency, USD by default). If the key has a credit limit, this is the lower of your account balance and the key's remaining credit; otherwise it equals your account balance, shown as 0 when the balance is negative.key.limited: whether this key has a credit limit.key.quota_micros,key.used_micros, andkey.remaining_micros: the key's credit limit, amount used, and credit remaining, in micros (1,000,000 micros = 1 currency unit). Without a credit limit,key.remaining_microsis 0, which does not mean nothing is left.- Responses from this endpoint do not include
X-Request-Id.
List models
GET /v1/models lists the models currently available to this key's service group. It uses the THINKAPI_KEY variable set above.
curl -sS "https://api.thinkapi.org/v1/models" \
-H "Authorization: Bearer $THINKAPI_KEY"$headers = @{ Authorization = "Bearer $env:THINKAPI_KEY" }
(Invoke-RestMethod -Uri "https://api.thinkapi.org/v1/models" -Headers $headers).data | Select-Object id, owned_by{
"object": "list",
"data": [
{
"id": "<model-id>",
"object": "model",
"owned_by": "anthropic",
"display_name": "<model name>"
}
]
}- The format does not depend on the protocol you use: it is always the OpenAI-style list above.
owned_by,display_name, anddescriptionmay be omitted. - Results are filtered by the key's service group, so moving the key to another group changes the list. See Models & pricing for prices and supported protocols.
- It is free and works even when your account balance is negative.
- It lists the models the platform has synchronized and enabled. It does not query upstream providers, so it answers quickly, and it reflects the platform's latest synchronization rather than live upstream state, so do not use it as a health check. If the model list cannot be read, it returns 502
models_unavailable.
Client setup
This section shows how to connect the official Claude Code and Codex clients to ThinkAPI, and how to import the settings from the Portal in one step with CC Switch. Check the Client access field in the model's details on Models & pricing, then pick a setup from the table below.
| Client | Recommended setup | Groups it works with |
|---|---|---|
| Claude Code (CLI, JetBrains plugin, VS Code extension, desktop app) | Set the gateway URL and API key; for the CLI, put them in ~/.claude/settings.json. See Claude Code: recommended setup | Native client, Standard API |
| Codex (CLI, IDE extension) | Option 1: point the built-in provider at ThinkAPI and sign in with your API key. See option 1 | Native client, Standard API |
| Codex, keeping your ChatGPT sign-in | Option 2: a custom provider. See option 2 | Native client, Standard API (switch to option 1 on 403 non_client_disabled) |
| CC Switch for Claude Code | Choose Import to CC Switch in the Portal. See One-click import with CC Switch | Native client, Standard API |
| CC Switch for Codex | Choose Import to CC Switch in the Portal. See importing into Codex | Native client, Standard API |
The last column refers to the Client access field in a model's details. Groups marked Native client accept only official clients; groups marked Standard API also accept other requests that follow the protocol; for Unconfirmed, contact support first. See Client restrictions by group.
Claude Code: recommended setup
Claude Code connects through two environment variables: set ANTHROPIC_BASE_URL to https://api.thinkapi.org and ANTHROPIC_AUTH_TOKEN to your API key. Do not add /v1 to the URL: Claude Code appends /v1/messages itself, so another /v1 duplicates the path and returns 404.
| Surface | Where to configure | Supported |
|---|---|---|
| Claude Code CLI | The env block in ~/.claude/settings.json (recommended), or shell environment variables | Yes |
| JetBrains plugin | The plugin runs the CLI, so it uses the CLI settings | Yes |
| VS Code extension | claudeCode.environmentVariables in your VS Code user settings; see other surfaces | Yes |
| Code tab in the Claude desktop app | Help → Troubleshooting → Enable Developer Mode, then Developer → Configure Third-Party Inference; see other surfaces | Yes |
| Claude Code on the web (claude.ai/code), mobile, Slack, Remote Control | Web, mobile and Slack always use Anthropic's own service; Remote Control needs a claude.ai sign-in and is unavailable once the gateway is set | No |
We recommend putting both in your user settings file, ~/.claude/settings.json (%USERPROFILE%\.claude\settings.json on Windows). If the file already exists, merge the two keys into its env block instead of replacing your other settings. Save it and start a new Claude Code session.
{
"env": {
"ANTHROPIC_BASE_URL": "https://api.thinkapi.org",
"ANTHROPIC_AUTH_TOKEN": "sk-ThinkAPI_xxxxxxxx"
}
}Why this setup:
- Values in a settings file's
envblock take precedence over shell variables of the same name, and background agents may not inherit shell variables, while the settings file reaches them too. ANTHROPIC_AUTH_TOKENis sent as anAuthorization: Bearerheader and takes effect without a confirmation prompt.- You can use
ANTHROPIC_API_KEYinstead. It is sent as anx-api-keyheader, which the gateway also accepts, but interactive mode asks you to approve it once.
- Do not put your key in a project's
.claude/settings.json; that file is usually committed to the repository. - A project's
.claude/settings.jsonand.claude/settings.local.json, and managed settings from your administrator, take precedence over user settings, so leftover variables such asANTHROPIC_BASE_URLthere override yours. Use/statusto see the URL and credential in effect.
Claude Code: environment variables, VS Code and desktop
To try it in the current terminal only, set environment variables as the official docs show. They last for that terminal session, and values in a settings file override them.
export ANTHROPIC_BASE_URL=https://api.thinkapi.org
export ANTHROPIC_AUTH_TOKEN=sk-ThinkAPI_xxxxxxxx
claude$env:ANTHROPIC_BASE_URL = "https://api.thinkapi.org"
$env:ANTHROPIC_AUTH_TOKEN = "sk-ThinkAPI_xxxxxxxx"
claudeVS Code extension: run Preferences: Open User Settings (JSON) from the Command Palette and add claudeCode.environmentVariables. The extension's own sign-in check does not read ~/.claude/settings.json, so with only that file it may still ask you to sign in.
{
"claudeCode.environmentVariables": [
{ "name": "ANTHROPIC_BASE_URL", "value": "https://api.thinkapi.org" },
{ "name": "ANTHROPIC_AUTH_TOKEN", "value": "sk-ThinkAPI_xxxxxxxx" }
]
}The Code tab in the Claude desktop app does not take its gateway settings from ANTHROPIC_BASE_URL or ~/.claude/settings.json. Open Help → Troubleshooting → Enable Developer Mode, then Developer → Configure Third-Party Inference. In Connection, set Inference provider to Gateway, enter https://api.thinkapi.org as the Gateway base URL and your API key as the Gateway API key, and leave the rest at their defaults. If your administrator has distributed a configuration, the form is read-only.
Claude Code: choosing a model
Claude Code does not switch to your group's models on its own, and its default model may not be in your group. Choose a model in one of these ways, copying the model ID exactly as shown on Models & pricing or returned by List models:
- Run
/modelin a session and pick or enter the model ID. - Pass it at startup:
claude --model <model-id>. - Make it the default: add
ANTHROPIC_MODELto theenvblock in~/.claude/settings.json, as in the example below. If CC Switch manages that file, edit the provider in CC Switch instead; see Importing into Claude Code.
Optional: set CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY to 1, and Claude Code asks the gateway for your group's models at startup and adds them to the /model list. Note that:
- Only models whose ID contains
claudeoranthropicare listed. - The gateway answers from the models the platform has synchronized, usually well within the default 3-second timeout. On a timeout, Claude Code silently falls back to the previously cached list or its built-in list; on a slow network, raise the timeout with
CLAUDE_CODE_GATEWAY_MODEL_DISCOVERY_TIMEOUT_MS(in milliseconds; requires Claude Code v2.1.269 or later). - It only adds entries; it does not change the default model.
{
"env": {
"ANTHROPIC_BASE_URL": "https://api.thinkapi.org",
"ANTHROPIC_AUTH_TOKEN": "sk-ThinkAPI_xxxxxxxx",
"ANTHROPIC_MODEL": "<model-id>",
"CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY": "1",
"CLAUDE_CODE_GATEWAY_MODEL_DISCOVERY_TIMEOUT_MS": "10000"
}
}Model IDs with a [1m] suffix, such as <model-id>[1m], also work, and Requests shows the ID without the suffix. Whether you actually get a 1M-token context depends on the model, group and upstream provider, and is not guaranteed.
Claude Code: checking the setup
- Run
/statusin Claude Code and check that it shows an "Anthropic base URL" line withhttps://api.thinkapi.organd an "Auth token" line. - Send a real message, then find the request by time and model in Requests and check its status and cost.
Common errors: 403 non_client_disabled (shown as "Please upgrade your Claude client."), 400 unsupported_client_platform, and 400 unsupported_client_feature or unsupported_client_profile are covered in Client restrictions by group. For 401 (invalid key) and 402 (insufficient balance), see Error codes.
Codex: surfaces and setup options
This applies to the Codex CLI and the Codex IDE extension (openai.chatgpt) in VS Code, Cursor and Windsurf. Codex in the ChatGPT desktop app reads the same configuration. Codex cloud, /cloud delegation and Realtime voice do not go through ThinkAPI.
Put the settings in your user-level ~/.codex/config.toml (%USERPROFILE%\.codex\config.toml on Windows; a CLI running in WSL uses the Linux ~/.codex, which is not shared with Windows). Provider-related keys in a project's .codex/config.toml are ignored, so do not put them there. Top-level keys such as model must come before any table header, such as [model_providers.thinkapi].
| Option | How it works | Groups it works with | ChatGPT sign-in |
|---|---|---|---|
| Option 1 (recommended) | Point the built-in provider at ThinkAPI with openai_base_url and sign in with your API key | Native client, Standard API | Turned off on this machine (for that CODEX_HOME) |
| Option 2 | A custom thinkapi provider that reads the key from the THINKAPI_KEY environment variable | Native client, Standard API (switch to option 1 on 403 non_client_disabled) | Both can be used side by side |
If in doubt, use option 1. Both options need the model ID filled in by hand; see Codex: models, checks and troubleshooting.
Codex option 1: API key sign-in
Add these three lines at the top of ~/.codex/config.toml, before any table header. If the file already has a model, openai_base_url or forced_login_method line, change its value instead of adding another line; a duplicate key stops Codex from starting. If you have ever picked a model with /model, the file usually has a model line already.
model = "<model-id>"
openai_base_url = "https://api.thinkapi.org/v1"
forced_login_method = "api"Then sign in with your API key. Store the key in the THINKAPI_KEY environment variable as shown in Check your balance, then run:
printenv THINKAPI_KEY | codex login --with-api-key$Env:THINKAPI_KEY | codex login --with-api-keyThe official docs have no PowerShell example; this version passes the key through standard input in the same way. If you see No API key provided via stdin., THINKAPI_KEY is empty. You can also sign in without the command line: in the IDE extension, choose "Use API Key" on the sign-in screen; in the desktop app, choose "Sign in another way"; then paste your key.
To keep your ChatGPT sign-in in the CLI as well, create a separate directory for ThinkAPI to use as CODEX_HOME (the directory must already exist), put the three lines above in the config.toml inside it, sign in with that CODEX_HOME, and pass it every time you start Codex. In this setup, leave ~/.codex/config.toml unchanged; Codex started without CODEX_HOME keeps using your ChatGPT sign-in. In Windows PowerShell, create the directory, run $Env:CODEX_HOME = "$HOME\.codex-thinkapi", then sign in and start Codex in the same terminal (the variable lasts only for that session).
mkdir -p ~/.codex-thinkapi
# Put the three lines above in ~/.codex-thinkapi/config.toml, then sign in once:
printenv THINKAPI_KEY | CODEX_HOME="$HOME/.codex-thinkapi" codex login --with-api-key
# Start Codex with ThinkAPI; add this prefix every time, or save it as an alias:
CODEX_HOME="$HOME/.codex-thinkapi" codex- Do not leave out
forced_login_method = "api"; without it, a cached ChatGPT sign-in token would be sent to the gateway. - A red "OpenAI base URL is overridden …" warning when you open
/modelin Codex is expected. That list shows Codex's built-in models, not the models in your group. Do not pick from it: your choice is saved tomodelinconfig.tomland replaces the model ID you entered. To switch models for one run, usecodex -m <model-id>. - See Codex authentication in the official docs.
Codex option 2: custom provider
Option 2 leaves your Codex sign-in alone, so it can coexist with a ChatGPT subscription sign-in. Add the following to ~/.codex/config.toml:
model = "<model-id>"
model_provider = "thinkapi"
[model_providers.thinkapi]
name = "ThinkAPI"
base_url = "https://api.thinkapi.org/v1"
env_key = "THINKAPI_KEY"
wire_api = "responses"modelandmodel_providerare top-level keys and must come before any table header. If the file already has amodelormodel_providerline, change its value instead of adding another line; a duplicate key stops Codex from starting.- You do not need
codex login, and do not addrequires_openai_auth.codex login statusshowingNot logged inis expected. - To go back to ChatGPT, delete the top-level
model_providerandmodellines; your sign-in is untouched.
Codex reads the key from the THINKAPI_KEY environment variable. On macOS or Linux, add it to your shell startup file; on Windows, set a user environment variable:
# Add to ~/.zshrc (zsh) or ~/.bashrc (bash), then open a new terminal
export THINKAPI_KEY="sk-ThinkAPI_xxxxxxxx"# Saved for your Windows user; takes effect in new terminals
[Environment]::SetEnvironmentVariable('THINKAPI_KEY', 'sk-ThinkAPI_xxxxxxxx', 'User')
# Current PowerShell session only
$Env:THINKAPI_KEY = 'sk-ThinkAPI_xxxxxxxx'The IDE extension and desktop app may not see shell variables. In that case, put the key in ~/.codex/.env (%USERPROFILE%\.codex\.env on Windows) and restart the editor or desktop app:
THINKAPI_KEY=sk-ThinkAPI_xxxxxxxx.envis plain text; never commit or share it.- Values in
.envoverride shell variables of the same name, and variable names starting withCODEX_are ignored. - For more, see Codex advanced configuration in the official docs.
Codex: models, checks and troubleshooting
With either option, fill in model with a model ID from your group, copied exactly from Models & pricing or List models. Codex does not read the gateway's model list, so the startup notice "Model metadata for <model-id> not found. Defaulting to fallback metadata" means the ID is not in Codex's built-in model catalog and Codex falls back to default model settings.
- To switch models for one run:
codex -m <model-id>. Do not switch with/model: it lists Codex's built-in models, and your choice is saved toconfig.toml, replacing themodelyou entered. - Newer models may require a recent Codex version, so keep Codex on the latest release.
By default Codex passes its environment variables, including THINKAPI_KEY, to the commands it runs. To prevent that, add the filter below to ~/.codex/config.toml (do not also use the older exclude or include_only arrays in the same file; Codex rejects that combination):
[shell_environment_policy.filters]
"THINKAPI_KEY" = "exclude"To check the setup with option 1, first run this command in a terminal; it should show Logged in using an API key.
codex login statusThen start Codex and run /status to check the Model and Model provider entries: openai for option 1, thinkapi for option 2.
/statusThe real test is sending a message and finding the request in Requests. Codex does not show the X-Request-Id. Option 1 sends over WebSocket by default; each turn still appears in Requests but has no request ID returned to the client. Find the request by time and model.
When troubleshooting:
- Start Codex with
codex --strict-configto have unknown configuration keys reported as errors, which catches typos. A configuration imported by CC Switch may contain keys Codex no longer recognizes, such asdisable_response_storage, and they are reported too. Such keys do not affect a normal start without--strict-config, so you can ignore that error or delete the line. - Do not set an
OPENAI_BASE_URLenvironment variable; current Codex versions do not read it. - Do not write
wire_api = "chat"; that value has been removed and causes a configuration error. - Do not write
[model_providers.openai]: built-in providers cannot be overridden, and Codex fails to start with "model_providers contains reserved built-in provider IDs". - Do not use
[profiles.<name>]; Codex stopped reading it in 0.134.0. - Do not point
model_catalog_urlat the gateway; its model list format is not compatible with Codex. - On managed work computers, an administrator may restrict sign-in methods or settings, in which case option 1 may not be available.
One-click import with CC Switch
CC Switch is a desktop app that manages provider settings for Claude Code, Codex and other clients. The Portal can hand it your base URL and API key through a ccswitch:// link, so you do not have to edit configuration files by hand.
- Install: download it from GitHub Releases or the official site, ccswitch.io. On macOS you can also run
brew install --cask cc-switch. - Version: v3.16.0 or later is required, or the Portal's Claude Code options are lost on import. The latest release is recommended.
- Key: its status must be Ready and it must be revealable. If an older key says it cannot be revealed, create a new one.
- Group: the key must belong to a Claude or an OpenAI / Codex group, which imports into CC Switch's Claude or Codex panel respectively.
Start the import from either place:
- On API keys, choose Setup on the key, then Import to CC Switch. Pick the API URL and options in the dialog, then choose Import to CC Switch.
- In Connection setup, set Client to Native client in step 01, then choose Import to CC Switch in step 02.
Then:
- Allow your browser to open CC Switch when it asks.
- Review the name, endpoint and configuration in CC Switch's import confirmation dialog, then choose Import.
- In CC Switch's Claude or Codex panel, choose Enable on the newly imported provider card. The Portal does not enable it for you; it is enabled automatically only if it is the first provider in that panel.
- Start a new Claude Code session, or restart Codex.
CC Switch: importing into Claude Code
For a key in a Claude group, CC Switch writes ANTHROPIC_BASE_URL (https://api.thinkapi.org) and ANTHROPIC_AUTH_TOKEN to ~/.claude/settings.json, the same as the recommended setup. The four options under Claude Code settings in the dialog are all checked by default:
| Option | What it does |
|---|---|
| Enable 1-hour caching | Requests 1-hour prompt caching. Writes to the 1-hour cache are billed at a separate rate, usually higher than regular cache writes. The price table in a model's details does not list it separately and only notes that the model has additional pricing dimensions; the settled amount in Requests is final. |
| Discover available models | Queries the gateway for your group's models at startup and adds them to the /model list without changing the default model, and the Portal does not set a default model for you either. Choose one in Claude Code with /model or claude --model; see Choosing a model. To set a default, edit the provider in CC Switch and add ANTHROPIC_MODEL to the env block of its configuration rather than editing ~/.claude/settings.json directly. |
| Disable Artifacts | Turns off the Artifact tool that publishes content to claude.ai. |
| Disable client telemetry | Turns off client analytics, error reports and OpenTelemetry export. It also stops feature-flag fetching, so features that depend on it, such as starting in auto mode by default and skills sync, stop working, and Claude Code no longer leaves out MCP tools with an invalid input schema, so requests that include such a tool return 400 (Remote Control is already unavailable once the gateway is set). Whether to check it depends on whether you need these features. |
The ~/.claude/settings.json that CC Switch writes applies to the Claude Code CLI and the JetBrains plugin. The VS Code extension still needs the same two variables in claudeCode.environmentVariables, and the Claude desktop app needs its own Third-Party Inference setup; see other surfaces.
CC Switch: importing into Codex
For a key in an OpenAI / Codex group, CC Switch writes a custom provider to ~/.codex/config.toml and saves the key in ~/.codex/auth.json. It also works with groups that accept only native clients, which has been confirmed in the ChatGPT client. If a client returns 403 non_client_disabled, set up option 1 by hand instead.
- Unlike option 2, it does not coexist with a ChatGPT sign-in: enabling the provider makes CC Switch overwrite
~/.codex/auth.jsonwith your ThinkAPI key, and your previous ChatGPT sign-in is not kept. To go back to ChatGPT, switch to the official login preset in CC Switch and sign in again. - The model is always set to
gpt-5.5withhighreasoning effort, regardless of the model you chose in Connection setup, and requests fail if your group does not offer that model. After importing, edit the provider in CC Switch and changemodelin its config.toml (model_reasoning_effortis in the same place), or usecodex -m <model-id>for one run. - The URL CC Switch writes has no trailing
/v1, unlike the manual setup. Keep the imported value as is. - Restart Codex after changing its configuration.
CC Switch: balance and caveats
CC Switch refreshes the balance every 30 minutes through GET /v1/usage (see Check your balance), but only while that provider is the one currently enabled. It shows the lower of your account balance and the key's remaining credit.
- When you enable or switch providers, CC Switch rewrites the whole of
~/.claude/settings.json, or~/.codex/config.tomlandauth.json, from that provider's settings plus its shared config, so other settings you added to the original file by hand, such aspermissionsorhooks, do not carry over. Back up the original files before you enable a provider, and keep settings you want in every provider in CC Switch's shared config snippet. - CC Switch ignores
CLAUDE_CONFIG_DIRandCODEX_HOME. If you set either one, point CC Switch's configuration directory setting at the same path, or the import has no effect. - If Claude Code still does not pick up the import, check whether a project's
.claude/settings.jsonor.claude/settings.local.jsonsets the same variables, and run/statusin Claude Code to see the URL and credential in effect. - With groups that accept only native clients, let the client connect to the gateway directly and do not turn on CC Switch features that proxy requests.
If nothing happens when you choose Import to CC Switch, make sure CC Switch is installed and registered for the ccswitch:// protocol. On macOS, run this command to register it again:
/usr/bin/open -a "CC Switch" --args --register-protocolClient restrictions by group
Open a model's details on Models & pricing; the Client access field shows what the group requires. Native client means only official clients are accepted; Standard API means other requests that follow the protocol are accepted too; Unconfirmed means compatibility is not settled yet, so contact support first. The field is a guide, and actual request results take precedence.
In groups that accept only native clients:
- Only the official Claude Code and Codex work, including setups imported with CC Switch. When configuring Codex by hand, option 1 is recommended.
- Requests must be sent unmodified by the official client: do not change the User-Agent or other headers, and do not route through proxies or third-party tools that rewrite headers.
- Use a recent official release.
- The online test in Connection setup is not available. Make a real request from your client, then find it by time and model in Requests (Claude Code and Codex do not show the
X-Request-Id, and Codex uses WebSocket by default). To check it in step 03 of Connection setup, copy the request ID from the request details and select the same key, group and model in step 01.
| Code | What you see | What to do |
|---|---|---|
non_client_disabled (403) | Claude Code shows "Please upgrade your Claude client." and Codex shows "Unknown client is not supported." It is not only about old versions: usually the request was not recognized as coming from an official client, for example because the User-Agent or headers were changed, or it went through a proxy or third-party tool. | Connect directly with an unmodified, current official release; switch Codex to option 1; or use a Standard API group. |
unsupported_client_platform (400) | The group does not support your operating system. Claude Code shows the same "Please upgrade your Claude client." message, but the version is not the cause. | Use a group that supports your operating system, or open a ticket. |
unsupported_client_feature (400) | While rebuilding the request in the Claude client's format, the gateway found a field it does not support yet or that does not meet the format's requirements. The message names the field path (such as thinking.display). Both third-party requests and official clients can hit this; for official clients it is most common right after a new release. | Retrying unchanged will not help. For a third-party request, change or remove the field the message names. For an official client, note the time, your client version, and the X-Request-Id if you have one, then open a ticket. |
unsupported_client_profile (400) | The target client configuration the platform set for your group or its upstream credentials cannot currently be used to rebuild this request. Your local client version is not the cause. | Retrying unchanged will not help, and neither will upgrading or downgrading your client. Note the time and the X-Request-Id if you have one, then open a ticket. |
external_client_scope_blocked (403) | Requests from this client are blocked for this key; other clients and keys are unaffected. | Use the official native client instead. To have the block lifted, open a ticket. |
unsupported_client (403) | The platform does not support this client ("This client is not supported."). | Switch to a supported client; retrying unchanged will not help. |
See Error codes for the full list.
Endpoint reference
All paths are relative to https://api.thinkapi.org. The protocols shown in a model's details on Models & pricing are a guide to how to call it; if your group has cross-protocol translation turned on, other protocols may work too, as described below.
| Protocol | Method and path | Streaming |
|---|---|---|
| Anthropic Messages | POST /v1/messages | Set "stream": true in the body |
| Anthropic token counting | POST /v1/messages/count_tokens | — |
| OpenAI Chat Completions | POST /v1/chat/completions | Set "stream": true in the body |
| OpenAI Responses | POST /v1/responses | Set "stream": true in the body |
| OpenAI Responses token counting (gateway estimate) | POST /v1/responses/input_tokens | — |
| OpenAI Responses compaction | POST /v1/responses/compact | Not supported; always JSON |
| OpenAI Responses WebSocket | GET /v1/responses with Upgrade: websocket | See Responses WebSocket |
| OpenAI Embeddings | POST /v1/embeddings | — |
| OpenAI Images | POST /v1/images/generations | Not supported; returns 400 unsupported_image_streaming |
| Model list | GET /v1/models | — |
| Codex standalone search | POST /v1/alpha/search | — |
| Balance | GET /v1/usage | — |
When the Codex client calls GET /v1/models it adds client_version, and the reply uses Codex's model manifest format; without it you get the usual OpenAI list format. A model ID can carry a reasoning-effort suffix: <model>-high calls <model> with the reasoning effort set to high (an effort set in the request still wins). This only applies when the suffixed name is not itself an available model.
Whether you can call a model through another protocol's endpoint depends on whether your service group has cross-protocol translation set up for it. If a request needs translation and the group does not allow it, you get 403 translate_disabled. Protocol-specific fields and streaming events are not guaranteed to translate exactly, and parameters that cannot be translated return 400 unsupported_parameter.
Unsupported endpoints
Sub-resource endpoints such as GET /v1/models/{id}, GET /v1/responses/{id}, and /v1/messages/batches are not supported. They, like any other path the gateway does not recognize, return 404 unknown_route.
Responses WebSocket
Send a GET request to /v1/responses with Upgrade: websocket to open a WebSocket connection. The handshake checks your key and account balance, and failures come back as ordinary HTTP errors.
- Send
{"type":"response.create", ...}to start a turn; apart fromtype, the fields match thePOST /v1/responsesrequest body. Send{"type":"response.cancel"}to cancel the current turn. - The server sends each Responses streaming event as a JSON frame. Every turn ends with a
response.completed,response.incomplete,response.failed, orerrorevent. - Each connection handles turns one at a time. While a turn is running, one more can wait in the queue; further requests return 429
response_queue_full. - Every turn is authenticated, rate limited, and billed separately. A failed turn returns an error frame shaped like
{"type":"error","status":N,"error":{...}}, and the connection stays open. - A single frame can be up to 32 MiB; a larger frame closes the connection.
- Cross-origin browser connections are rejected based on their
Originheader. - Individual turns do not expose an
X-Request-Id. For troubleshooting, note the time (with time zone) and the key name.
Response headers
| Header | Description |
|---|---|
X-Request-Id | The ID of this request, 32 hexadecimal characters. Open /app/requests/{request_id} in the Portal to see the request, and include the ID in support tickets. Header names are case-insensitive; if two values appear, use the first. |
X-ThinkAPI-Error-Code | The error code. It appears only on some errors, mainly when the error body is in the upstream provider's format. |
X-ThinkAPI-Error-Action | Sent with the error code to suggest what to do next, such as correct_request (fix the request) or restore_history (restore the full conversation history or start a new session). |
X-Should-Retry: false | The gateway advises against automatic retries. Handle the error based on its code. |
Retry-After | The minimum number of seconds to wait before retrying, calculated by the gateway from current limits. |
X-ThinkAPI-Local-Probe | The gateway answered the request itself. It was not sent to a model and is not billed. |
X-Accel-Buffering: no | Sent on streaming responses to tell intermediate proxies not to buffer. |
| Other headers | Upstream response headers are passed through. Which ones appear is not guaranteed. |
Errors and rate limits
Every error comes with an HTTP status and an error code. Learn the error body formats first, then look up the code below.
Error formats
Error bodies come in two formats. When the gateway rejects a request itself, the format is the same whichever protocol you call:
{
"error": {
"type": "Unauthorized",
"code": "invalid_api_key",
"message": "The API key is not valid."
}
}error.type is the HTTP status phrase. Limit errors such as 429 user_rpm_exceeded also include error.reason and error.details.
When you call a model through its own protocol, request errors (4xx) from the upstream provider keep the provider's original status and body. For example, with Anthropic Messages:
{
"type": "error",
"error": {
"type": "invalid_request_error",
"message": "<upstream error message>"
}
}These responses carry X-Should-Retry: false, X-ThinkAPI-Error-Code (for example invalid_request_error), and X-ThinkAPI-Error-Action (for example correct_request). With cross-protocol translation, upstream errors are converted to the format of the protocol you called, and their type fields are not the provider's original values.
Identifying errors
- Identify errors by the HTTP status plus
error.code. Do not rely onerror.type. - When the body is in the upstream format,
error.codemay be missing or hold the provider's own value, so check theX-ThinkAPI-Error-Codeheader instead. That header appears only on some errors; when it is absent, go byerror.code. - One status can have different causes. A 429, for example, may mean your account hit a rate limit, the platform is protecting capacity, or the key reached its credit limit, and each calls for a different response.
- A misspelled or unavailable model can return 403
model_not_allowed, 400model_not_supportedormodel_disabled, a 503 (such asno_account_available) or a 502, or an upstream 404 in the provider's own format (such as Anthropic'snot_found_error). A 404 means the path does not exist only when the body is in the gateway format anderror.codeisunknown_route.
Error codes
The table lists common error codes and how to handle them. Filter by category to narrow it down.
| Status | Code | Meaning | What to do |
|---|---|---|---|
| 401 | missing_api_keyAuthentication | The request carried no API key, or put it somewhere the gateway does not look. | Send Authorization: Bearer <key> for OpenAI protocols or x-api-key: <key> for Anthropic, and check that your client config or environment variable is actually loaded. |
| 401 | invalid_api_keyAuthentication | The gateway could not match the key ("The API key is not valid."). It may be truncated or deleted, or carry a Bearer prefix in a header other than Authorization. | Copy the full key again from API keys. Only the Authorization header takes the Bearer prefix. |
| 403 | api_key_disabledAuthentication | This key has been disabled. | Re-enable it in API keys, or use another active key. |
| 403 | api_key_expiredAuthentication | This key is past its expiry date. | Re-enabling it does not extend the expiry. Create a new key in API keys and update your client. |
| 403 | group_unavailableAuthentication | The key's service group is unavailable: the key has no group, the group was disabled or removed, or you no longer have access to that dedicated group. | Assign an available group to the key in API keys. For access to a dedicated group, open a ticket. |
| 403 | user_unavailableAuthentication | The account that owns this key is not active. | Contact us to check your account status. |
| 500 | authorization_failedAuthentication | The gateway hit an internal error while checking the key ("Could not authorize the request."). It does not mean the key is wrong. | Retry shortly. If it keeps happening, open a ticket with the time and the key name. |
| 402 | insufficient_balanceBalance & credit | Your account balance is zero or below, so new requests are refused; the message is a short notice in Chinese. All keys share the balance, and in-flight requests can push it below zero. | Top up in Billing; requests work again once the balance is positive. Retrying alone will not help. |
| 429 | api_key_quota_exhaustedBalance & credit | This key has used up its credit limit ("This API key has reached its usage limit."). The limit counts settled charges and is separate from your account balance. | Retrying will not help, and a top-up does not reset key usage. Raise the credit limit in API keys or switch keys, and turn off automatic retries for this code. |
| 429 | user_rpm_exceededRate limits | Your account hit its requests-per-minute (RPM) limit. Every API key on the account shares it, so switching keys will not get around it. | Wait for Retry-After, then retry at a lower rate. Your limits are under API request limits in Settings; open a ticket to ask for a change. |
| 429 | user_concurrency_exceededRate limits | Your account already has as many requests in flight as its maximum concurrency allows. All your keys share this limit too. | Let in-flight requests finish and retry after Retry-After, or lower your client's concurrency. Your limit is shown in Settings. |
| 503 | coordination_unavailableRate limits | The gateway briefly could not check your account's RPM or concurrency limits ("Request limits could not be verified. Retry shortly."). Only accounts with such limits are affected. | Retry shortly. If it persists, open a ticket. |
| 403 | model_not_allowedModels & groups | The group offers a fixed set of models, and the one you asked for is not among them ("The requested model is not enabled for this group."). | Find the model IDs your group offers in Models & pricing or with GET /v1/models, and use one of those. For other models, move the key to another group. |
| 400 | model_not_supportedModels & groups | The key's group does not support the requested model. | Use a model listed for your group in Models & pricing, or move the key to another group. |
| 400 | model_disabledModels & groups | The requested model is disabled ("The requested model is disabled in the provider catalog."). The same code appears when the endpoint does not match the group's protocol, so the model itself may be fine. | Check the model ID, and make sure the request path matches a protocol shown in the model's details (see the endpoint list). |
| 400 | provider_not_supportedModels & groups | The key's group does not serve the endpoint you called. | Call an endpoint for a protocol shown in the model's details, or move the key to another group. |
| 403 | translate_disabledModels & groups | Calling this model through the current protocol needs cross-protocol translation, which the group has not enabled ("Cross-provider translation is not enabled for this group."). | Call the model through a protocol shown in its details, or use a group with translation enabled. |
| 400 | capability_unsupportedModels & groups | Nothing serving this endpoint supports the requested operation ("No provider serving this route supports the requested operation."). This depends on the endpoint, not on the model you chose. | Check the endpoint list and use an endpoint that supports the operation. If it persists, open a ticket. |
| 503 | model_route_unavailableModels & groups | The gateway briefly could not resolve this model's configuration ("Could not resolve the configured model route."). | Retry shortly. If it persists, open a ticket. |
| 403 | plan_excludes_modelModels & groups | The upstream service behind your group does not include the selected model. | Use another model or group. If you need this model, open a ticket. |
| 400 | invalid_reasoning_effortModels & groups | The reasoning effort is not one the gateway recognizes (none, minimal, low, medium, high, xhigh, max, adaptive, enabled); the message reads unsupported reasoning effort "...". | Use one of the values above, or leave the reasoning effort out. When a model has a value disabled, you get reasoning_effort_disabled instead. |
| 400 | reasoning_effort_disabledModels & groups | That reasoning effort is disabled in your group ("The requested reasoning effort is disabled for every eligible model target."). | Pick another reasoning effort, or leave it out. |
| 403 | reasoning_effort_deniedModels & groups | The group's request policy does not allow this reasoning effort: a mapping rule denies it, or it is above the group's limit and the limit is set to deny. The message reads like reasoning effort "xhigh" exceeds this group's limit of "high". | Lower the reasoning effort, or leave it out. |
| 403 | service_tier_not_allowedModels & groups | The group's request policy does not allow the requested service_tier (for example priority). The message is the platform's explanation, or reads like service_tier=priority is not allowed for model gpt-5.5 when none is set. | Drop service_tier, or use a value the message allows. |
| 409 / 400 | response_chain_unavailableModels & groups | The response that previous_response_id points to cannot be continued. A 409 means the upstream that produced it is gone; a 400 means it can no longer be replayed and the full input history is needed. | Drop previous_response_id and send the full conversation history, or start a new conversation. |
| 403 | non_client_disabledClients | The group only accepts native clients, and this request did not come from a supported one. The message is "Unknown client is not supported." or "Please upgrade your Claude client." | Despite the wording, this does not necessarily mean your client is out of date. Usually the request was not sent unmodified by an official client, for example because the User-Agent or headers were changed, or a proxy or third-party tool rewrote them. Connect directly with an unmodified, current release of Claude Code or Codex, or move to a group that allows standard API clients. See the Client access field in the model's details and client restrictions. |
| 403 | unsupported_clientClients | The platform does not support this client ("This client is not supported."). The request was not forwarded and is not billed. | Switch to a supported client; retrying unchanged will not help. If you think this is a mistake, open a ticket. |
| 403 | external_client_scope_blockedClients | Requests from this client are blocked for this key; other clients and keys are unaffected. This usually follows an upstream third_party_extra_usage_required response, and whether and when the block lifts depends on the group's settings. | Use the official native client instead. To have the block lifted, open a ticket. |
| 400 | unsupported_client_platformClients | The upstream service behind your group does not support the operating system your client runs on (some groups support only macOS or only Windows; Linux counts as macOS). The message is the same as for non_client_disabled, so Claude Code also shows "Please upgrade your Claude client.", but the cause has nothing to do with the version. | Use a group that supports your operating system, or open a ticket. |
| 400 | unsupported_client_featureClients | While rebuilding the request in the Claude client's format, the gateway found a field it does not support yet or that does not meet the format's requirements. Both third-party requests and official clients can hit this. The message names the field path, such as thinking.display, tools[0].eager_input_streaming, or tools[0].type. For official clients it is most common right after a new release. | Retrying unchanged will not help. For a third-party request, change or remove the field the message names. For an official client, note the time, your client version and the X-Request-Id if you have one, then open a ticket. |
| 400 | unsupported_client_profileClients | The target client configuration the platform set for your group or its upstream credentials cannot currently be used to rebuild this request. It has nothing to do with your local client version, and both official clients and third-party requests can hit it. | Retrying unchanged will not help, and neither will upgrading or downgrading your client. Note the time and the X-Request-Id if you have one, then open a ticket. |
| 503 | client_policy_unavailableClients | The gateway briefly could not confirm whether your client is allowed. | Retry later. If it keeps failing, open a ticket. |
| 400 | third_party_extra_usage_requiredHeaderClients | The upstream refused this third-party client request and asked for separately metered extra usage. The body is the upstream's own error, so identify it by the X-ThinkAPI-Error-Code header; X-ThinkAPI-Error-Action is check_extra_usage. | Retrying unchanged will not help; switch to the official native client. Depending on the group's settings, this client may then be blocked for this key (external_client_scope_blocked). |
| 429 | all_accounts_busyPlatform capacity | The group's platform capacity is momentarily full. This is platform-side capacity protection, not your own limit. | Retry after Retry-After. If it persists, open a ticket. |
| 429 | upstream_rate_limitedPlatform capacity | The upstream rate limits on the platform side are used up; this has nothing to do with your RPM. | Retry after Retry-After. If it persists, open a ticket. |
| 429 | upstream_usage_limit_reachedPlatform capacity | The platform's usage protection for this group has kicked in. The limit and used values in the items of details.reasons[], when present, are platform-side figures, not your credit. | Retry after Retry-After. If it persists, open a ticket. |
| 429 | upstream_quota_exhaustedPlatform capacity | The upstream quota on the platform side is temporarily exhausted. | Retry later, waiting for Retry-After when it is present. If it persists, open a ticket. |
| 503 | no_account_availablePlatform capacity | The group has no service capacity available right now. It is also the final code when upstream errors use up automatic failover. It can also mean no upstream in the group can serve the requested model, for example because the model ID is mistyped. | First check that model is a model ID your group offers (Models & pricing or GET /v1/models). If it is, retry later (no Retry-After is sent) and check Service status. If it persists, open a ticket. |
| 503 | quota_observation_unavailablePlatform capacity | The platform needs fresh capacity data before it can accept the request. | Retry after Retry-After. |
| 503 | model_access_unavailablePlatform capacity | The platform cannot currently confirm access to this model. | Retry later or use another model. If it persists, open a ticket. |
| 503 | coordinator_unavailablePlatform capacity | The gateway briefly cannot enforce platform-side concurrency controls ("Concurrency limits cannot be enforced right now. Retry shortly."). | Retry after Retry-After. |
| 500 | selection_failedPlatform capacity | The gateway hit an internal error while allocating capacity for the request. The message is "Please contact the administrator." | Retry later. If it persists, open a ticket with the X-Request-Id. |
| 502 | upstream_unavailableUpstream & timeouts | The upstream kept failing after the gateway's automatic retries and failover. The message is a short normalized summary. | Retry later and check Service status. If it persists, open a ticket with the X-Request-Id. |
| 502 | upstream_attempt_budget_exhaustedUpstream & timeouts | The request used up the gateway's upstream retry budget without succeeding ("The request exhausted its upstream retry budget."). | Retry later. If it persists, open a ticket with the X-Request-Id. |
| 502 | upstream_delivery_uncertainUpstream & timeouts | The connection dropped before an upstream response arrived, so it is unclear whether the upstream ran the request ("The upstream connection ended before a response could be confirmed. Retry the request explicitly."). The gateway does not replay it, and this request is not billed. | Decide for yourself whether to retry. A retry may run the request twice, so check first if it has side effects such as tool actions. |
| 503 | upstream_overloadedUpstream & timeouts | Every available upstream is temporarily overloaded ("the upstream is temporarily overloaded; retry shortly"), and the gateway already tried switching. The response carries Retry-After. | Retry after Retry-After. If it persists, open a ticket. |
| 504 | upstream_idle_timeoutUpstream & timeouts | The upstream sent nothing for too long, so the gateway ended the request before sending response headers ("The upstream response was idle for too long. Retry the request."). If a stream had already started, it is cut off instead. | You can retry. If it persists, open a ticket. |
| 504 | upstream_first_output_timeoutUpstream & timeouts | With first-output protection enabled on the platform (streaming requests only), the upstream produced no usable output in time. The request is not replayed. | Retry yourself if needed; a retry may run the request twice. |
| 502 | upstream_first_output_invalidUpstream & timeouts | With first-output protection enabled on the platform, the upstream's first output was unusable. The request is not replayed. | Retry yourself if needed; a retry may run the request twice. |
| 502 | upstream_first_output_limitUpstream & timeouts | With first-output protection enabled on the platform, the upstream's first output exceeded what the gateway can inspect. The request is not replayed. | Retry yourself if needed; a retry may run the request twice. |
| 502 | upstream_response_invalidUpstream & timeouts | The gateway could not decode the upstream response ("Could not decode the upstream response."). The upstream may already have run the request, so the response usually carries X-Should-Retry: false. | Do not retry automatically. Retry by hand only once you are sure it should run again; if it persists, open a ticket. |
| 200 | bps_protocol_errorUpstream & timeouts | It only appears in a response.failed event of a streaming Responses request, with HTTP status 200. On some upstream routes the gateway translates tool calls; when the model's call cannot be mapped back to a tool you declared (for example an undeclared tool, or broken arguments), the turn ends with this code instead of sending a partial tool call. | You can retry the turn. If it keeps happening, declare fewer tools at once, or open a ticket with the X-Request-Id. |
| 502 | models_unavailableUpstream & timeouts | Only returned by the model list endpoints (GET /v1/models, and GET /models without /v1): the gateway could not fetch the live upstream model list, and it never returns a partial list. | Retry later, and do not poll it as a health check. You can also see available models in Models & pricing. |
| 503 | gateway_drainingMaintenance | The gateway is restarting ("The gateway is restarting."). This response has no X-Request-Id. | Retry in a moment. |
| 503 | billing_integrity_unavailableMaintenance | Billing records are temporarily unavailable, so the gateway pauses new requests rather than serve them unbilled. | Retry later, waiting for Retry-After when it is present. |
| 503 | billing_recovery_backlogMaintenance | Some of your account's usage is still being recorded ("Usage recovery is pending. Retry shortly."), and new requests are refused until that finishes. | Retry after Retry-After. |
| 503 | usage_tracking_unavailableMaintenance | The gateway could not open a usage record for this request, so it was not forwarded ("The request could not be tracked safely. Retry shortly."). | Retry shortly. |
| 404 | unknown_routeRequest content | Nothing serves this method and path ("No provider serves this method and path."). Common causes: a typo, an unsupported sub-resource such as /v1/models/{id}, a browser OPTIONS preflight, or /embeddings without /v1. These responses include an X-Request-Id but are not recorded in Requests. | Check the method and path against the endpoint list. OpenAI-compatible clients should use https://api.thinkapi.org/v1 as the base URL. |
| 413 | body_too_largeRequest content | The request body is over the 32 MiB limit. With input security auditing enabled, the audit's own input limit can also return this code first. | Shrink the request body, for example by compressing images or trimming history. |
| 400 | unreadable_bodyRequest content | The gateway could not read the request body ("Could not read the request body."). | Check your client and network, then retry. |
| 400 | invalid_request_metadataRequest content | A request field is longer than allowed; model, for example, is capped at 128 bytes. The message names the field. | Shorten that field and retry. |
| 400 | request_rejectedRequest content | The request failed the gateway's checks. The message gives the reason, such as a missing required field or a field clients are not allowed to set. | Fix the request as the message describes; retrying unchanged will not help. |
| 400 | unsupported_parameterRequest content | A parameter in the request cannot be expressed in the target protocol, typically during cross-protocol translation. | Remove the parameter, or call the model through a protocol shown in its details. |
| 400 | unsupported_image_streamingRequest content | Image generation does not support streaming. | Remove "stream": true from the request body. |
| 400 | invalid_thinking_signatureRequest content | The conversation contains a thinking signature the upstream has already rejected, so the gateway refuses it; retrying unchanged will not fix it. | Restore the complete original thinking blocks with their signatures, or start a new session. |
| 406 | response_encoding_not_acceptableRequest content | The request's Accept-Encoding accepts neither the upstream's compression nor uncompressed content (identity). | Relax Accept-Encoding so it allows at least identity, or drop the header. |
| 400 / 403 | content_policy_violationContent safety | Platform screening or the upstream content policy rejected the request. Upstream refusals stop the request without switching accounts or denying account model access. Same-protocol errors retain the upstream body; see X-ThinkAPI-Error-Code for classification. An HTTP 200 stream may contain an error event, with classification in the final request record. | Revise the content before sending again. If you think it is a false positive, open a ticket with the request ID. |
| 429 | account_security_cooldownContent safety | Your account is in a security cooldown and all its API keys are paused ("This account is temporarily unavailable. Try again later."). Retry-After gives the time left. | Retrying before the cooldown ends will not help. Wait it out, and turn off automatic retries for this code. |
| 403 | account_security_bannedContent safety | The account is banned under a security restriction, and none of its API keys work ("This account is unavailable. Contact an administrator."). | Open a ticket or contact us. |
| 503 | input_audit_unavailableContent safety | Input security auditing is temporarily unavailable, so the request was not processed ("The request could not be processed. Try again later."). It can appear even when content screening is off, and it does not mean your content broke a rule. | Retry later. If it persists, open a ticket. |
| 400 / 403 | cyber_policyContent safety | The upstream refused this conversation's content under its cyber-security policy. The gateway preserves the reason without switching accounts or denying account model access. Same-protocol errors may still use upstream permission_error; see X-ThinkAPI-Error-Code. HTTP 200 streams may contain an error or response.failed refusal, with classification in the final request record. | Revise the content and start a new session; do not retry unchanged. Your group may refuse the same session for a while, see session_blocked_by_cyber_policy. |
| 403 | session_blocked_by_cyber_policyContent safety | The upstream's cyber-security policy refused this session earlier, and your group blocks such sessions for a while, so requests in it are refused without reaching the upstream ("This session is blocked by the upstream cyber-security policy. Start a new session."). | Start a new session. Retrying the same session during the block will not help. |
Retries and rate limits
- If a response includes
Retry-After, wait at least that many seconds before retrying. - If a response includes
X-Should-Retry: false, the gateway advises against automatic retries. Handle it based on the error code. - Official SDKs and clients retry 429 and 5xx responses by default. Retrying does not help with 429
api_key_quota_exhaustedor 429account_security_cooldown: the first needs a higher credit limit or another key, and the second clears only when the cooldown ends. - Requests per minute (RPM) and maximum concurrency apply to your whole account and are shared by all its API keys, so switching keys does not help. Find your limits under API request limits in Settings.
- Capacity errors such as 429
all_accounts_busyand 429upstream_rate_limitedmean the platform is protecting its capacity. Retry afterRetry-After, and open a ticket if they persist. Thelimitandusedvalues in the items of theirdetails.reasons[], when present, describe platform capacity, not your account limits. - If your service group queues requests, a request waits for capacity first, so you see higher latency instead of an immediate 429.
Failover and timeouts
- When an upstream provider fails, the gateway may retry or switch to another available upstream within the same request. This is transparent to you and
X-Request-Idstays the same, though latency may increase. - Once a streaming response has started, the gateway no longer switches. If the stream breaks, you are billed for the usage produced so far.
- 502
upstream_delivery_uncertainand first-output timeout errors (when the platform enables them, such as 504upstream_first_output_timeout) are not replayed automatically. The request may already have run upstream, so retrying it yourself can run it twice. - A request with no data flowing in either direction for a long time is cut off: before the response starts you get 504
upstream_idle_timeout, and after it starts the stream ends early. Give your client a generous read timeout.
Billing and credit
Balance and credit limits
- Your account balance is a wallet shared by all your API keys. Add funds and review transactions on Billing; available payment methods depend on platform settings.
- A credit limit caps spending for a single key. Set it when you create or edit a key on API keys; 0 means no limit. It counts settled charges, and once reached, requests return 429
api_key_quota_exhausted. A top-up does not reset key usage, so raise the limit or use another key. - Billing is postpaid: new requests need a balance above zero, and requests already in progress can push it below zero. At zero or below, requests return 402
insufficient_balanceuntil you top up. - Credit limits are also checked only when a request starts, so requests in progress can take a key slightly past its limit.
- Use
GET /v1/usageto check what you can spend at any time; see Check your balance.
Cost breakdown
Cost per request ≈ model unit price × usage in each billing dimension × group multiplier. Dimensions include input, output, cache reads, and cache writes. Some models add dimensions such as cache write duration, reasoning, or long context. Model details on Models & pricing only note that a model has additional pricing dimensions and do not list those rates; the settled amount in Requests is final.
- Charges are in your account currency (USD by default).
- Models & pricing shows a snapshot of current prices with your group multiplier applied. It is not a price lock, and a price shown as “—” does not mean free.
- The settled amount in Requests is the final charge.
What is not billed
- Requests the gateway rejects before forwarding them, such as authentication failures, insufficient balance, rate limits, and malformed requests.
- Upstream attempts that fail before the gateway switches to another upstream.
- 502
upstream_delivery_uncertain. GET /v1/models,GET /v1/usage, and Anthropic'sPOST /v1/messages/count_tokens(which still needs a balance above zero).POST /v1/responses/input_tokens: the gateway estimates it locally from the request text, and the response carriesX-ThinkAPI-Token-Count: estimated. The number is approximate (a balance above zero is still needed).- Connectivity probes answered locally: a bare greeting may get a reply from the gateway itself, with the
X-ThinkAPI-Local-Probeheader, and appear in Requests as “Probe request · Not forwarded”. It only proves you can reach the gateway, not that the model is available. It still needs a balance above zero and counts toward your RPM.
Service status and troubleshooting
Service status
Service status shows the health of public service groups over the last 24 hours. It refreshes about every 30 seconds, and intervals without requests count as healthy. For dedicated groups, sign in and open Service status in your workspace.
Tracing a request
- Model responses normally include an
X-Request-Idheader (except 503gateway_drainingand individual Responses WebSocket turns). Save it in your logs. - Open
/app/requests/{request_id}in the Portal (replace{request_id}with the request ID) to see the request details, which show the execution status, HTTP status, billing status, and error details. Opening/app/usage?request_id={request_id}shows only that request in the Requests list. - If you have questions about the outcome or the charge, choose Get help with this request on the details page. The ticket is linked to that request automatically.
- Some HTTP clients merge repeated headers into one comma-separated value. If you see two request IDs, use the first 32-character hexadecimal value.
Missing request records
These requests do not appear in Requests:
- Errors in the Authentication category of Error codes (401, 403, and 500
authorization_failed), plus 429api_key_quota_exhausted. They include anX-Request-Idbut return before a record is written. - 404
unknown_route: the method or path is not recognized (for example because of a wrong base URL). It includes anX-Request-Idbut returns before authentication and is never recorded. - Model list requests (
GET /v1/models,GET /models): they include anX-Request-Idbut are never recorded. - 503
gateway_draining: the gateway is restarting, and this response has noX-Request-Id.
In these cases, include the time (with time zone), the key name, and the error code in your ticket.
Clients such as Claude Code and Codex usually do not show you the X-Request-Id, and individual Responses WebSocket turns do not receive that header at all; the req_… in Claude Code errors is an upstream ID. These requests are still recorded, so look them up in Requests by time, model, and key.
Contacting support
When you open a ticket in Support, include as much of the following as you can:
X-Request-Id- When it happened, with time zone
- HTTP status
error.codeanderror.reason, if present- Key name (never paste the key itself)
- Model ID
- Client name and version
- Whether the request was streaming
If you cannot sign in, reach support through the channels on Contact us.
Glossary
These terms match the wording used in the Portal.
- Service group
- Every API key belongs to one service group, which sets the models it can call, the price multiplier and any client restrictions. Groups are either Shared or Dedicated; Dedicated groups are open only to authorized accounts.
- Group multiplier
- The factor a group applies to a model's base price; ×1 means the base price. Models & pricing shows it for each group, and the prices listed there already include it.
- Credit limit
- A spending cap on a single API key, counted against settled charges; 0 means no limit. It is checked when a request starts, so in-flight requests can take usage slightly past it. A top-up does not reset key usage.
- Account balance
- Your account's wallet, shared by all API keys and held in the account currency (USD by default). New requests need a balance above zero; charges settle after each request, so in-flight requests can take it below zero.
- Requests
- The Portal's log of your API calls, with outcome, usage and cost for each; see Requests. If you have a request ID, open
/app/requests/{request_id}to see that call directly. - Billing status
- Whether a call's charge is confirmed, shown in its request details as Settled, Awaiting pricing, Not billed and so on; the list's Billing column shows the same. Only a settled zero is a confirmed zero charge.
- X-Request-Id
- The request ID the gateway returns as a response header (32 hex characters). Use it to open
/app/requests/{request_id}for the call's details, and include it in support tickets. Requests that fail authentication, requests to unknown paths (404unknown_route) and model list requests are not recorded; see Missing request records. If two values appear, use the first. - Native client
- A model vendor's official client, such as Claude Code or Codex. Some groups only accept native clients; the Client access field in a model's details is a guide, and other clients get
non_client_disabled. - Protocol
- The request and response format, such as Anthropic's Messages or OpenAI's Chat Completions and Responses. The protocols a model supports are listed in its details; see Endpoint reference for the paths of each protocol.
- Streaming
- Returning output as it is generated. Turn it on with
"stream": truein the request body for Messages, Chat Completions and Responses. - RPM
- Requests per minute. The limit is shared by all API keys on your account and shown under API request limits in Settings. Going over it returns
user_rpm_exceeded. - Maximum concurrency
- How many requests your account can have in flight at once, shared by all API keys and shown in Settings. Going over it returns
user_concurrency_exceeded. - Cross-protocol translation
- When you call a model served over a different protocol, the gateway converts the request and response between formats. It only happens if the group enables it; otherwise you get
translate_disabled.