403 model_not_allowed_for_key explained
The key works, the balance is fine, and the model still returns 403. Here is how per-key model allowlists work and how to fix them.
4 min · · Updated · errors
The key authenticates fine, the balance isn't empty, and the request still fails with 403. The cause is almost never the key itself — it's the key's settings, specifically the list of models that particular key is allowed to call.
What this error means
{"error": {"message": "Model 'claude-opus-5-5' is not allowed for this API key", "code": "model_not_allowed_for_key"}}Every API key in the dashboard can have not just a daily or monthly spending limit in dollars, but also a list of models that specific key is permitted to call. If the model in the request isn't on that list, the gateway rejects it before it ever reaches the provider — the same way it does with an empty balance: a failed attempt costs nothing.
This is a separate mechanism from 429 (key_daily_limit_reached / key_monthly_limit_reached) — that one is about an exhausted budget, this one is about the model itself, regardless of how much of the key's limit is left.
Why restrict models on a key at all
This is typically set up when one account issues different keys for different purposes: a key for a test environment or a third-party contractor gets limited to cheaper models like glm-5.3-flash or gpt-5.6-luna, while access to the pricier claude-opus-5-5 stays on the main key only. If such a restricted key accidentally ends up in code that starts sending requests to the pricier model, the gateway simply refuses — instead of quietly charging against someone else's limit.
How to check the allowed model list
The setting lives in the dashboard, on the specific key's page: https://zukko.pro/app/api-keys — open the relevant key and look at the block listing allowed models and spending limits. If the list is empty or unset, all catalog models are typically allowed; if it's set, only the listed ones work.
You can check this programmatically too, by comparing the model name from the error against what's set on the key's page — but the API itself doesn't expose a per-key allowed-models list through a separate endpoint, only the general catalog:
curl https://api.zukko.pro/v1/modelsThis list is every model available on the gateway in principle, not what's allowed for your specific key. The gap between "exists in the catalog" and "allowed on this key" is exactly where the 403 comes from.
What to do when 403 shows up unexpectedly
Check that it's the right key. If your code reads a key from an environment variable rather than hardcoding it, make sure it isn't pulling in an old, restricted test key instead of the main one.
Open the key's page in the dashboard. The allowed-models list is set right there — add the model you need if you have permission to, or ask whoever issued the key to change it.
Switch models in the request, if the restriction is intentional. If a key is deliberately limited to, say, gpt-5.6-sol and glm-5.3 for budget reasons, it's simpler to switch to an allowed model than to change the key's policy.
ALLOWED_FALLBACK = {
"claude-opus-5-5": "claude-sonnet-5",
"gpt-5.6-sol": "gpt-5.6-luna",
}
def call_with_fallback(model, messages, api_key):
try:
return call_api(model, messages, api_key)
except ModelNotAllowedError:
fallback = ALLOWED_FALLBACK.get(model)
if fallback:
return call_api(fallback, messages, api_key)
raiseHow it differs from similar errors
Worth not confusing 403 model_not_allowed_for_key with its neighbors:
- 404 model_not_found — no model with that name exists in the catalog at all, a typo in the id.
- 400 model_retired — the model used to be in the catalog but was decommissioned; the response names its replacement.
- 503 model_disabled — the model is disabled in the catalog for everyone, not just for one key; the response names no replacement.
- 403 model_not_allowed_for_key — the model exists and works fine, but this particular key is policy-restricted from calling it.
If you're unsure which case you're in, the error text and the code field in the JSON tell you exactly which one it is — no need to guess from symptoms.
A quick check before deploying
Before rolling out code with a new key to production, it's worth making a trial call to every model the service uses, to confirm a 403 won't surface only after release:
for model in claude-sonnet-5 claude-opus-5-5 gpt-5.6-sol; do
echo "Checking $model..."
curl -s -o /dev/null -w "%{http_code}\n" https://api.zukko.pro/v1/messages \
-H "Authorization: Bearer fetch_your_key" \
-H "Content-Type: application/json" \
-H "anthropic-version: 2023-06-01" \
-d "{\"model\":\"$model\",\"max_tokens\":8,\"messages\":[{\"role\":\"user\",\"content\":\"ping\"}]}"
doneA 200 means the model is allowed and the key works; a 403 with model_not_allowed_for_key is a signal to fix the model list on the key's page before users hit it in production, rather than after the first bug report.
Key restrictions don't inherit or add up
If a service spreads load across several keys, each key's limits and permissions are evaluated independently: one key's daily limit doesn't affect another's, and one key's allowed models don't open access through a different key. The allowed-models list has to be synced manually on each key separately, through the dashboard at https://zukko.pro/app/api-keys — there's no group setting that applies to several keys at once.
Bottom line
403 model_not_allowed_for_key isn't a failure or a balance issue — it's the result of a specific key's configuration in the dashboard: it has an allowed-models list, and the requested model isn't on it. Fix it on the key's page at https://zukko.pro/app/api-keys — either by adding the model to the list, or by switching your code to a model that's already allowed.
zukko
Pay 25×
less
for Claude Code & ChatGPT
One fetch_* key and base URL api.zukko.pro — for Cursor, Claude Code, Codex, and any SDK.
Related articles
- OpenAI API Keys in the Open: Why You Should Not Use ThemWhere free keys on GitHub come from, why using them is dangerous for you and for the owner, and how to protect your own key from the same leak.
- GLM API Errors: Why the Request Fails and How to Fix ItA wrong model id, an overflowing 128K context, the key, the balance, limits: the typical causes of GLM API errors and a quick way to find yours.
- This organization has been disabled in the Claude API: What to DoAn Anthropic key suddenly answers "organization has been disabled". What the error is, what to check, and how to move the SDK and Claude Code to another provider.
- JetBrains AI Assistant not working: causes and workaroundsAI Assistant will not activate, stays silent or is off for some files. We go through the causes in the JetBrains docs and connect your own models via a gateway.