Core answer: Route every API error by status code — 4xx means the client side (401 unauthenticated, 403 forbidden, 429 too many requests), 5xx means the server side (502 upstream down, 503 overloaded, 504 timeout). Decode a failing JWT locally and read exp first. A 99.9% SLA equals 43.8 minutes of downtime per month.
Status code decision tree
- 4xx? The request side: parameters, auth, permissions, rate
- 5xx? The service side: check the status page before retrying
- No status code (CORS/network)? The request never arrived or the browser blocked the response — read the console, the API itself may be fine
High-frequency codes table
| Code | Meaning | First move |
|---|---|---|
| 400 | Malformed request | Check field types against the docs |
| 401 | Not authenticated | Token expired or missing |
| 403 | No permission | Check scope/role/IP whitelist |
| 404 | Path not found | Check URL spelling and version prefix |
| 429 | Rate limited | Exponential backoff; read Retry-After |
| 500 | Server exception | Report with the trace id |
| 502 | Upstream down | Check upstream process liveness |
| 504 | Gateway timeout | Slow queries / timeout config |
Search all 26 codes with the [HTTP status lookup](/c/dev/http-status).
JWT structure and expiry
A JWT is xxxxx.yyyyy.zzzzz, three Base64URL segments: header (algorithm), payload (claims — exp/iat/nbf as Unix seconds), signature. Paste a token into the [JWT decoder](/c/dev/jwt) to read header and payload locally; exp converts to local time with a live countdown. Decoding is not verifying — anyone can read a payload, so never put sensitive data inside a JWT.
SLA downtime budget table
| SLA | Per year | Per month | Typical use |
|---|---|---|---|
| 99% | 3.65 days | 7.3 h | Internal tools |
| 99.9% | 8.77 h | 43.8 min | General commercial |
| 99.95% | 4.38 h | 21.9 min | E-commerce |
| 99.99% | 52.6 min | 4.38 min | Payments |
| 99.999% | 5.26 min | 26.3 s | Carrier grade |
Each extra nine cuts the budget by 10× and multiplies architecture cost — multi-region active-active is the entry ticket to 99.99%.
Example: five steps for a 502
curl https://api.example.com/pay returns 502: ① retry from another network — still 502; ② check the status page — nothing posted; ③ curl -v shows an nginx server header — the gateway is alive, the upstream is down; ④ ss -lptn on the gateway — the upstream port is not listening, the app crashed; ⑤ the app log ends with OOM killed — restart, cap memory, add alerting.
Example: hunting a JWT 401
Front end gets 401 from /api/orders: ① decode the token — exp was 2 hours ago, expired; ② why did refresh not fire? The refresh call also 401s; ③ the refresh token decodes fine → inspect the request header → Authorization was concatenated without the space after "Bearer". Tools used: JWT decoder for time, [URL parser](/c/dev/url-parser) for query parameters, status lookup for 401 vs 403 semantics.
Common mistakes
- Using 401 as 403: 401 = "who are you" (missing/expired credentials), 403 = "I know you, and no". Mixing them breaks front-end login redirects.
- Retrying 5xx without backoff: blind retries during an outage are a self-DDoS. Use exponential backoff with jitter and a circuit breaker.
- Storing personal data in JWT: the payload is only Base64 — readable by anyone. A JWT is not an encryption container.
- Promising four nines casually: 99.99% is 52 minutes per year; one careless restart can spend the whole budget.