A simple guide to HTTP status codes for your APIs. The 12 essential status codes you need to know

There are many dozen HTTP status response codes that you can return from your back-end API. Fortunately, you do not need to know and memorize every single one of them. The guide would show the essentials you need to know and how to use them correctly to write great APIs. This guide will show you how to use the essentials such as 200, 201, 400, 422, and explain why should you not be explicitly throwing 500 errors.

For front-end developers, this guide would also provide you an overview of the different HTTP response codes so you can better debug API requests.

Why is it important

It is important to use the correct status codes as they are essential to communicate statuses and errors. Using the wrong status codes can lead to end-users of an API debugging the wrong issues. It happens a lot where a back-end would throw an authorization error code when the issue has to do with the request format.


The first digit of the code helps you to quickly understand what's going on. They are essentially like this:

  • 2** - Success
  • 3** - Redirect
  • 4** - Client-side f**ked up
  • 5** - Server-side f**ked up

2** - Success

200: OK

The most standard response that was for a long time the only used code for websites. You can use this code for every success case if you wish, skipping the others here would be completely acceptable.

201: Created

These are the second most used of the 200s. It is for POST requests where new entries are created such as:

  • Registering a user
  • Adding a post
  • Adding a directory listing
  • You get the idea...

202: Accepted

This is to indicate that you have accepted a request but may not have necessarily completed it. For example, the request to the API may start to run a long processing task that you cannot confirm that it completed, like sending an OTP (one-time PIN) code.

204: No Content

No content is for APIs that return no body content, like endpoints where you would just like to check if an email is taken when users are signing up. In such cases, you could do with just success and failure status codes without needing a response body content.

3** - Redirect

Redirects are less commonly used for API purposes as APIs are often revised instead of redirected. They are more relevant for web pages.

301: Moved permanently

It indicates that the URL has been moved permanently. This can lead clients (such as a browser) to 'cache' the new URL and make them automatically call the new URL instead of trying to look up the previous URL. Since you have said it was permanent.

302: Found (aka moved temporarily)

It is the same as the 301, indicating that the client should redirect. Instead, you're declaring that it is a temporary redirect and that they should come back to looking up this URL at some point.

4** - Client error

400: Bad request

Send this when there is an error with the request such as invalid parameters, missing data, etc. It is also a generic and default code for errors that do not have any other appropriate response code.

401: Unauthorized

The code 401 has to do with authentication. It is often incorrectly interchangeably used with 403. The appropriate time to send a 401 response is to indicate that the client did not send any authentication credentials.

403: Forbidden

This error code has to do with authorization. It means they have credentials but they are wrong or insufficient. There are two common cases when a user fails authorization.

  1. They have sent credentials but the credentials are invalid (bad token, wrong password).
  2. They have valid credentials but do not have the privilege and authorization
    • A User trying to access Admin stuff
    • Changing the password of an account that isn't theirs

404: Not found

The most well-known that doesn't need much explanation. For the server-side, the use case is when the client sends an unknown entity ID (which can either be in the URL or request body). For example:

  • User ID not found
  • Add user to a project but the project ID is not found

422: Unprocessable entity

This is often used interchangeably with 400 as a generic error code as Unprocessable entity can mean both I can't read it and I can read but I can't understand it. Both these codes are generally acceptable as a generic error codes. However, I prefer the use case where this code is for formatting errors, such as sending an XML request body when you expect JSON and using 400 instead for generic errors.

5** - Server error

500: Internal server error

This is the code that the error handler sends when there is an unintentional error in your code. You can be very sure it is to do with a server-side coding error or config when throwing this error. The only place where you should return this is in the error handler of your back-end. Never explicitly throw this status code.

app.get('/user', (req, res) => {
    if (!validate(req.body)) {
        // Do not do this, intentionally send 500s
        return res.status(500).send({ error: 'Error with request body validation' });
    // ...

If there is an error case which you need to handle, look instead to:

  1. Use an appropriate 400s status code
  2. Throw an error and let the error handler take care of it when you are not sure how to handle it, or if you need to (edge cases)

Here is the right way to do it instead.

app.get('/user', async (req, res) => {
  if (!validate(req.body)) {
      // Appropriate error code
      return res.status(400).send({ error: 'Error with request body validation' });
  try {
    const result = await whatCouldPossibleGoWrongHere();
  } catch (e) {
    // Let the error handler deal with it, sends a 500
    throw new Error('I did not expect to reach here');

Wei-Ming Thor

I write guides on programming, mostly on how to be awesome at JavaScript.


I am a full-stack engineer who builds web and mobile apps.

Writing unmaintainable code since 2010.


Best: JavaScript, Python, C
Others: Android, iOS, React Native, Ruby, PHP


Kuala Lumpur, Malaysia