Keyboard Shortcuts N Next post
P Previous post
S Save / unsave
R Read aloud
T Toggle theme
/ Focus search
Esc Close panels
🔥
Ready to read...
API design APIs APIs From Zero to Real-World API Testing & Automation errors Module 3 — REST APIs REST API

REST Error Handling — Status Codes and Error Response Formats

Reviewed & accurate
AI Summary

What You'll Learn

  • Which status code to return for each error scenario
  • How to structure error response bodies
  • The difference between 400, 401, 403, 404, 409, 422, 429
  • The RFC 7807 "Problem Details" standard

Why This Matters

Bad error messages waste hours. A response of 404 Not Found with no body leaves the developer wondering: wrong URL? wrong ID? missing auth? Good error responses tell the developer exactly what went wrong and how to fix it. This lesson shows you how.

Status Codes for Common Scenarios

ScenarioCodeName
Resource created201Created
Successful read/update200OK
Successful delete204No Content
Async job accepted202Accepted
Malformed JSON400Bad Request
Missing auth token401Unauthorized
Valid token, no permission403Forbidden
Resource does not exist404Not Found
Method not allowed on this URL405Method Not Allowed
Conflict (duplicate email)409Conflict
Wrong Content-Type415Unsupported Media Type
Valid JSON, validation failed422Unprocessable Entity
Rate limit hit429Too Many Requests
Server crash500Internal Server Error
Database down503Service Unavailable
Upstream timeout504Gateway Timeout

The Anatomy of a Good Error Response

Just returning a status code is not enough. The body should explain what went wrong and how to fix it. A good error response has:

  1. A stable error code or slug (e.g., VALIDATION_FAILED)
  2. A human-readable message
  3. Optional details (which field failed, what was wrong)
  4. Optional documentation URL
  5. Optional request ID for support

Example — Validation error

HTTP/1.1 422 Unprocessable Entity
Content-Type: application/json

{
  "error": {
    "code": "VALIDATION_FAILED",
    "message": "The request body failed validation.",
    "details": [
      {
        "field": "email",
        "issue": "must be a valid email address",
        "value": "anita@"
      },
      {
        "field": "age",
        "issue": "must be greater than 0",
        "value": -5
      }
    ],
    "documentation_url": "https://docs.example.com/api/errors/validation",
    "request_id": "req_abc123"
  }
}

Example — Authentication error

HTTP/1.1 401 Unauthorized
Content-Type: application/json
WWW-Authenticate: Bearer

{
  "error": {
    "code": "MISSING_TOKEN",
    "message": "Authentication required. Send a Bearer token in the Authorization header.",
    "documentation_url": "https://docs.example.com/api/auth"
  }
}

Example — Conflict (duplicate)

HTTP/1.1 409 Conflict
Content-Type: application/json

{
  "error": {
    "code": "DUPLICATE_EMAIL",
    "message": "A user with this email already exists.",
    "resource": "User",
    "conflicting_field": "email",
    "value": "anita@example.com"
  }
}

Example — Rate limited

HTTP/1.1 429 Too Many Requests
Content-Type: application/json
Retry-After: 60
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1692820860

{
  "error": {
    "code": "RATE_LIMIT_EXCEEDED",
    "message": "Too many requests. Try again in 60 seconds."
  }
}

The RFC 7807 "Problem Details" Standard

RFC 7807 defines a standard JSON format for HTTP errors. It uses these fields:

  • type — a URL to documentation about the error
  • title — a short human-readable description
  • status — the HTTP status code
  • detail — a longer explanation
  • instance — a URL identifying this specific occurrence

Example

HTTP/1.1 400 Bad Request
Content-Type: application/problem+json

{
  "type": "https://docs.example.com/errors/validation",
  "title": "Invalid request",
  "status": 400,
  "detail": "The 'email' field is required.",
  "instance": "/users/failed-validation-req-abc"
}

Using RFC 7807 gives your API a consistent error format that tools and clients can recognize.

What to Put in the Body for 5xx Errors

For 5xx (server errors), do not leak stack traces or internal details. Return a generic message and a request ID so support can find the issue in logs:

HTTP/1.1 500 Internal Server Error
Content-Type: application/json

{
  "error": {
    "code": "INTERNAL_ERROR",
    "message": "Something went wrong on our end. Please try again.",
    "request_id": "req_abc123",
    "support_url": "https://support.example.com"
  }
}

Common Mistakes

  1. Returning 200 with an error in the body. Some APIs do this — it breaks HTTP clients that check the status code. Always return a proper 4xx or 5xx.
  2. Using 401 for permission errors. 401 = "I don't know who you are". 403 = "I know you, but you can't do this".
  3. Using 404 to hide authorization failures. Some APIs return 404 instead of 403 to avoid leaking that a resource exists. This is a deliberate security choice but should be documented.
  4. Generic error messages. "Bad request" with no detail is useless. Tell the developer which field failed and why.
  5. Leaking stack traces in 500 errors. Internal details should never reach the client. Log them server-side; return a generic message with a request ID.
  6. Inconsistent error formats. One endpoint returns {"error": "..."}, another returns {"message": "..."}. Pick one format and use it everywhere.

Practical Exercise (10 minutes)

For each scenario, write the correct status code and a sample error response body:

  1. User sends a POST with {"email": "not-an-email"}.
  2. User sends a request without an auth token.
  3. User sends a valid token but tries to access another user's data.
  4. User requests /users/999999 but no such user exists.
  5. User sends a POST with {"email": "existing@example.com"} and that email is already registered.
  6. User hits the API 1000 times in 10 seconds.
  7. Database crashes mid-request.

Mini Challenge

Design the error response format for a new API. Decide: do you use RFC 7807 or a custom format? What fields does every error have? What does the documentation URL look like? Write three example error responses covering a 400, a 401, and a 500.

Key Takeaways

  • Use the right status code: 201 created, 204 deleted, 400 malformed, 401 missing auth, 403 forbidden, 404 not found, 409 conflict, 422 validation, 429 rate limited, 500 server error.
  • Every error response should have: a stable error code, a message, optional field-level details, and a documentation URL.
  • RFC 7807 "Problem Details" is a standardized error format worth adopting.
  • Never return 200 with an error body. Never leak stack traces. Never use 401 for permission errors.
  • Be consistent: one error format across the entire API.
Course continuity
Previously: Lesson 20 covered versioning and idempotency.
Today: You learned how to design error responses that help developers fix problems.
Next: Module 3 is complete. Module 4 begins with lesson 22 — How to Call an API from the Browser.

FAQ

Should I use 400 or 422 for validation errors?

Use 400 when the JSON itself is malformed (missing brace, wrong quotes). Use 422 when the JSON is valid but the values fail business rules (invalid email, age negative). Some APIs use 400 for both, but 422 is more precise.

What should I do for "not found" when the user is not authenticated?

Return 401, not 404. The user needs to authenticate before you can tell them whether the resource exists. After authentication, return 404 if it does not exist or 403 if it exists but they cannot access it.

Test Your Knowledge
How did you find this?

Comments

Join the discussion! Sign in with your Google or Blogger account, or comment as Anonymous - no account needed. For quick questions, also reach me on Telegram @cytestch.

Comments