Engineering Notes
Home/Backend/API Design/HTTP Methods and Status Codes
Backend · API Design

HTTP Methods and Status Codes

Pick the right method and the right status code, every time. Learn the safe and idempotent properties that clients and proxies rely on, then use the decision helper to choose a status code for any situation.

⚙️ Backend● Beginner⏱ 8 min read▶ 2 interactive🛠️ 2 exercises🎤 5 interview Q&As

🎯 What you'll learn

  • The HTTP methods and their meaning
  • Safe and idempotent: why they matter for retries and caching
  • Request/response examples per method
  • Status code categories and the codes every API needs

1The HTTP Methods

HTTP defines a fixed vocabulary of methods (also called "verbs"). Each one carries well-known semantics that clients, servers, proxies, and caches all agree on — this shared meaning is exactly what "self-descriptive messages" (from the REST Fundamentals topic) relies on.

MethodPurposeHas a request body?Typical success status
GETRetrieve a representation of a resourceNo200 OK
HEADSame as GET, but headers only, no bodyNo200 OK
POSTCreate a resource, or trigger a non-idempotent actionYes201 Created
PUTReplace a resource entirely with the given representationYes200 OK / 204 No Content
PATCHPartially modify a resourceYes200 OK
DELETERemove a resourceUsually no204 No Content
OPTIONSDiscover what methods/headers are allowed on a resourceNo204 No Content
TRACEDiagnostic loopback of the request (rarely used, often disabled)No200 OK
CONNECTEstablish a tunnel (used for HTTPS proxying)No200 OK

2Safe and Idempotent — the Two Properties That Matter Most

Two properties, formally defined by the HTTP specification (RFC 9110), determine how a method may be treated by caches, proxies, retry logic, and browsers:

  • Safe — the method does not (and is not expected to) change server state. A safe request can be issued speculatively (link prefetching, crawler indexing) without side effects.
  • Idempotent — issuing the same request N times has the same effect on server state as issuing it once. This is about effect, not about the response being identical every time.

The full Idempotency topic covers how servers actually implement idempotency guarantees for non-naturally-idempotent methods like POST. This topic only covers what the spec itself defines.

3Request/Response Examples per Method

http
POST /orders HTTP/1.1
Content-Type: application/json

{ "customerId": 42, "items": [{ "sku": "SKU-1", "qty": 1 }] }
http
HTTP/1.1 201 Created
Location: /orders/901
Content-Type: application/json

{ "id": 901, "status": "pending" }
http
PUT /orders/901 HTTP/1.1
Content-Type: application/json

{ "customerId": 42, "items": [{ "sku": "SKU-1", "qty": 3 }], "status": "pending" }
http
HTTP/1.1 200 OK
Content-Type: application/json

{ "id": 901, "customerId": 42, "items": [{ "sku": "SKU-1", "qty": 3 }], "status": "pending" }
http
PATCH /orders/901 HTTP/1.1
Content-Type: application/json

{ "status": "cancelled" }
http
HTTP/1.1 200 OK
Content-Type: application/json

{ "id": 901, "customerId": 42, "items": [{ "sku": "SKU-1", "qty": 3 }], "status": "cancelled" }
http
DELETE /orders/901 HTTP/1.1
http
HTTP/1.1 204 No Content

4Status Code Categories

RangeCategoryMeaning
1xxInformationalRequest received, processing continues (rarely touched directly by API code)
2xxSuccessThe request was received, understood, and accepted
3xxRedirectionFurther action needed to complete the request
4xxClient ErrorThe request has a problem the client must fix
5xxServer ErrorThe server failed to fulfill an otherwise valid request

5Status Codes Every API Developer Should Know

CodeNameWhen to use it
200OKGeneric success with a response body (GET, PUT, PATCH)
201CreatedA new resource was created (POST); include Location header
202AcceptedRequest accepted for async processing, not yet complete
204No ContentSuccess with no response body (DELETE, some PUT/OPTIONS)
304Not ModifiedConditional GET — cached representation is still valid
400Bad RequestMalformed request (bad JSON, invalid parameter type)
401UnauthorizedMissing or invalid authentication credentials
403ForbiddenAuthenticated, but not allowed to perform this action
404Not FoundNo resource at this URI
405Method Not AllowedResource exists, but doesn't support this method
409ConflictRequest conflicts with current server state (e.g. duplicate)
422Unprocessable EntityWell-formed request, but semantically invalid (failed validation)
429Too Many RequestsRate limit exceeded (see the Rate Limiting topic)
500Internal Server ErrorUnhandled server-side failure
503Service UnavailableServer temporarily can't handle the request (overload, maintenance)
Under the HoodWhat the Spec Actually Says About Safe/Idempotent▾

RFC 9110 (which obsoletes the older RFC 7231) defines safety and idempotency per method, and it's a fixed, precise table — not a guideline left to interpretation:

MethodSafeIdempotent
GET✅✅
HEAD✅✅
OPTIONS✅✅
TRACE✅✅
PUT❌✅
DELETE❌✅
POST❌❌
PATCH❌❌
CONNECT❌❌
Grid of the nine HTTP methods against the safe and idempotent properties defined by RFC 9110
Grid of the nine HTTP methods against the safe and idempotent properties defined by RFC 9110

Why GET/HEAD/OPTIONS/TRACE are safe: by definition, these methods are read-only requests to the server — the spec says a client "does not request, and does not expect, any state change on the origin server" as a result. This is precisely what lets browsers prefetch links, search engine crawlers index a site, and CDNs cache GET responses without any risk of accidentally deleting or mutating data. It's a contract — if your API performs a side effect (like a "click to unsubscribe" GET link, a classic real-world violation), you break every tool that assumes GET is free of consequences.

Why PUT and DELETE are idempotent but not safe: they change server state (not safe), but repeating the exact same request converges to the exact same end state. PUT /orders/901 with the same body twice leaves the order in the same final shape both times. DELETE /orders/901 twice leaves it deleted both times — the second call may return 404 instead of 204, but the server state (order 901 does not exist) is identical either way. The spec is explicit that the response can differ across idempotent calls; only the resulting state must match.

Why POST is neither safe nor idempotent by spec: POST's defining trait is "process the enclosed representation according to the resource's own semantics" — which could mean "create a new order" (each call creates a different new resource, so repeating it is absolutely not idempotent), or it could mean something else entirely defined by the API. Because the spec cannot know what a given POST endpoint does, it makes no idempotency guarantee. This is exactly the gap the Idempotency topic's Idempotency-Key header pattern exists to close for POST endpoints where safe retries matter (e.g. payments).

Why PATCH is neither safe nor idempotent by spec — even though it often behaves idempotently in practice: PATCH sends a set of changes to apply, not a full replacement. If the patch document says {"status": "cancelled"}, applying it twice is harmless (idempotent in practice). But if the patch document says something relative, like a JSON Patch {"op": "incr", "path": "/stock", "value": -1} (decrement stock by 1), applying it twice halves the stock twice — very much not idempotent. Because PATCH's payload format is open-ended, the spec can't guarantee idempotency the way it can for PUT's "replace wholesale" semantics.

Practical consequence for caches and proxies: intermediaries are allowed to automatically retry safe and idempotent methods on connection failure (a browser silently retrying a timed-out GET is safe to do); they must not automatically retry POST, because a lost response after a successful POST (rather than a lost request) would cause a duplicate resource to be created if blindly retried.

⚠️ Common Pitfalls

  1. Using GET with side effects — e.g. /unsubscribe?email=x performing the unsubscribe on GET. Breaks prefetching, crawler safety, and caching assumptions.
  2. Returning 200 OK for everything, including errors — putting the real status in the JSON body ({"success": false}) defeats HTTP-aware tooling (caches, monitoring, retries) that rely on the actual status line.
  3. Using POST for updates that should be PUT/PATCH — makes the API harder to reason about and loses the idempotency guarantee PUT would have given clients for free.
  4. Returning 204 No Content with a non-empty body — a 204 response must have no body by spec; some HTTP clients will error or silently drop it.
  5. Conflating 401 and 403 — 401 means "who are you? credentials missing/invalid," 403 means "I know who you are, and you're not allowed." Returning 403 for missing auth leaks less information in some designs, but mixing them up inconsistently confuses API consumers either way.
  6. Assuming PATCH is always idempotent — as shown in section 6, only PUT is idempotent by spec; PATCH depends entirely on the semantics of the patch document you define.

📋 Quick Reference

text
Safe + idempotent:      GET, HEAD, OPTIONS, TRACE
Idempotent, not safe:   PUT, DELETE
Neither:                POST, PATCH, CONNECT

201 Created      -> POST that created a resource (+ Location header)
204 No Content   -> success, no body (DELETE, sometimes PUT)
400 Bad Request  -> malformed syntax
401 Unauthorized -> missing/invalid credentials
403 Forbidden    -> authenticated but not allowed
404 Not Found    -> no such resource
409 Conflict     -> request conflicts with current state
422 Unprocessable Entity -> well-formed but semantically invalid
429 Too Many Requests    -> rate limited
5xx              -> server's fault, not the client's

🧠 Key takeaways

  • GET/HEAD/OPTIONS are safe; PUT/DELETE idempotent; POST/PATCH neither by default.
  • Safe → cacheable and prefetchable; idempotent → safe to retry automatically.
  • 2xx success, 3xx redirect, 4xx client error, 5xx server error. Never 200 with an error body.
  • Know 200, 201, 204, 400, 401, 403, 404, 409, 422, 429, 500, 503 cold.

🎤 Interview Q&A

Questions interviewers ask about this topic. Answer out loud first, then open a card to compare.

Q1When do you use PUT vs PATCH vs POST?core▾

POST: create a resource where the server assigns the ID, or trigger an action (not idempotent). PUT: replace a resource entirely at a known URI (idempotent; can create if client chooses the ID). PATCH: partial update (JSON Merge Patch or JSON Patch), not necessarily idempotent.

Follow-ups interviewers ask
  • Is PATCH idempotent? Merge patches setting fields are; operations like 'append' aren't.
Q2Which status codes should a create endpoint return?core▾

201 Created with a Location header (and usually the resource body); 202 Accepted if processing is asynchronous; 400/422 for validation errors; 409 Conflict for duplicates; 401/403 for auth issues.

Follow-ups interviewers ask
  • 400 vs 422? 400 for malformed requests; 422 for well-formed requests failing validation (conventions vary).
Q3What should DELETE return?core▾

204 No Content on success (or 200 with a body), and either 204 or 404 on repeated deletes; both keep the operation idempotent in effect. Use 409 if deletion is blocked by dependent data.

Follow-ups interviewers ask
  • Soft delete? Still return 204; the resource no longer appears to clients.
Q4When do you return 409 vs 412 vs 429?senior▾

409 Conflict: request conflicts with current state (duplicate, invalid state transition). 412 Precondition Failed: If-Match ETag didn't match (optimistic concurrency). 429 Too Many Requests: rate limited, with Retry-After.

Follow-ups interviewers ask
  • Which for 'order already shipped' on cancel? 409.
Q5Why is returning 200 with an error body a bad practice?core▾

Clients, proxies, monitoring and retries rely on status codes; hiding errors in 200 responses breaks caching, alerting and client error handling.

Follow-ups interviewers ask
  • Exception? GraphQL commonly returns 200 with an errors array by convention.

🛠️ Practice exercises

Each exercise is a file in this topic folder. Code exercises are self-checking: run them and make the checks pass.

✅ Check yourself

Answer the questions — feedback is instant.

1. Creating a resource should return…
Point to the new resource.
2. Which method can a proxy retry safely after a network error?
Idempotent.
3. Order can't be cancelled because it already shipped:
Conflicts with current state.
4. An upstream payment provider times out:
Gateway Timeout.