REST
02 / 04

HTTP Methods & Status Codes

HTTP Methods & Status Codes

Correct use of HTTP methods and status codes is the difference between a good API and a confusing one. Clients, caches, and frameworks all rely on these semantics to behave correctly.

Methods: Semantics, Safety, Idempotency

# Safe = does not modify server state (read-only)
# Idempotent = identical repeated requests produce the same result
#              (state is the same whether you call it once or ten times)

# Method     | Safe | Idempotent | Typical response
# GET        |  Yes |    Yes     | 200 OK
# HEAD       |  Yes |    Yes     | 200 OK (no body)
# OPTIONS    |  Yes |    Yes     | 200 OK
# POST       |  No  |    No      | 201 Created
# PUT        |  No  |    Yes     | 200 OK or 204 No Content
# PATCH      |  No  | Depends    | 200 OK or 204 No Content
# DELETE     |  No  |    Yes     | 204 No Content

# GET - retrieve
GET /products?category=shoes&sort=price&limit=20
# Always cacheable, never change state with GET

# POST - create (not idempotent - two identical POSTs = two resources)
POST /orders
# Request body: {"productId": 5, "qty": 2}
# Response: 201 Created + Location: /orders/99

# PUT - full replace (idempotent: same PUT = same result)
PUT /users/42
# Request body: complete user object
# If user 42 does not exist, some APIs create it (upsert)

# PATCH - partial update
PATCH /users/42
# Request body: only fields to update
# {"email": "new@example.com"}  ← only email changes
# Idempotent only if operation is absolute (not incremental like "increment by 1")

# DELETE - remove
DELETE /users/42
# Idempotent: deleting an already-deleted resource = 404 or 204 (both OK)
# Return 204 No Content on success (no body needed)

# Idempotency keys for POST (making POST idempotent)
POST /payments
Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000
# Server stores the key and returns the same response for retries

Complete Status Code Reference

# 2xx Success
200 OK               # GET/PATCH/PUT returned updated resource in body
201 Created          # POST created resource; include Location header
202 Accepted         # Async operation started (webhook/queue); return job ID
204 No Content       # DELETE/PUT succeeded; no body to return
206 Partial Content  # GET with Range header (video streaming)

# 3xx Redirection
301 Moved Permanently  # Resource URL changed forever; browser caches
302 Found              # Temporary redirect; use for login → dashboard
304 Not Modified       # Conditional GET; use cached copy
307 Temporary Redirect # Temp redirect preserving HTTP method
308 Permanent Redirect # Permanent redirect preserving HTTP method

# 4xx Client Errors (client did something wrong)
400 Bad Request        # Malformed JSON, missing required field, invalid param
401 Unauthorized       # Missing or invalid authentication credentials
                       # Include WWW-Authenticate: Bearer realm="api"
403 Forbidden          # Authenticated but not allowed to do this action
                       # Do NOT expose unauthorized resource existence
404 Not Found          # Resource with this ID does not exist
405 Method Not Allowed # Include Allow: GET, POST header
408 Request Timeout    # Client too slow; connection will be closed
409 Conflict           # State conflict: duplicate email, version mismatch, edit conflict
410 Gone               # Resource permanently deleted (stronger than 404)
412 Precondition Failed  # If-Match, If-Unmodified-Since check failed
415 Unsupported Media Type # Wrong Content-Type (sent XML, expected JSON)
422 Unprocessable Entity  # Valid JSON but fails business logic validation
429 Too Many Requests     # Rate limited; include Retry-After: 60 header

# 5xx Server Errors (server did something wrong)
500 Internal Server Error  # Catch-all for unexpected server errors
501 Not Implemented        # Method not supported by server
502 Bad Gateway            # Upstream server returned bad response
503 Service Unavailable    # Overloaded or maintenance; include Retry-After
504 Gateway Timeout        # Upstream server timed out

Consistent Error Response Format

// Consistent error response structure
// 400 Bad Request
{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Request validation failed",
    "details": [
      { "field": "email", "message": "Invalid email format" },
      { "field": "age", "message": "Must be between 18 and 120" }
    ],
    "requestId": "7f3a9b2c-1234-5678-abcd"
  }
}

// 401 Unauthorized
{
  "error": {
    "code": "UNAUTHORIZED",
    "message": "Authentication required",
    "requestId": "7f3a9b2c-1234-5678-abcd"
  }
}

// 429 Too Many Requests
{
  "error": {
    "code": "RATE_LIMIT_EXCEEDED",
    "message": "API rate limit exceeded. Try again in 60 seconds.",
    "retryAfter": 60,
    "limit": 100,
    "remaining": 0,
    "resetAt": "2025-03-15T10:01:00Z"
  }
}

Keep your own version of these notes — editable, searchable, and organised by your stack.

Start free