Blog

401 vs 403 for API Developers

401 means the credential is missing or expired. 403 means the credential is valid and not allowed. Mixing them sends developers into the wrong fix, and they leave before they file anything.

2026-07-23 · 6 min read · AethelLayer

The next step is different

A 401 should tell them to refresh a token or pass the header. A 403 should tell them which permission or tenant is missing. One 'access denied' string makes both look like a bad password.

Use two stable codes. Scripts and agents branch on the code. Humans search it.

Say which credential you looked at

Name the header or the query parameter. Do not echo the secret. 'Authorization bearer was missing' is enough. 'The token for org_123 cannot call billing:write' is the 403 version.

If you have more than one auth scheme, say which one this route expects. Evaluators paste an OAuth access token into an API-key header all week.

SDKs must not collapse the two

Throw distinct errors. A single ApiError with only the HTTP status forces every sample into the same catch. The code property is the contract.

On 401, the SDK may refresh once if it owns the refresh token. On 403, it must not retry. Retrying a permission error looks like abuse.

Cluster by code and route

A spike of 403 on one new route is a scope you forgot to document. A spike of 401 on login is an expired sample token. The status code alone mixes them if you only chart '4xx'.

Keep the developer's sentence with the probe. 'I was using the key from the dashboard example' is the doc bug.

Capture the failure before they leave

Capture the failure before they leave. Book early access or talk to sales, then install @aethellayer/sdk when you are onboarded.