Use each status class correctly, pick specific codes for common API outcomes, distinguish client errors from server errors for retries and monitoring, handle redirects and conditional responses, and implement consistent status handling in Spring Boot
A status code is a three-digit number in every HTTP response that tells the client (and intermediaries) the outcome of the request. The first digit defines the class.
Figure 1 — Status code classes
TOC class
Meaning
Client behaviour
2xx
Success
Use the response
3xx
Redirection / cached copy
Follow Location or use cache
4xx
Client error
Fix the request; don't retry unchanged (except 408/429 after waiting)
5xx
Server error
Retry idempotent requests with backoff; alert operators
Table 1 — Status classes
2Why It Exists
Generic clients and infrastructure need a machine-readable outcome independent of the body format. Status codes let a retry library, a CDN, a load balancer and a monitoring system make correct decisions without parsing application payloads.
Consumer
Uses status to…
Client SDK
Throw typed exceptions, decide retries
CDN / cache
Cache 200/301/404 as configured; never cache 5xx long
Load balancer
Mark instances unhealthy on 5xx rates
Monitoring/SLOs
Count 5xx as errors, 4xx as client issues
Browser
Follow redirects, show auth prompts
Table 2 — Who reads status codes
3Core Concepts
3.1 2xx
Figure 2 — 2xx success codes
Situation
Code
GET found
200 + body
POST created
201 + Location (+ body)
Async processing started
202 + status resource Location
DELETE done / PUT without echo
204
Range download
206
Table 3 — Choosing 2xx
3.2 3xx
Figure 3 — 3xx redirection codes
Situation
Code
Resource moved permanently (API path change)
308 (preserves method)
After POST, see result elsewhere
303
Conditional GET, unchanged
304 (no body)
Temporary routing
307
Table 4 — Choosing 3xx
API clients often don't follow redirects for unsafe methods — use 308/307 and document them; prefer stable URIs.
3.3 4xx
Figure 4 — 4xx client error codes
Common confusion
Guidance
401 vs 403
401 = not authenticated (send WWW-Authenticate); 403 = authenticated but not allowed
400 vs 422
400 = malformed (bad JSON, wrong types); 422 = well-formed but violates business rules
404 vs 403 for others' resources
404 avoids revealing existence (multi-tenant)
409 vs 412
409 = conflicts with current state; 412 = client precondition (ETag) failed
429
Rate limit; include Retry-After
Table 5 — 4xx decisions
3.4 5xx
Figure 5 — 5xx server error codes
Rule
Reason
Never expose stack traces
Security; use correlation ids
503 + Retry-After for overload/maintenance
Clients back off correctly
502/504 from gateways
Upstream problems — outcome may be unknown
Don't map business errors to 500
Pollutes error budgets and alerts
Table 6 — 5xx practice
4How It Works Internally — Status Handling in Spring
A legacy API returned 200 with {"success": false, "errorCode": "E102"} for all errors. The gateway cached error responses, retry libraries never retried outages, and dashboards showed 100% success during incidents. Introducing proper status codes (with a transition period and client SDK update) restored caching, retries and alerting.
6.2 Validation errors as 500
Unhandled ConstraintViolationExceptions produced 500s. The error budget burned on client typos and the on-call engineer was paged nightly. A @RestControllerAdvice mapping validation to 400/422 fixed alerts overnight.
Table 13 — Authentication vs authorisation vs visibility
6.7 Worked example — 429 with Retry-After
HTTP
HTTP/1.1 429 Too Many RequestsRetry-After: 30RateLimit-Policy: 100;w=60RateLimit: limit=100, remaining=0, reset=30Content-Type: application/problem+json{"type":"https://api.example.com/problems/rate-limit", "title":"Too many requests","status":429}
Clients (and SDKs) back off precisely instead of hammering.
6.8 Worked example — gateway-generated codes
Code
Generated by
Meaning for API teams
502
Gateway/LB
Upstream returned invalid response or closed connection
503
Gateway
No healthy upstream / circuit open
504
Gateway
Upstream too slow (gateway timeout < service timeout?)
413
Gateway
Payload too large
Table 14 — Not every status comes from your code
Align timeouts: gateway timeout > service timeout > downstream timeouts, to avoid 504s for work that still completes.
6.9 Worked example — monitoring by status class
Metric
Alert
5xx rate > 1% for 5 min
Page on-call
4xx rate spike on one client
Notify client team (likely client bug)
429 rate
Capacity/quotas review
401 spike
Token issuer/clock issues
Table 15 — Status classes power observability
6.10 Worked example — 410 Gone for retired resources
Situation
Response
Endpoint version retired
410 + link to migration guide
Temporary unavailability
503 + Retry-After
Resource deleted permanently and known
410 (optional; 404 acceptable)
Table 16 — Communicate permanence
6.11 Worked example — client-side handling with RestClient
Align gateway, service and client behaviour on retries and timeouts.
Use status codes as the basis of error budgets and SLOs.
10Interview Questions & Answers
Q1BasicConcept
What does each status class mean?
The first digit sets the class, so even a client that does not know a specific code can react correctly. 1xx is informational, an interim response such as 100 Continue or 103 Early Hints before the final one. 2xx means success: use the response. 3xx means redirection or use a cached copy: follow Location or reuse what you have, as with 304. 4xx means the client made a mistake, so fix the request instead of retrying it unchanged, apart from 408 and 429 after waiting. 5xx means the server failed, so idempotent requests may be retried with backoff and operators should be alerted.
Q2BasicConcept
Which status should a successful POST that creates a resource return?
Return 201 Created with a Location header holding the URI of the new resource, for example /api/orders/5001. Most APIs also return a representation of the created resource in the body, so the client gets generated fields such as the id, timestamps and status without a second GET; adding an ETag lets the client start conditional updates straight away. In Spring, ResponseEntity.created(uri).body(view) does all of this, and ServletUriComponentsBuilder can build the URI from the current request. Use 200 only when the POST did not create a resource, and 202 when creation will happen asynchronously. Combine creation with an idempotency key so retries do not create duplicates.
Q3IntermediateTricky
What is the difference between 401 and 403?
401 Unauthorized actually means unauthenticated: the request has no valid credentials, for example a missing, expired or badly signed token, and the response must include a WWW-Authenticate header such as Bearer error="invalid_token" telling the client how to authenticate. Retrying after logging in or refreshing the token can succeed. 403 Forbidden means the server knows who the caller is but that identity is not allowed to do this, for example a token without the orders:write scope; re-authenticating will not help. For resources belonging to another tenant, many APIs return 404 instead of 403 so they do not reveal that the resource exists. Spring Security produces 401 and 403 through separate handlers.
Q4IntermediateScenario
When would you return 202?
Return 202 Accepted when the server has validated and queued the request but the actual work will finish later, such as report generation, bulk imports, video processing or a payment awaiting a slow provider. The response should not pretend the work is done. Include a Location header pointing to a status resource like /api/report-jobs/77, optionally a Retry-After hint, and a body with the job id and state. The client polls that resource, which returns RUNNING with progress and then DONE with a link to the result, or redirects with 303. This keeps HTTP connections short, avoids gateway 504 timeouts and fits naturally with queue-based processing behind the API.
Q5Senior EngineerTrade-off
When should you use 409 vs 412?
409 Conflict means the request conflicts with the current state of the resource according to business rules, regardless of what the client sent as preconditions: cancelling an order that has already shipped, or creating a username that is taken. 412 Precondition Failed is narrower: the client sent a conditional header such as If-Match with an ETag, and the current version no longer matches, which is optimistic concurrency at the HTTP level. A 412 tells the client to reload the resource, merge and retry; a 409 tells it the operation is not allowed in this state. If you require preconditions on updates, return 428 when the client omits them.
Q6Senior EngineerTricky
Why not return 200 with an error body?
Because everything outside your application code reads the status line, not your JSON. With 200 for errors, CDNs and gateways cache failures as if they were valid content, retry libraries never retry real outages, circuit breakers never open, and SLO dashboards report 100 percent success during an incident. Client SDKs cannot throw typed exceptions, so every caller must parse a custom error flag and some will forget, silently treating failures as success. Browser and HTTP tooling behaviour breaks too. Use proper 4xx and 5xx codes with an RFC 9457 Problem Details body for the detail. Migrating a legacy always-200 API needs a transition period and updated client SDKs.
Q7Technical ArchitectDesign
How do status codes relate to retry policies?
Status codes are the input to retry classification. Most 4xx responses, such as 400, 403, 404, 409 and 422, mean the request itself is wrong and will fail again, so never retry them unchanged; 401 can succeed only after re-authenticating. 408, 429 and 503 mean try later, so wait for Retry-After before retrying. 500, 502 and 504 and connection errors may be transient, so retry idempotent requests with exponential backoff and jitter and a small attempt limit. Non-idempotent POSTs that time out have an unknown outcome and need idempotency keys before retrying. Encode this once in Resilience4j or the client SDK so every team behaves the same.
Q8Solution ArchitectDesign
How would you standardise status codes across hundreds of APIs?
Standardise by making the right thing the default and the wrong thing visible. Publish an organisation-wide status catalogue in the API guidelines that maps common situations, validation, not found, conflict, stale ETag, rate limit, to specific codes with RFC 9457 Problem Details bodies. Ship it as a shared Spring Boot starter with a @RestControllerAdvice and exception types so services map errors identically. Lint OpenAPI specs in CI with Spectral rules, for example requiring 201 plus Location on creation and documented 4xx responses. Back it with contract tests and dashboards by status class per API, and align gateway behaviour, timeouts and SDK retry policies with the same catalogue.
Newsletter
Get new tutorials every week
New ArchitectMind tutorials and interview questions, once a week. No spam, unsubscribe anytime.
Your subscription could not be saved. Please try again.
Almost done! Check your inbox and click the confirmation link to complete your subscription.