Poor error handling is the silent killer of API adoption. You can have the fastest, most feature-rich API in the world — but if your errors are confusing, inconsistent, or unhelpful, developers will abandon it.
Here are 5 common API error handling mistakes and how to fix them.
1. Returning 200 OK for Errors
This is the cardinal sin of API design. Some APIs wrap every response in a 200 status and put the "real" status in the body:
// ❌ Don't do this
HTTP/1.1 200 OK
{
"success": false,
"error": "User not found"
}
This breaks HTTP clients, caching layers, and monitoring tools that rely on status codes.
// ✅ Do this instead
HTTP/1.1 404 Not Found
{
"error": {
"code": "USER_NOT_FOUND",
"message": "No user exists with ID 42"
}
}
Why it matters: Monitoring tools like Datadog and Grafana track 4xx/5xx rates automatically. If everything is 200, you're flying blind.
2. Generic Error Messages
Nothing frustrates a developer more than:
{
"error": "Something went wrong"
}
Your errors should tell developers what went wrong, why, and how to fix it:
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Request validation failed",
"details": [
{
"field": "email",
"issue": "Must be a valid email address",
"received": "not-an-email"
}
],
"docs": "https://api.example.com/docs/errors#VALIDATION_ERROR"
}
}
The docs link is the secret weapon — it reduces support tickets dramatically.
3. Inconsistent Error Formats
If your /users endpoint returns errors one way and /orders returns them differently, developers have to write custom error handling for each endpoint. Pick a format and stick to it:
// A consistent error response interface
interface ApiError {
error: {
code: string; // Machine-readable: "RATE_LIMITED"
message: string; // Human-readable: "Too many requests"
details?: any[]; // Optional field-level errors
request_id: string; // For debugging: "req_abc123"
}
}
Pro tip: RFC 9457 "Problem Details for HTTP APIs" is gaining traction as an industry standard. Consider adopting it for new APIs — tooling increasingly supports it out of the box.
4. Missing Rate Limit Headers
When you rate-limit a request, a bare 429 Too Many Requests is not enough. Always include these headers:
HTTP/1.1 429 Too Many Requests
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1739800800
Retry-After: 30
{
"error": {
"code": "RATE_LIMITED",
"message": "Rate limit exceeded. Try again in 30 seconds.",
"retry_after": 30
}
}
Without Retry-After, clients will either hammer your API with retries or give up entirely. Neither is what you want.
5. Leaking Internal Details in Production
Stack traces in error responses are a gift to attackers:
// ❌ Never in production
{
"error": "NullPointerException at com.myapp.UserService.find(UserService.java:42)",
"stack": "..."
}
// ✅ Safe for production
{
"error": {
"code": "INTERNAL_ERROR",
"message": "An unexpected error occurred",
"request_id": "req_x7k9m2"
}
}
Log the full details server-side, tagged with request_id. When a developer reports an issue, you can look it up instantly without exposing your internals.
Quick Checklist
Before shipping your API, verify:
- ✅ HTTP status codes match the actual outcome
- ✅ Every error has a machine-readable
codeand human-readablemessage - ✅ All endpoints use the same error format
- ✅ Rate limit responses include
Retry-After - ✅ No stack traces or internal paths leak in production
- ✅ Every error includes a
request_idfor debugging
Get these right and your API will be a joy to integrate with. Get them wrong and developers will quietly switch to your competitor.
Building APIs? 1xAPI provides ready-to-use APIs for email verification, sports data, and more — all with clean, consistent error handling built in.
Top comments (0)