Best Practices for Consistent API Error Handling
Clear and consistent API error handling is crucial for improving developer experience and reducing debugging time. Poor practices, like unclear messages or misuse of HTTP status codes, can frustrate developers and lead to increased support tickets. This guide covers actionable strategies to standardize API error handling, including:
- Use of HTTP Status Codes: Ensure accurate mapping (e.g., 400 for client errors, 500 for server issues).
- Structured Error Responses: Follow RFC 9457 (Problem Details) standards for clear, actionable error details.
- Error Message Clarity: Write brief, helpful, and secure messages.
- Protocol-Specific Practices: Tailor error handling for REST, GraphQL, and gRPC APIs.
Standards for API Error Handling#
To address the challenges highlighted earlier, you can apply these well-established methods.
Using HTTP Status Codes#
HTTP status codes are your first tool for communicating errors. The key is to use them accurately, rather than relying on generic codes.
Status Range | Purpose | Common Examples |
---|---|---|
400-499 | Client Errors | 401 Unauthorized, 422 Unprocessable Entity |
500-599 | Server Errors | 500 Internal Error, 503 Service Unavailable |
You can find a full list of status codes on MDN, but here's a few helpful docs we've written in the past that go into more depth:
RFC 9457 Error Response Format#
For consistent error reporting, modern APIs should follow the RFC 9457 Problem Details specification. This is the successor to the popular RFC 7807 draft. For an in-depth understanding of this format, check out our full problem details guide. In case you're short on time - here's a quick overview:
The problem details response is sent back as a JSON body with the following properties:
- type (string, URI): A URI that identifies the specific error type. This helps clients understand the error and potentially find more information or documentation about it. Ideally, this URI should be stable and not change over time.
- title (string): A short, human-readable summary of the problem. This should be a brief description that concisely conveys the error. The title should not change for a given "type" URI.
- status (integer, optional): The HTTP status code generated by the origin server for this occurrence of the problem. This helps clients understand the nature of the error and how it relates to the HTTP protocol.
- detail (string, optional): A more detailed, human-readable explanation of the problem. This can include specific information about the error and what might have caused it. The "detail" field is intended to provide context and suggestions to clients on how they might address the problem.
- instance (string, URI, optional): A URI that identifies the specific occurrence of the problem. This can help clients and servers correlate and track individual instances of errors.
Here's what a standardized error response might look like:
{
"type": "https://api.example.com/errors/invalid-input",
"title": "Invalid Input Parameters",
"status": 422,
"detail": "Email address must use user@domain.com format",
"instance": "/transactions/auth/2024-02-10/125"
}
This response would be accompanied with the following headers and status code:
HTTP/1.1 422 Unprocessable Content
Content-Type: application/problem+json
Content-Language: en
Here's a video that shows you how to send these error back in practice. It's in .Net/C# but the concepts are broadly applicable:
Updating Error Schemas#
When evolving error schemas, it's crucial to make changes without disrupting existing clients. Here's how you can manage this:
- Add optional fields instead of altering existing ones
- Preserve legacy formats during transitions
- Implement semantic versioning for your endpoints
- Include deprecation notices in metadata
API management tools like Zuplo are really handy when trying to bring consistency across your error formats, and are especially useful when trying to transition all of your APIs over from one format to another.
Writing Clear Error Messages#
Creating effective API error messages means providing useful guidance while keeping security in mind. Google's AIP-193 guidelines [4] recommend using a structured format with plain, straightforward language that remains technically accurate.
Clear and Helpful Messages#
The goal is to make error messages both clear and actionable. Here's a comparison of good and bad examples:
Component | Good Example | Bad Example |
---|---|---|
Message | "Invalid email format in 'user_email' field" | "ValidationError: field_23" |
Action | "Use format: user@example.com" | "Check input and try again" |
Context | "Request to /api/v1/users failed" | "Error in system" |
If you've ever used Azure, many of their system errors demonstrate this approach, for example:
{
"error": {
"code": "InvalidParameter",
"message": "Email format invalid",
"details": "Use user@domain.com format",
"target": "/users/email"
}
}
Security in Error Messages#
Maintaining security while providing useful error feedback involves a few key practices:
- Sanitize sensitive data to prevent leaks.
- Standardize authentication errors for consistency.
- Validate input carefully to avoid exposing vulnerabilities.
For example, following RFC 9457 guidelines, a secure error response might look like this:
{
"error": {
"code": "SERVICE_UNAVAILABLE",
"message": "The service is temporarily unavailable",
"retry_after": 300
}
}
This is another area where using an API gateway/API management tool is useful. You can monitor your outbound responses and scan them for PII or other sensitive information/keywords using a regex. A programmable gateway (ex. Zuplo) will even let you rewrite your response bodies to strip out sensitive data.

Over 10,000 developers trust Zuplo to secure, document, and monetize their APIs
Learn MoreError Handling by API Type#
Let's dive into how different API protocols handle errors, building on the standards discussed earlier.
REST Errors#
REST APIs rely on HTTP status codes paired with structured error payloads. A common standard for this is RFC 9457 Problem Details, which ensures a consistent format across endpoints. This method also supports content negotiation between JSON and XML, keeping the structure consistent across different formats.
GraphQL Errors#
GraphQL handles errors differently. It always responds with a 200 OK status
code, even when errors occur. Errors are communicated through an errors
array,
which allows for partial success. For instance, GitHub's GraphQL API might
return:
{
"data": {
"user": {
"name": "John Doe",
"email": null
}
},
"errors": [
{
"message": "Not authorized to access email field",
"locations": [{ "line": 5, "column": 3 }],
"path": ["user", "email"]
}
]
}
This approach allows for returning valid data alongside error details.
gRPC Errors#
gRPC uses a predefined set of numeric status codes (ranging from 0 to 16) for
error handling, aligned with grpc.status
. Errors include structured details
for better context. Here's an example:
{
code: 3,
message: "Invalid email format in user creation request",
details: [{
"@type": "type.googleapis.com/google.rpc.ErrorInfo",
"reason": "VALIDATION_ERROR",
"domain": "user-service",
"metadata": {
"field": "email",
"violation": "format"
}
}]
}
Comparison Table#
Protocol | Primary Error Mechanism | Success Response | Error Detail Location |
---|---|---|---|
REST | HTTP Status Codes | Standard HTTP success codes | Response Body |
GraphQL | Errors Array | Always 200 | Errors + Data Objects |
gRPC | Status Codes | Code.OK (0) | Status Message + Details |
Each protocol has its own approach, but they all follow two key principles: machine-readable error codes and human-readable details. This ensures errors are both understandable and actionable.
For cross-protocol APIs, API gateways can simplify error handling. They provide unified error schema management and automate transformations between protocol-specific formats. This helps maintain consistency while respecting the conventions of each API type.
Error Management Tools and Methods#
Effective error management relies on consistent monitoring and testing to uphold usability standards outlined in earlier protocols. This process builds on protocol-specific error conventions to ensure smooth handling across systems.
Error Code Management#
A centralized error code system can ensure uniformity across distributed
services. For example, Google uses the google.rpc.Status
format, which
enforces standardized error structures with both machine-readable codes and
human-readable messages [4].
In multi-service architectures, two main approaches are common:
Approach | Benefits | Challenges |
---|---|---|
Centralized Registry | Ensures uniform error codes, Acts as a single source of truth | Requires strict oversight |
Distributed with Prefixes | Allows team independence, Enables quicker updates | Demands thorough documentation |
Many organizations find that combining these methods provides the best results.
Error Tracking#
Key metrics to monitor include [2][3]:
- Mean Time to Acknowledge (MTTA) errors: Target under 30 minutes.
- Error recurrence rate: Should remain below 5% after fixes.
- 95th percentile error resolution time: A critical benchmark for resolution speed.
- User-impacting error ratio: Measured per 10,000 requests.
Tools like Sentry support distributed tracing in over 15 languages, while Raygun offers deployment correlation to pinpoint issues [2].
Error Testing#
Testing ensures compliance with HTTP status codes and response formats covered in earlier sections. Error testing is typically done manually using tools like Postman - but you should definitely invest in automation as your API grows and evolves. To test specific errors, you should invest in automated end-to-end API testing using tools like Playwright and StepCI.
If you have schematized errors, you can perform schema validation on your live responses to ensure they adhere to those schemas. When response validation is combined with an API design specification like OpenAPI to enforce outputs match what's documented, it's known as Contract Testing.
Summary#
Main Points#
Effective API error handling builds on the protocol-specific conventions discussed earlier. Two key goals are ensuring consistent response formats and reducing repeat client errors [2][5].
Key requirements include:
- Standardized Response Structure:
- Machine-readable error codes
- Clear, human-readable messages
- Links to relevant documentation
- Request correlation IDs for troubleshooting
- Security-Focused Practices:
- Prevent exposure of sensitive data by adhering to security guidelines outlined in the Writing Clear Error Messages section
- Use appropriate status codes
- Follow established security best practices
Implementation Steps#
To create a reliable error-handling system, follow these four phases:
- Audit and Define Schema
Use an API monitoring tool to analyze errors at the endpoint level. This helps identify inconsistencies and establish a baseline for improvement [2][5]. - Implement Middleware
Introduce centralized error-handling middleware to enforce the newly defined standards. Often, an API gateway plays this role. - Monitor and Improve
Use error tracking tools to evaluate the system’s performance. Track metrics like error recurrence rates, resolution times, and Mean Time to Acknowledge (MTTA).
These steps align with earlier recommendations for policy-driven error handling and offer practical ways to enhance your API's reliability.
FAQs#
How do you handle errors in API?#
Handling API errors effectively requires a clear and structured approach. This involves combining standard protocols with additional application-specific information to provide clarity and maintain security.
1. Protocol-Specific Handling
Each API protocol has its own method for managing errors. Here’s how to handle errors for some common protocols:
- REST APIs: Use HTTP status codes alongside detailed error messages in the response body.
- GraphQL: Include error arrays in the response, allowing for partial success when appropriate.
- gRPC: Utilize standardized status codes with structured error details.
2. Security Best Practices
To keep your API secure, follow these guidelines (as outlined in the "Security in Error Messages" section):
- Use generic error messages for authentication failures to prevent revealing sensitive information.
- Avoid exposing internal system details in error responses.
- Filter sensitive data on the server side before sending error responses.
3. Monitoring and Consistency
Set up monitoring tools, such as distributed tracing, to identify and analyze error patterns. Use API gateways to enforce consistent error formats and schemas across your system for better management and debugging [2][3].