---
title: "Best Practices for Consistent API Error Handling"
description: "Mastering API error handling enhances developer experience and reduces debugging time, ensuring clearer communication and systematic management."
canonicalUrl: "https://zuplo.com/learning-center/best-practices-for-api-error-handling"
pageType: "learning-center"
authors: "adrian"
tags: "API Management, API Best Practices, Tutorial"
image: "https://zuplo.com/og?text=Best%20Practices%20for%20Consistent%20API%20Error%20Handling"
---
**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](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status), but here's a
few helpful docs we've written in the past that go into more depth:

- [HTTP 429 Too Many Requests](/learning-center/http-429-too-many-requests-guide)
- [HTTP 431 Request Header Fields Too Large](/learning-center/http-431-request-header-fields-too-large-guide)

### RFC 9457 Error Response Format

For consistent error reporting, modern APIs should follow the
[RFC 9457](https://www.rfc-editor.org/rfc/rfc9457.html) Problem Details
specification. This is the successor to the popular
[RFC 7807](https://www.rfc-editor.org/rfc/rfc7807) draft. For an in-depth
understanding of this format, check out our
[full problem details guide](/learning-center/the-power-of-problem-details). 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:

```json
{
  "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
HTTP/1.1 422 Unprocessable Content
Content-Type: application/problem+json
Content-Language: en
```

We talked with Erik Wilde, one of the authors of RFC 9457 about the impetus and
implementation of the standard. Check it out here:

<YouTubeVideo videoId="OOVUxzoWkhQ" />

### 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](/learning-center/semantic-api-versioning) for
  your endpoints
- Include [deprecation notices](/learning-center/deprecating-rest-apis) 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\]](https://google.aip.dev/193) 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:

```json
{
  "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:

```json
{
  "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.

## Error 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:

```json
{
  "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:

```protobuf
{
  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\]](https://google.aip.dev/193).

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\]](https://raygun.com/blog/best-error-monitoring-tools/)[\[3\]](https://techcommunity.microsoft.com/discussions/appsonazure/best-practices-for-api-error-handling-a-comprehensive-guide/4088121):

- **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](https://sentry.io/) support distributed tracing in over 15
languages, while [Raygun](https://raygun.com/) offers deployment correlation to
pinpoint issues [\[2\]](https://raygun.com/blog/best-error-monitoring-tools/).

### 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](/learning-center/end-to-end-api-testing-guide) using
tools like Playwright and StepCI.

If you have schematized errors, you can perform
[schema validation](/blog/verify-json-schema) on your live responses to ensure
they adhere to those schemas. When response validation is combined with an
[API design specification](/learning-center/mastering-api-definitions) 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\]](https://raygun.com/blog/best-error-monitoring-tools/)[\[5\]](https://blog.postman.com/best-practices-for-api-error-handling/).

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:

1. **Audit and Define Schema**  
   Use an
   [API monitoring tool](/learning-center/8-api-monitoring-tools-every-developer-should-know)
   to analyze errors at the endpoint level. This helps identify inconsistencies
   and establish a baseline for improvement
   [\[2\]](https://raygun.com/blog/best-error-monitoring-tools/)[\[5\]](https://blog.postman.com/best-practices-for-api-error-handling/).
2. **Implement Middleware**  
   Introduce centralized error-handling middleware to enforce the newly defined
   standards. Often, an API gateway plays this role.
3. **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.