The five categories
HTTP codes are grouped by their first digit. 1xx are informational (rare in practice). 2xx indicate success. 3xx are redirects. 4xx are client errors: the request is wrong or the resource doesn't exist. 5xx are server errors: the client did everything right but something failed on the other side.
2xx: success with nuance
200 OK is the general "all good". 201 Created is used
after creating a resource (successful POST) and should include a Location
header pointing to the new resource. 202 Accepted means the request was
received and will be processed async. 204 No Content is the right
response when there's no body (typical for successful DELETE or PUT without return).
3xx: redirects
301 Moved Permanently means the resource changed URL forever; search engines update the index. 302 Found and 307 Temporary Redirect are temporary (the difference: 307 forces preserving the HTTP method). 304 Not Modified is returned when the client has a valid cached copy and no body retransmission is needed.
4xx: client errors
The most common: 400 Bad Request (malformed), 401 Unauthorized (missing or invalid credentials), 403 Forbidden (authenticated but no permission), 404 Not Found (resource missing), 409 Conflict (current state incompatible with the request, typical in concurrency), 422 Unprocessable Entity (field validation), 429 Too Many Requests (rate limit exceeded).
5xx: server errors
500 Internal Server Error is the catch-all: something failed and it's not
the client's fault. 502 Bad Gateway means an upstream returned an error.
503 Service Unavailable is maintenance or overload (send
Retry-After). 504 Gateway Timeout: upstream didn't respond
in time.
401 vs 403: the classic confusion
The distinction is: 401 = "I don't know who you are"; 403 = "I know who you are but you
can't". If a logged-in user tries to hit the admin panel without admin rights, return 403.
If the token expired, return 401 with a WWW-Authenticate header.
422 vs 400
400 is for syntax errors: invalid JSON, missing header. 422 (semantic) is when JSON parses fine but data violates rules: invalid email, negative age, required field empty. Pair it with a body listing per-field errors.
Idempotency and retries
GET, PUT and DELETE are idempotent: repeating them doesn't change state. POST isn't. This
matters when the client retries after a 5xx: with PUT you can repeat safely, with POST
you might create duplicates. Current practice is to use the
Idempotency-Key header on POST to guarantee no duplication.