Skip to content

HTTP Status Codes in REST API Design — Complete Guide

DodaTech Updated 2026-06-28 4 min read

In this tutorial, you will learn about HTTP Status Codes in REST API ilink "Api Design" >}}. We cover key concepts, practical examples, and best practices to help you master this topic.

HTTP status codes are three-digit responses that indicate the result of a REST API request, grouped into five classes that tell clients whether their request succeeded, failed, or needs additional action.

flowchart TD
  A[HTTP Response] --> B[1xx Informational]
  A --> C[2xx Success]
  A --> D[3xx Redirection]
  A --> E[4xx Client Error]
  A --> F[5xx Server Error]
  C --> C1[200 OK]
  C --> C2[201 Created]
  C --> C3[204 No Content]
  E --> E1[400 Bad Request]
  E --> E2[401 Unauthorized]
  E --> E3[404 Not Found]
  style C fill:#c8e6c9
  style E fill:#ffcdd2
  style F fill:#ffcdd2

Choose the right status code so clients can handle responses without Parsing the body. A 200 OK means the request succeeded. A 201 Created means a new resource was created. A 204 No Content means success with no response body. A 400 Bad Request means the client sent invalid data. A 401 Unauthorized means authentication is needed. A 404 Not Found means the resource does not exist. A 500 Internal Server Error means something went wrong on the server.

Think of status codes like traffic lights. Green (2xx) means go ahead, the request succeeded. Yellow (3xx) means you need to take a different route. Red (4xx) means you made a mistake. Flashing red (5xx) means the traffic light itself is broken.

Example: 2xx Success Codes

import requests

# 200 OK - Standard success
get_response = requests.get("https://api.example.com/users/42")
print(f"GET: {get_response.status_code}")

# 201 Created - Resource created
post_response = requests.post(
    "https://api.example.com/users",
    json={"name": "New", "email": "new@example.com"}
)
print(f"POST: {post_response.status_code}")

# 204 No Content - Success, no body
delete_response = requests.delete("https://api.example.com/users/42")
print(f"DELETE: {delete_response.status_code}")

Expected output:

GET: 200
POST: 201
DELETE: 204

Example: 4xx Client Error Codes

import requests

# 400 Bad Request - Invalid input
bad_data = {"name": ""}
response = requests.post("https://api.example.com/users", json=bad_data)
print(f"400: {response.status_code} - {response.json()['error']}")

# 401 Unauthorized - No auth token
no_auth = requests.get("https://api.example.com/users/42")
print(f"401: {no_auth.status_code}")

# 404 Not Found - Non-existent resource
missing = requests.get("https://api.example.com/users/99999")
print(f"404: {missing.status_code}")

Expected output:

400: 400 - Name is required
401: 401
404: 404

Example: 5xx Server Error Codes

import requests

# Simulate a server error
try:
    response = requests.get("https://api.example.com/error-test")
    print(f"Status: {response.status_code}")
    response.raise_for_status()
except requests.exceptions.HTTPError as e:
    print(f"Server error: {e}")

Expected output:

Status: 500
Server error: 500 Server Error: Internal Server Error for url: https://api.example.com/error-test

Common Mistakes

  1. Returning 200 for all successful requests — Use 201 for creation and 204 for deletion. Different codes give clients meaningful information without parsing the body.
  2. Using 400 for authentication failures — Use 401 for missing or invalid authentication, 403 for authorized but not permitted. 400 means malformed syntax.
  3. Returning 500 for client errors — If the client sends bad data, return 400, not 500. 500 means the server failed, not the client.
  4. Not using 404 for deleted resources — After deletion, GET should return 404, not 410 Gone unless you want to distinguish between never-existed and deleted.
  5. Returning 201 for non-creation operations — Only return 201 when a resource was created. Returning 201 for a successful GET or UPDATE is incorrect.

Practice Questions

  1. What is the difference between 401 Unauthorized and 403 Forbidden?
  2. When should you return 204 No Content?
  3. What status code indicates the server received malformed input?
  4. Should you use 404 or 410 for a deleted resource?
  5. Challenge: Write a Python script that handles all common HTTP status codes with appropriate error messages and retry logic for 5xx errors.

FAQ

What is the difference between 400 and 422?

400 Bad Request means malformed syntax. 422 Unprocessable Entity means the syntax is fine but the content is invalid (like a negative price). Use 422 for validation errors.

When should I use 202 Accepted?

Use 202 when the request is accepted but processing is not complete. This is common for async operations where the server queues the request.

Should I expose 500 errors to clients?

Return 500 but do not include stack traces or internal details. Log the full error server-side and return a generic message.

What is 429 Too Many Requests?

429 indicates rate limiting. The client has sent too many requests in a given time window. Include a Retry-After header.

How do I handle 3xx redirects in API clients?

Most HTTP clients follow redirects automatically. For APIs, avoid 3xx codes if possible. Use 201 for creation with a Location header instead of 303.

Mini Project

Create a Python function that takes an HTTP response and logs an appropriate message based on the status code range. Test it by making requests to various endpoints and verifying the handler produces correct messages for 2xx, 3xx, 4xx, and 5xx responses.

What's Next

Now learn about content negotiation in REST API Design.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro