# HTTP methods and status codes that trip up backend interviews

Source: https://www.techinterview.org/post/3233476915/http-methods-and-status-codes-cheat-sheet/
Updated: 2026-07-30 · techinterview.org

The fastest way to lose points in an API design round isn't a shaky database schema. It's returning `200 OK` for a request that failed, or claiming `PATCH` is idempotent when it isn't. Interviewers reach for HTTP because it's a cheap proxy: the status code you pick and the method you choose tell them how carefully you reason about retries and failure.

The rest is grouped the way the questions come up, with enough reasoning behind each code to survive a follow-up.

## Safe and idempotent are not the same word

Two properties describe every method, and mixing them up is the most common miss on this topic.

*Safe* means the method doesn't change server state. It's read-only. `GET`, `HEAD`, and `OPTIONS` are safe, so a caching proxy or a search-engine crawler can hit them freely with no side effects. If a `GET` deletes something, someone has built a bug and a security hole in the same line of code.

*Idempotent* means calling the method once or five times leaves the server in the same state. Every safe method is idempotent, but the reverse doesn't hold. `DELETE` changes state, so it isn't safe, yet it is idempotent: delete a resource, then delete it again, and the end state is identical because the thing is already gone. The second call often returns `404`, and that's what trips people up. Idempotency is about the resulting state, not about getting the same response code every time.

`PUT` is idempotent because it replaces the whole resource with the payload you sent. Send it twice, you get the same stored object. `POST` is neither safe nor idempotent, which is why a double-submitted checkout form can charge a card twice.

`PATCH` is the one candidates get wrong. It's idempotent only when the change is absolute. Setting `{"status": "shipped"}` lands in the same place no matter how often you apply it. A relative change like an increment-view-count patch does not, so that `PATCH` is not idempotent, and a client retry after a dropped connection quietly inflates the number.

## The methods in one grid

| HTTP method | Safe (read-only) | Idempotent | Cacheable | Sends a body | What it's for |
| --- | --- | --- | --- | --- | --- |
| GET | Yes | Yes | Yes | No | Read a resource |
| HEAD | Yes | Yes | Yes | No | Read only the headers, no body |
| OPTIONS | Yes | Yes | No | No | Ask which methods and CORS rules apply |
| POST | No | No | Rarely | Yes | Create a resource or trigger a non-repeatable action |
| PUT | No | Yes | No | Yes | Replace a resource wholesale |
| PATCH | No | Sometimes | No | Yes | Apply a partial modification |
| DELETE | No | Yes | No | Optional | Remove a resource |

The cacheability column has a caveat worth saying out loud in an interview. A `POST` response can technically be cached when the server sends explicit freshness headers, but almost nobody relies on that, so treat it as a no in practice.

## Status codes by their first digit

The leading digit is the whole read for a client. `2xx` succeeded. `3xx` means go somewhere else to finish. `4xx` is the caller's fault. `5xx` is the server's fault. There's also `1xx`, informational and rarely seen, though `103 Early Hints` has started showing up in production to let a browser preload assets while the real response is still being assembled.

This grouping isn't trivia. A client library decides whether to retry based on the class. Retrying a `4xx` is usually pointless because the request itself is wrong. Retrying a `5xx` or a `429` with backoff is often correct. Get the class wrong and clients either hammer a struggling server or give up on a request that would have worked on the second try.

## Picking the right 2xx

`200 OK` is the default success. Reach past it when the extra precision carries information. `201 Created` says a new resource now exists, and you should return its address in a `Location` header so the client isn't left guessing the URL. `202 Accepted` means you took the request but haven't finished it, which is what you return for anything queued or processed in the background, like a video transcode or a bulk import. `204 No Content` says it worked and there's deliberately nothing in the body, common for a `DELETE` or a `PUT` that doesn't echo back the updated object.

## Redirects: 301, 302, 307, and 308

The confusion here is permanent versus temporary, crossed with whether the method survives the redirect. `301 Moved Permanently` tells browsers and search engines to update their links, and it gets cached aggressively, so a wrong `301` is painful to walk back. `302 Found` is temporary. The catch with both is that older clients would switch a `POST` to a `GET` when following them, which silently breaks form submissions. `307 Temporary Redirect` and `308 Permanent Redirect` exist to fix exactly that: they preserve the original method and body. If a redirect has to keep a `POST` a `POST`, reach for `307` or `308`.

`304 Not Modified` is the odd member of the family. It answers a conditional `GET`: the client sent an `If-None-Match` with an `ETag`, the resource hasn't changed, so the server skips the body and tells the client to use its cached copy. It's the backbone of HTTP caching and a natural follow-up once you mention `ETag`.

## The 4xx codes interviewers actually probe

This is where the real questions live, because choosing between two plausible `4xx` codes shows whether you understand the semantics or just memorized a list.

`400 Bad Request` is the catch-all for malformed input: broken JSON, a missing field the parser choked on. `401 Unauthorized` and `403 Forbidden` are the pair to get right. `401` means the server doesn't know who you are, so your credentials are missing or invalid and you should authenticate. `403` means it knows exactly who you are and you still can't have this. Some APIs deliberately return `404` instead of `403` so they don't reveal that a resource exists at all, a reasonable choice for private repos or paid tiers.

`405 Method Not Allowed` fires when the route exists but not for that verb, and the spec says you should list the valid ones in an `Allow` header. `409 Conflict` is the one to name when an interviewer asks about concurrent writes: two clients edit the same record, you version it with optimistic concurrency, and the write that arrives with a stale version loses and gets a `409`. `422 Unprocessable Entity` covers a request that parsed fine but fails a business rule, like an email that's syntactically valid but already registered. Whether you separate `400` from `422` is a genuine design debate, so pick a line and be ready to defend it.

`429 Too Many Requests` is rate limiting, and the detail that earns the point is the `Retry-After` header telling the client how long to back off. `410 Gone` is a firmer `404`: the resource existed and was intentionally removed, so stop asking for it.

The way these show up in a live round:

- "A client PUTs the same order twice because the first response timed out. What happens, and what should?"

- "Your endpoint returns 200 with an error message buried in the JSON body. Why is that a problem?"

- "When would you return 403 versus 404 for a resource the caller isn't allowed to see?"

- "A load balancer sits in front of your service and clients are seeing 502s. Where do you look first?"

## 5xx: 500, 502, 503, and 504 are different bugs

Lumping the server errors together wastes a chance to show you've operated real systems. `500 Internal Server Error` is the generic unhandled exception, your code threw and nobody caught it. `502 Bad Gateway` means a proxy or load balancer reached your app and got back something invalid, often because the upstream process crashed or returned garbage. `503 Service Unavailable` means the server is up but refusing work right now, overloaded or in maintenance, and it should carry a `Retry-After`. `504 Gateway Timeout` means the proxy waited for an upstream response that never arrived in time.

That last split answers the load-balancer question above. A `502` points at the app process itself, which crashed or sent a malformed reply. A `504` points at latency, so the app is alive but too slow, or a dependency it calls is dragging. Knowing which one you're staring at tells you whether to open crash logs or go hunt a slow query.

## Idempotency keys, the follow-up to POST

Once you've said `POST` isn't idempotent, a sharp interviewer pushes back: so how does a payment API let clients retry safely? The answer is an idempotency key. The client generates a unique value, usually a UUID, and sends it in an `Idempotency-Key` header. The server records the result against that key on first use and returns the same stored result for any repeat, so a charge retried after a network blip settles the account exactly once. Stripe popularized the pattern, and it's now standard for anything that moves money or creates records you can't afford to duplicate.

That's the thread running under all of it. The status code and the method aren't decoration on top of your logic. They're the contract a client reads to decide whether to retry, cache, redirect, or show an error to a user. Choose them as if the caller is a stranger who will do exactly what the numbers tell them, because a well-written client does.
