The API
Aderra speaks the OpenAI and Anthropic API formats. Most SDKs and tools work once you change the base URL and the key.
Two kinds of key
Section titled “Two kinds of key”Every Aderra key starts with ak_. There are two kinds, and they pay from different places.
| API key | Terminal key | |
|---|---|---|
| Made on | Developers → Create key | Connect → Generate key |
| Pays from | Your API wallet (prepaid, separate from your plan) | Your plan’s credits |
| Plans | Every account, including Free | From Plus |
| Limits you can set | Monthly spending cap and requests per minute | None |
| Meant for | Your own code, apps and scripts | Aderra Agent, Claude Code and Codex on your computer |
Both kinds call the same endpoints below. A terminal key follows your plan’s rules, including its context window. An API key only has the limits of the key and the wallet.
Get started
Section titled “Get started”-
Open Developers in the left bar. Click Add funds, enter an amount and click Pay with crypto. See the Developers page.
-
Click Create key, give it a name, and click Create key again. Copy the key: it is shown once.
-
Save it in an environment variable, so it never ends up in your code:
Terminal window export ADERRA_API_KEY=ak_...
You can create a key before adding funds, but it can’t make calls until the wallet holds credits.
Base URL and endpoints
Section titled “Base URL and endpoints”| Format | Base URL for your SDK |
|---|---|
| OpenAI | https://api.aderra.ai/v1 |
| Anthropic | https://api.aderra.ai (without /v1, the SDK adds it) |
| Method and path | Format | Send the key as |
|---|---|---|
POST /v1/chat/completions |
OpenAI Chat Completions | Authorization: Bearer <key> |
POST /v1/messages |
Anthropic Messages | x-api-key: <key> or Authorization: Bearer <key> |
POST /v1/responses |
OpenAI Responses (what Codex uses) | Authorization: Bearer <key> |
GET /v1/models |
OpenAI model list | Authorization: Bearer <key> or x-api-key: <key> |
Setups that still use https://aderra.ai as the base keep working. The aderra command moves itself to the new address.
Models
Section titled “Models”| Model id | Name in the app |
|---|---|
glm-5.3-flash |
GLM-5.3 Flash, the standard model |
glm-5.3-flash-uncensored |
GLM-5.3 Flash Unfiltered, the same model uncensored |
Use these two ids. The model list endpoint may show other ids: they are not supported for the API and have no published price. The Prices tab of the Developers page lists the models you can buy.
OpenAI format
Section titled “OpenAI format”curl https://api.aderra.ai/v1/chat/completions \ -H "Authorization: Bearer $ADERRA_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "glm-5.3-flash", "max_tokens": 1024, "messages": [{"role": "user", "content": "Write a haiku about the sea."}] }'To stream, add "stream": true and -N. The reply comes as server-sent events, one data: line per chunk, ending with data: [DONE]. The last chunk before it carries the token usage.
curl -N https://api.aderra.ai/v1/chat/completions \ -H "Authorization: Bearer $ADERRA_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "glm-5.3-flash", "max_tokens": 1024, "stream": true, "messages": [{"role": "user", "content": "Write a haiku about the sea."}] }'pip install openaiimport osfrom openai import OpenAI
client = OpenAI(base_url="https://api.aderra.ai/v1", api_key=os.environ["ADERRA_API_KEY"])
reply = client.chat.completions.create( model="glm-5.3-flash", max_tokens=1024, messages=[{"role": "user", "content": "Write a haiku about the sea."}],)print(reply.choices[0].message.content)
# Streamingstream = client.chat.completions.create( model="glm-5.3-flash", max_tokens=1024, stream=True, messages=[{"role": "user", "content": "Write a haiku about the sea."}],)for chunk in stream: if chunk.choices and chunk.choices[0].delta.content: print(chunk.choices[0].delta.content, end="", flush=True)print()npm install openaiimport OpenAI from 'openai';
const client = new OpenAI({ baseURL: 'https://api.aderra.ai/v1', apiKey: process.env.ADERRA_API_KEY,});
const reply = await client.chat.completions.create({ model: 'glm-5.3-flash', max_tokens: 1024, messages: [{ role: 'user', content: 'Write a haiku about the sea.' }],});console.log(reply.choices[0].message.content);
// Streamingconst stream = await client.chat.completions.create({ model: 'glm-5.3-flash', max_tokens: 1024, stream: true, messages: [{ role: 'user', content: 'Write a haiku about the sea.' }],});for await (const chunk of stream) { process.stdout.write(chunk.choices[0]?.delta?.content ?? '');}process.stdout.write('\n');Anthropic format
Section titled “Anthropic format”curl https://api.aderra.ai/v1/messages \ -H "x-api-key: $ADERRA_API_KEY" \ -H "anthropic-version: 2023-06-01" \ -H "Content-Type: application/json" \ -d '{ "model": "glm-5.3-flash", "max_tokens": 1024, "messages": [{"role": "user", "content": "Write a haiku about the sea."}] }'To stream, add "stream": true and -N. You get Anthropic’s events: message_start, then content_block_delta events whose text_delta holds the text, then message_delta with the usage, and message_stop.
curl -N https://api.aderra.ai/v1/messages \ -H "x-api-key: $ADERRA_API_KEY" \ -H "anthropic-version: 2023-06-01" \ -H "Content-Type: application/json" \ -d '{ "model": "glm-5.3-flash", "max_tokens": 1024, "stream": true, "messages": [{"role": "user", "content": "Write a haiku about the sea."}] }'pip install anthropicimport osimport anthropic
# No /v1 here: the SDK adds it.client = anthropic.Anthropic(base_url="https://api.aderra.ai", api_key=os.environ["ADERRA_API_KEY"])
message = client.messages.create( model="glm-5.3-flash", max_tokens=1024, messages=[{"role": "user", "content": "Write a haiku about the sea."}],)print(message.content[0].text)
# Streamingwith client.messages.stream( model="glm-5.3-flash", max_tokens=1024, messages=[{"role": "user", "content": "Write a haiku about the sea."}],) as stream: for text in stream.text_stream: print(text, end="", flush=True)print()npm install @anthropic-ai/sdkimport Anthropic from '@anthropic-ai/sdk';
// No /v1 here: the SDK adds it.const client = new Anthropic({ baseURL: 'https://api.aderra.ai', apiKey: process.env.ADERRA_API_KEY,});
const message = await client.messages.create({ model: 'glm-5.3-flash', max_tokens: 1024, messages: [{ role: 'user', content: 'Write a haiku about the sea.' }],});console.log(message.content[0].text);
// Streamingconst stream = client.messages.stream({ model: 'glm-5.3-flash', max_tokens: 1024, messages: [{ role: 'user', content: 'Write a haiku about the sea.' }],});stream.on('text', (text) => process.stdout.write(text));await stream.finalMessage();process.stdout.write('\n');How requests behave
Section titled “How requests behave”Your system prompt is replaced. Every API request runs under Aderra’s own instructions, written for coding agents, and the model introduces itself as Aderra. What happens to yours depends on the format:
- Chat Completions: every
systemanddevelopermessage is removed. - Messages (Anthropic): the
systemfield is removed. - Responses:
instructionsis replaced.systemordeveloperitems insideinputare kept.
To steer the model, put your instructions in the first user message. Aderra’s instructions add roughly 780 input tokens to each request.
In Chat Completions the model thinks before it answers, and thinking counts as output. With a small max_tokens, it can spend the whole budget thinking and return an empty answer with finish_reason: "length". Leave room (1,024 tokens or more), or send "reasoning_effort": "minimal" for short, fast answers. Messages and Responses answer without extended thinking unless you ask for it (thinking, or reasoning / reasoning_effort).
max_tokens is capped at the model’s output limit, 32,768 tokens for both models today. If you leave it out, the limit is used, which also sets how much is reserved (see Prices).
One answer per request. n must be 1.
Nothing is kept between requests. Send the whole conversation each time. The Responses format does not store responses, so previous_response_id and store are ignored.
Fields Aderra doesn’t use are dropped rather than refused. The OpenAI format keeps the usual ones: messages, max_tokens, temperature, top_p, stop, tools, tool_choice, response_format, seed, stream, the penalties and logprobs, among others.
Prices
Section titled “Prices”Prices are per million tokens and differ for input, cached input and output. The Prices tab of the Developers page always shows the current figures. API credits are bought at 1,000 credits = $1.
Most models cost the same through the API as on a plan. The unfiltered model costs more through the API than on a subscription. The Prices tab shows both prices next to it.
Before a request runs, Aderra reserves the most it could cost: your input plus the full max_tokens. When it finishes you pay only what it used, and the rest goes back to your wallet. A failed request costs nothing. So a nearly empty wallet can refuse a request with a large max_tokens. Send a smaller one, or add funds.
Limits
Section titled “Limits”| Limit | Value |
|---|---|
| Requests per minute, per key | 60 unless you set another; up to 600 (API keys only). Counted per clock minute, so a burst across a minute boundary can briefly reach twice the limit |
| Requests in progress at once | 8 per account, across all your keys |
| Monthly spending cap | Optional, per API key; resets on the 1st of each month (UTC) |
| Active keys | 20 per account |
| Request size | A few MB. Very large requests, usually many images, are refused |
Errors
Section titled “Errors”Every refusal says what happened and what to do. The shape depends on the format:
- Chat Completions: usually
{"error": {"type", "code", "message"}}, with the reason inerror.code. A few refusals senderroras a plain string instead, such as{"error": "unauthorized"}. Check whethererroris a string or an object before reading it. - Messages (Anthropic):
{"type": "error", "error": {"type", "message"}}, with Anthropic’s types (authentication_error,billing_error,rate_limit_error,invalid_request_error,api_error). The reason is inerror.message. - Responses:
{"error": {"type", "message"}}with the same types. Refusals about your key, wallet and limits also carry the Chat Completions reason inerror.code.
| Status | Chat Completions | Messages and Responses | What to do |
|---|---|---|---|
| 401 | {"error": "unauthorized"} (also GET /v1/models) |
authentication_error |
Check the key. It may have been revoked |
| 402 | api_wallet_empty |
billing_error |
Add funds on the Developers page, or lower max_tokens |
| 402 | key_spend_cap_reached |
billing_error |
Raise or remove the key’s cap, or use another key |
| 429 | rate_limited |
rate_limit_error |
Wait for the Retry-After seconds, or raise the key’s limit |
| 429 | too_many_concurrent_requests |
rate_limit_error |
Wait for one of your requests to finish |
| 400 | {"error": "unknown_model"} |
invalid_request_error, “unknown model …” |
Use glm-5.3-flash or glm-5.3-flash-uncensored |
| 400 | unsupported_n |
invalid_request_error |
Send n: 1, or leave it out |
| 413 | request_too_large |
request_too_large |
Send less at once. The message suggests /compact, which only applies to Claude Code and Codex |
| 429, 502 and others | error.type is upstream_error, or {"error": "upstream_unreachable"} with 502 |
api_error |
The model is busy or failed. You were not charged. Try again in a few seconds |
A terminal key gets your plan’s messages instead: out of credits, a conversation too long for your plan’s context window, or, on Free, a message that the terminal needs the Plus plan. See Error messages.
Other tools
Section titled “Other tools”Anything that takes an OpenAI- or Anthropic-compatible base URL and key works: Cursor, Open WebUI and others. The API access box at the bottom of the Connect page shows the exact settings for the Anthropic and OpenAI SDKs, Cursor, and Open WebUI & others.