top of page

Bad-request vs Unprocessable-entity


One of the most important aspects of a great API is the way in which it responds to its clients. How it returns the expected results and how errors are reported with proper (and unique) codes and descriptions are a couple of topics to care about while you are developing an API.

During the development of a REST API, there are mainly two types of errors that our clients care about:

  • Bad request responses: We provide information about the input format. For example, if a text input was meant to be an email, we simply tell the client that.

  • Unprocessable entity responses: This type of response aims to tell the client “your input has the right format, BUT somehow the server was not able to process the request”.

For example, let’s imaging we have an API with a single endpoint:

POST /process?

The rules to call this endpoint are:


  • The query parameter userId MUST be an email.

  • The query parameter accountId CAN NOT be a number.


  • To start the so-called process, the user MUST be ACTIVE.

  • To start the so-called process, the account MUST be OPEN.

The last couple of rules are meant to provide context about a business process. For the sake of this example, let’s say that there are two internal services that allow us to check those statements.

public interface IAccountRepository
    Task<bool> IsAccountOpen(string accountId);
}public interface IUserRepository
    Task<bool> IsUserActive(string userId);

As you can imagine, there is a difference in terms of cost to check the format-rules (very fast and we don’t need any extra call) and the business-rules (Their cost is high since requiring extra operations inside our system).

How we do it

Compromising between speed and user-experience, we decided to:

  • All format errors are provided to the client. We follow the core 3.1 format, which is quite handy. Also, the format errors do not have a dedicated error code.

Request and response provided by the example service using Postman.

  • When there is a business error, only the first occurrence is provided to the client to obtain a fast (and cheap) response.

Unprocessable entity response with error code and description when user status is inactive.


The implementation is quite straight-forward:

  • For the format rules, you need to make use of the class called “ModelState”. This class allows storing errors that would be shown to the user as the content of the bad-request response.

How to manage input format verification with ModelState class.

  • For the business rules, my advice is to use a dedicated “BusinessExceptionMiddleware” that catches business exceptions. You can add it to your middleware pipeline and, at any point inside your service, launch business exceptions.

Business exception middleware.

Injection of the BusinessExceptionMiddleware.

After injecting your middleware, you can launch business exceptions that will be caught by your middleware. Example:

Launching exceptions when business rules are not verified.


We’ve seen our way to respond to our clients. We separate our responses into Bad-request and Unprocessable-entity. The first one provides all the errors the client did in an array as a response. The second one provides a single occurrence of the problem to avoid expensive flows and fast response.

Source: Medium - Eduard Bargues

The Tech Platform



bottom of page