Debug a Failing API Request
The problem
Your API call is failing with an unclear error — a 400 Bad Request, a 401 Unauthorized, a mysterious 422, or an intermittent 429. It could be a malformed body, an expired token, a wrong header, or a rate limit. This workflow systematically eliminates each possible cause in the right order, from the outermost layer (the request format) inward to authentication and then server-side constraints.
What you'll accomplish
Step-by-step
Why this workflow works
API debugging is most efficient when it follows OSI-like layer ordering: format first (is the request structurally valid?), authentication second (is the caller who they claim to be?), authorisation third (does the caller have permission for this resource?), and infrastructure last (is rate limiting or a network issue the constraint?). This workflow mirrors that order. Developers often jump to JWT inspection (Step 3) immediately on a 401 error, but 20–30% of auth failures are actually malformed request bodies (Step 2) that cause the server to reject the request before even checking the token. Running all five steps systematically is faster than jumping around because each step eliminates an entire category of failure cause.
Frequently asked questions
What is the most common cause of API authentication failures?
In order of frequency: (1) Expired JWT — the token's exp claim is in the past; regenerate it. (2) Wrong Authorization header format — 'Bearer TOKEN' not 'Token TOKEN' or 'Basic TOKEN'. (3) Incorrect token scope — the JWT is valid but doesn't include the permission scope for this endpoint. (4) Wrong audience (aud claim) — a token issued for staging is being used against production. (5) Clock skew — the server's clock and the client's clock differ by more than the tolerance (usually 5 minutes for JWT validation). (6) Revoked token — the token was manually invalidated before expiry. The JWT Decoder surfaces all of these in one view.
What is the difference between a 401 and a 403 error?
401 Unauthorized means the request lacks valid authentication credentials — you either didn't provide a token, provided an expired token, or provided an invalid token. The server can't verify who you are. 403 Forbidden means the server successfully authenticated you but you don't have permission to access this specific resource. You're known, but not allowed. Debugging path: 401 → check JWT validity (Step 3 in this workflow). 403 → check token scope, user role, resource ownership, or IP allowlist. A common confusion: some APIs return 403 for expired tokens instead of 401, which can misdirect debugging toward permission issues when the real problem is authentication.
How do I decode a JWT without sending it to an online tool?
A JWT is three base64url-encoded segments separated by dots: header.payload.signature. You can decode the header and payload locally: in Python — import base64, json; parts = token.split('.'); json.loads(base64.urlsafe_b64decode(parts[1] + '==')). In JavaScript/Node — JSON.parse(Buffer.from(token.split('.')[1], 'base64url').toString()). Note: this decodes the payload claims without verifying the signature. Signature verification requires the secret (HS256) or public key (RS256/ES256). The JWT Decoder in this workflow does both decoding and signature verification if you provide the key.
How do I handle rate limit errors in production code?
Best practice: (1) Read Retry-After header — wait exactly that many seconds before retrying, not less. (2) Implement exponential backoff with jitter — base delay × 2^attempt + random(0–1000ms). This prevents thundering herd when many clients retry simultaneously. (3) Track X-RateLimit-Remaining proactively — when it drops below a threshold (e.g., 10% of limit), slow down requests before hitting 429. (4) Implement a circuit breaker — after N consecutive 429s, stop sending requests for a defined period. (5) Queue and batch requests if the API supports it — bulk endpoints have higher rate limits than per-item endpoints. (6) Consider request-level caching — return cached responses for repeated identical requests.
What is the best way to share a working API endpoint with my team?
Options in order of preference: (1) REST Endpoint Documenter → export as OpenAPI YAML and commit to your repository alongside the integration code. (2) A Postman collection exported as JSON and shared via version control. (3) An internal wiki page with the request example in a code block. (4) A Markdown file in your repo's /docs folder. Avoid: Slack/Teams messages (unsearchable, disappear), email (siloed), and undocumented shared Postman workspaces (no version history). The goal is: in 6 months, someone encountering the same API can find the working example without needing to reverse-engineer or re-debug it.