How to Handle API Errors Like a Pro
Every API will fail at some point. Servers go down, rate limits get hit, networks drop, and responses come back malformed. The difference between a frustrating app and a great one is how it handles these failures. Here's a complete guide to API error handling.
Understanding HTTP status codes
When an API responds, it includes a three-digit status code that tells you what happened. Here are the ones you'll encounter most:
Success codes (2xx)
- 200 OK — Everything worked. The response contains your data.
- 201 Created — Your POST request created a new resource successfully.
- 204 No Content — Success, but there's no response body (common for DELETE requests).
Client error codes (4xx) — something YOU did wrong
- 400 Bad Request — Your request was malformed. Check your parameters, headers, or request body.
- 401 Unauthorized — Your API key is missing, expired, or invalid.
- 403 Forbidden — Your key is valid but you don't have permission for this resource.
- 404 Not Found — The endpoint or resource doesn't exist. Check the URL.
- 422 Unprocessable Entity — The request format is correct, but the data is invalid (e.g., wrong data types).
- 429 Too Many Requests — You've hit the rate limit. Slow down and retry later.
Server error codes (5xx) — something on THEIR end went wrong
- 500 Internal Server Error — The API server crashed. Not your fault; try again later.
- 502 Bad Gateway — A server in the chain is down or misconfigured.
- 503 Service Unavailable — The API is temporarily overloaded or in maintenance.
- 504 Gateway Timeout — The API server took too long to respond.
Basic error handling pattern
Every API call in your app should follow this pattern:
Retry with exponential backoff
For transient errors (429, 500, 502, 503, 504), retrying often works. But don't retry immediately — use exponential backoff to give the server time to recover:
The random jitter (Math.random() * 1000) prevents the "thundering herd" problem where many clients retry at the same moment.
Show users helpful error messages
Your users don't need to see "HTTP 503 Service Unavailable." Translate technical errors into friendly messages:
Common mistakes to avoid
- Swallowing errors silently. An empty
catch {}block means bugs go unnoticed. Always log errors, even if you handle them gracefully in the UI. - Retrying non-retryable errors. A 404 won't magically start working. Only retry on transient errors (429, 5xx).
- No timeout. A hanging request can freeze your UI. Always set a timeout (5-10 seconds is usually appropriate).
- Showing raw error responses. API error messages are for developers, not users. Translate them.
- Not handling network errors.
fetch()throws a TypeError when the network is unreachable. This is different from an HTTP error status.
Testing error handling
Don't wait for errors to happen in production. Test them intentionally:
- Use an invalid API key to test 401 handling
- Request a non-existent resource to test 404 handling
- Disconnect your network to test network error handling
- Use browser DevTools to throttle network speed
- Build a mock server that returns specific error codes on demand
Good error handling isn't glamorous, but it's what separates apps that feel polished from apps that feel broken.