API keys
Issue a separate Half Off Dev key for every app, cap its spend, rate and models, and rotate or revoke it without breaking production.
A Half Off Dev key is the only thing your code needs to reach Claude, GPT and Gemini at half the official price. Every key draws on the same prepaid balance, but each one carries its own limits, so a runaway script on one key can't drain what the rest of your stack depends on. Create as many as you want.
Make a new key#
Open the keys page
In the dashboard, go to API keys and press New key.
Give it a name and a group
Pick a name that tells you where the key lives, for example
support-bot-prodornightly-evals. Channel group can stay on Auto — best available; choose Claude · Value, GPT · Value or Gemini · Value only if every call from this key must run in that group. More in Channel groups.Add limits if you want them
Limits & restrictions lets you cap spend, request rate, concurrency, allowed models and lifetime. The table below explains each setting. All of them except the group can be changed later.
Copy it while you can
Press Create. A banner at the top of the page shows the full key a single time. Copy it now, or press Use in tools… to configure Claude Code, Codex CLI and similar clients with it straight away. Once you press Done, the list only shows the start of the key, such as
ho-a1b2c3….
You see the full key once
Half Off Dev keeps a hash, not the key itself, so nobody can show it to you again. A lost key has to be revoked and replaced.
Settings you can put on a key#
| Setting | Default | Effect |
|---|---|---|
| Name | — | Up to 60 characters. Appears in the key list and next to each request in Logs. |
| Channel group | Auto | Pins every request from the key to one channel group. Auto chooses the best available group request by request. Fixed at creation. |
| Spend limit (USD) | No limit | The most this key can ever spend, in whole dollars. It never resets: think lifetime cap, not monthly budget. |
| Expires on | Never | The key is refused once this day is over, in your browser's time zone. |
| Requests per minute | Unlimited | How many model requests the key may start in any minute. |
| Max concurrent requests | Unlimited | How many requests the key may have open at the same moment. A stream counts until its last byte. |
| Only allow specific models | All models | The models this key may call. An alias such as claude-sonnet-5-5 is checked as the model it points to. |
In the key list you can see each key's group and limits, how much it has Spent, and when it was Last used.
When a key hits a limit#
Half Off Dev refuses the request before it reaches a model. You are not charged, nothing is written to Logs, and the error comes back in the format of the endpoint you called.
| Cause | Status | Anthropic type | OpenAI type | Message |
|---|---|---|---|---|
| Past the expiry date | 401 | authentication_error | authentication_error | This API key has expired |
| Revoked | 401 | authentication_error | authentication_error | Invalid or revoked API key |
| Model outside the allow-list | 403 | permission_error | permission_denied | This API key is not allowed to use 'gpt-6.1-sol'. |
| Spend limit used up | 402 | billing_error | insufficient_quota | This API key has reached its spend limit. |
| Too many requests per minute, or too many at once | 429 | rate_limit_error | rate_limit_exceeded | This API key has reached its rate or concurrency limit. |
Requests to the Gemini endpoints get the same status codes, written in Gemini's error shape. A 429 caused by a key limit includes a Retry-After header with the number of seconds to wait.
The spend-limit refusal, as each SDK receives it:
{
"type": "error",
"error": {
"type": "billing_error",
"message": "This API key has reached its spend limit."
}
}The spend limit is checked when a request starts. A request that is already running is allowed to finish, so a key's Spent can end up a little above its limit. Every other error body, and when to retry, is in Errors.
Change a key later#
Press Edit on a key to update its name, spend limit, expiry, rate and concurrency limits or model allow-list. The next request already sees the change. Empty a field to remove that limit.
The channel group is the one setting you can't edit. To move traffic to another group, create a key in that group and rotate over to it.
Swap keys without downtime#
- Create the replacement with the same group and limits.
- Ship it to your app or secrets manager.
- Watch the old key's Last used time until it stops moving.
- Revoke the old key.
Do this on a regular schedule, whenever someone with access leaves the team, and right away if a key might have been exposed.
Revoke a key#
Press Revoke, then Confirm revoke. From the next request on, the key is rejected, and there is no undo. It stays in the list, greyed out, with its spend history, and its past requests remain in Logs.
Connect a coding client#
Every key has a Use in tools button with ready-made settings for Claude Code, Codex CLI, Gemini CLI, Cherry Studio, DeepChat and AionUI, including one-click import for apps that support it. Each client has its own guide under Integrations.
Habits that keep spend under control#
- A key per app and per environment. Production, staging, CI and your own laptop each get their own key, so you can read spend per key and revoke one without touching the rest.
- Cap anything that runs without a human watching. Batch jobs, CI pipelines and retry loops can spin out of control. A spend limit bounds the cost; a requests-per-minute limit slows the spin down.
- Allow only what the job uses. A key that classifies tickets with a small model has no reason to reach the largest one.
- Put an end date on temporary keys. Demos, contractors and trials get a key that expires by itself.
- Never ship a key to a browser or phone. Call Half Off Dev from your server. See Authentication.