← Back to Blog

Fix 403 Forbidden and Permission Denied Errors in AI APIs

Zivv14 min read
403permissionstroubleshooting

A 401 means the server does not know who you are. A 403 means it knows exactly who you are, and the answer is no. That distinction changes the whole debugging path: with a 403 your key authenticated successfully, so re-copying it, regenerating it, or reinstalling the client rarely helps. Something between the key and the specific thing you asked for is refusing the request.

In practice a 403 from an AI API comes from one of four places: what the key is allowed to do, which model group the key belongs to, where the request comes from, or a policy set by your organization. Work through them in that order.

Map the Response to the Cause First

The body of the 403 usually tells you which bucket you are in, if you look at the raw response instead of the client popup:

What the response looks likeLikely causeWhere to fix it
HTML page, often with a CDN or firewall bannerRegion or network block before the APINetwork path, not the API config
permission_denied or permission_error JSONKey lacks access to this operation or modelKey settings in the console
Message naming a model or groupKey group does not include the modelKey group configuration
Message mentioning organization or policyAdmin-side restrictionYour workspace admin

A fast way to separate "real API refusal" from "blocked on the way there" is to check what came back, not just the status code:

curl -sS -o /dev/null -w "%{http_code} %{content_type}\n" \
  https://zivv.pro/v1/models \
  -H "Authorization: Bearer sk-your-key"

403 application/json is the API making a decision about your key. 403 text/html means something in the network path, a corporate proxy, a firewall, or a regional block, answered before the API ever saw the request.

Check What the Key Is Actually Allowed to Do

Keys are rarely all-powerful. Depending on the platform they can carry scopes, model allowlists, endpoint restrictions, and expiry dates. Start by asking the API what this key can see:

curl https://zivv.pro/v1/models \
  -H "Authorization: Bearer sk-your-key"

Then send a minimal request to a model that appeared in that list:

curl https://zivv.pro/v1/chat/completions \
  -H "Authorization: Bearer sk-your-key" \
  -H "Content-Type: application/json" \
  -d '{"model":"gpt-5.5","messages":[{"role":"user","content":"Reply OK"}]}'

Interpret the combination:

  • Model list works, chat works: the key is fine, and your original request asked for something outside its allowlist. Compare the model ID in the failing request against the list.
  • Model list works, chat returns 403: the key can authenticate but is restricted at the operation or model level. Open the key settings and check its allowlist and status.
  • Both return 403: the key itself is disabled, expired, or scoped away from this endpoint entirely.

Model Group Access

Gateways and multi-model platforms organize keys into groups, and each group maps to a set of models and protocols. A key created for one workflow will not automatically reach every model on the platform. The classic symptom: the same key calls GPT models fine but returns permission denied on Claude models, or works in an OpenAI-compatible SDK but fails in Claude Code.

On Zivv, for example, the Claude MAX group exists specifically to serve Claude Code over the native Anthropic protocol. A key attached to a group is expected to be used with that group's models and protocol; asking it for something else is a permissions question, not a bug. Check which group the failing key belongs to, and compare against the model you requested in Model Hub. If the model ID itself is stale rather than forbidden, you will typically see a 404 instead, covered in the 404 model guide.

Region and Network Blocks

Official provider APIs enforce regional availability, and some networks add their own filtering on top. Signals that you are in this bucket:

  • The 403 body is HTML, not JSON
  • The same key works from a different network, a phone hotspot is the quickest test
  • Direct calls to an official provider fail while other HTTPS traffic is fine
  • A corporate proxy or VPN is in the path

Routing personal traffic through ad-hoc VPNs to reach an official API is fragile: exit nodes change, and you can end up violating the provider's terms. The stable fix is to call an endpoint that is reachable from where you actually are. A gateway like Zivv gives you one endpoint in front of 100+ models across the OpenAI, Anthropic, and Gemini protocols, so per-provider regional quirks stop being your client's problem.

Organization Policy and Team Restrictions

If you are on a team account, an admin can restrict things per member: which models a member key may call, spending limits per key or per model, and key expiry. Some clients render any refusal as a generic "permission denied", so the 403 you see may simply be a budget or model policy doing its job.

Before escalating, check your own usage page to confirm whether the request even reached the platform, then ask the admin what the key's group, allowlist, and budget look like. Teams that issue one key per member with explicit budgets debug this in minutes, because the refusal maps to a single visible setting instead of a shared mystery key.

FAQ

The key worked yesterday and returns 403 today. What changed? Something stateful: the key expired, its group or allowlist was edited, an org policy tightened, or your network path changed. Keys do not decay on their own; find the setting that moved.

Is a 403 ever a billing problem? Billing exhaustion normally surfaces as a 402 or an explicit insufficient-balance message, not a 403. But team budget caps can be enforced as permission refusals in some clients, so if you are on a team account, check budgets too.

Same key: GPT works, Claude returns 403. Why? Key group or protocol mismatch. The key's group includes the GPT models but not the Claude ones, or you are calling the Claude models through the wrong protocol endpoint. Fix the group, not the key.

Should I just create a new key when I see 403? Only after checking the settings on the existing one. A new key created with the same group and restrictions will fail identically, and now you have two keys to audit.

Once you know which bucket your 403 lives in, the fix is usually one setting. If the underlying problem is juggling per-provider access rules, a single Zivv key in front of all three protocols removes most of the surface area where 403s breed.