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 retriesComplete 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 outConsistent 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