API Keys
Every API request to 2kw.ai needs an API key. Here is how to create, use, and manage them.
Creating a Key
Open Keys under Build in the sidebar and click Create. Give the key a name, pick the role it acts with and when it expires (30, 60 or 90 days, or never), then create it. You'll see the full key exactly once —copy it and store it somewhere safe.
Save your key immediately
After creation, only the first characters of the key are visible. If you lose the full key, you'll need to create a new one.
The role and the expiry take these values:
| Setting | Options | Default |
|---|---|---|
| Role | Viewer, Member, Admin, Owner | Member |
| Expiration | 30 days, 60 days, 90 days, Never | Never |
The role decides which endpoints the key may call, the same way it does for a person in your organization. A Viewer key can read, for example list models or convert documents, but gets 403 Forbidden from endpoints that need Member, such as chat completions, embeddings, and transcription. Most integrations need Member.
A key's role cannot be changed after creation. To give an integration a different role, create a new key with that role and rotate to it. A key past its expiry is rejected with 401 and the code key_expired.
Key Format
An API key is sk_ followed by 64 letters:
sk_ZpQxRtLmNbVcKsHdWfGjYaUeTiOoPlMnBvCxZaSdFgHjKlQwErTyUiOpAsDfGhJk
Using Your Key
Include the key as a Bearer token in the Authorization header:
Usage
curl -X POST https://api.2kw.ai/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer sk_your_api_key" \
-d '{
"model": "gpt-5.1",
"messages": [{"role": "user", "content": "Hello!"}]
}'
Environment Variables
Never commit API keys to source control
Use environment variables or a secrets manager. Never hardcode keys in your code.
# Set the environment variable
export BACKBONE_API_KEY="sk_your_api_key"
Then use it in your code:
Environment variable usage
import os
from openai import OpenAI
client = OpenAI(
api_key=os.environ["BACKBONE_API_KEY"],
base_url="https://api.2kw.ai/v1"
)
Managing Keys
On the Keys page you can:
- Create new keys for different environments or services
- View each key's name, preview, role, status, last use and creation date
- Edit a key to rename it, or disable it and enable it again later
- Delete keys that are no longer needed
A disabled key is rejected with 401 and the code key_disabled until you enable it again, so disabling is a safe way to pause an integration or to check that nothing still uses a key. Deleting a key revokes it permanently: every application still using it loses access at once, and it cannot be restored.
One key per service
Create separate keys for different environments (dev, staging, prod) and services. Makes it easy to rotate or revoke without affecting everything.
Security Best Practices
- Rotate keys regularly —especially if team members leave
- One key per service —makes it easy to revoke without affecting other integrations
- Monitor usage —check the dashboard for unexpected activity
- Disable or delete immediately if a key is compromised
Rotating a key
Replace a key without downtime by running old and new side by side for a short while:
- Create a new key with the same role as the old one.
- Deploy the new key to every service that used the old one.
- Disable the old key. Any caller you missed now gets
401 key_disabled; enable the key again, fix that caller, and repeat. - Delete the old key once nothing has failed for a while.
Rate Limits
Each API key accepts up to 50,000 requests. Every API call made with the key counts, whatever the endpoint. The count starts over once the key has gone 24 hours without an accepted request.
Over the limit, the API answers 429 Too Many Requests with the code rate_limited:
{
"error": {
"message": "Rate limit exceeded",
"type": "rate_limit_error",
"code": "rate_limited"
}
}
The key stays rejected until 24 hours after its last accepted request. The response carries no Retry-After header, so don't retry it in a tight loop: back off, and alert on repeated rate_limited errors rather than retrying them forever.
The limit counts per key
The limit belongs to each key, not to the organization. With one key per service, a busy integration that reaches its limit doesn't lock out your other services.
Error Responses
A rejected key gets an error in the OpenAI format, so SDKs surface error.message and error.code as usual. A request with no Authorization header at all gets a bare 401 with no body.
| Status | code | Meaning |
|---|---|---|
401 Unauthorized | invalid_api_key | The key is malformed or does not exist (for example, it was deleted) |
401 Unauthorized | key_disabled | The key was disabled on the Keys page |
401 Unauthorized | key_expired | The key is past its expiration date |
401 Unauthorized | no_organization | The key is not linked to an organization; create a new key |
403 Forbidden | forbidden | Valid key, but its role does not allow this action |
429 Too Many Requests | rate_limited | The key is over its rate limit |
429 Too Many Requests | usage_exceeded | The key has reached its usage limit |
The 401 errors have the type authentication_error and the two 429 errors the type rate_limit_error. The forbidden code comes from the OpenAI-compatible endpoints (such as /v1/chat/completions and /v1/audio/transcriptions); the other endpoints also answer 403, in their own error format. An invalid key looks like this:
{
"error": {
"message": "Invalid API key",
"type": "authentication_error",
"code": "invalid_api_key"
}
}