When should an API return 204 No Content?
Prompt and scope
Design an empty-response contract for a REST API. How should a successful delete respond? Should a collection query with no matches return 204? What should a successful update return when no representation is needed? Separate a missing resource, a successful operation with no representation, an asynchronous operation, and a valid empty collection. Assume generated clients in several languages and a long-lived contract.
What the interviewer is testing
Treat status codes as resource semantics, not a shortcut for “there is no data.” 204 means success with no message content and must not carry a message body; 200 can return a stable representation such as []; 404 means the target resource is absent or has no current representation. Discuss DELETE idempotency, caches, SDK decoding, and OpenAPI documentation.
Clarifications before answering
- What is the target? Deleting one resource, updating one resource, and querying a collection have different semantics.
- Is an empty collection a normal result? If yes, 200 with
[]usually preserves a stable response type better than 204. - Must clients decode one JSON shape? A generated client that always reads a body may turn 204 into an unexpected EOF unless it has an explicit branch.
- Does success require a new representation, ETag, or asynchronous job ID? If so, keep a response body and choose 200, 201, or 202.
Recommended decision and derivation
Define the contract by operation and representation need:
- A successful
DELETE /users/42with no representation to return can use 204. If repeated delete is defined as idempotent success, it may also remain 204, but document it. - If
GET /users?team=nonefinds an existing collection with no members, return 200 and[]to preserve the list type; zero rows are not a missing resource. - If
GET /users/42cannot find the target, return 404. That is target-resource semantics, not empty-list semantics. - If
PUT /users/42succeeds and the client needs the new representation, return 200 with JSON. If no representation is needed, 204 is valid and ETag can still carry metadata. - If the request is accepted but work continues, return 202 with a task-status link instead of disguising it as 204.
HTTP/1.1 204 No Content
ETag: "user-42-v7"
Cache-Control: no-store
HTTP/1.1 200 OK
Content-Type: application/json
[]RFC 9110 defines 204 as having no message content, so clients, proxies, and tests should treat an absent body as part of the contract. Do not put business errors inside a successful 200 just for uniformity, and do not turn every empty result into 204 to save a few bytes.
Alternatives and trade-offs
An empty-array 200 keeps types stable, is easy for generated SDKs, and can carry pagination metadata; it costs a few bytes. 204 clearly expresses success without a representation, fitting DELETE or an update that does not echo data; clients must handle an absent body and cannot read error details there. Reserve 404 for a missing target resource rather than a valid empty collection.
Failure modes, boundaries, and counterexamples
- Returning 204 for an empty
GETlist makes clients treat a valid empty result as a different response type, breaking pagination and generic decoding. - Sending a JSON body with 204 violates its message semantics; proxies may discard it and clients will diverge.
- Returning 204 on the first DELETE and 404 on a retry without documenting idempotency creates avoidable retry errors.
- Returning 200 with
{ "error": ... }makes monitoring and SDKs classify a business failure as success. - Returning 204 after an update that needs a new ETag but omitting the response header prevents safe caching or concurrency control.
Tests and verification checklist
Write contract tests for status, body, Content-Type, ETag, and cache headers on every endpoint. Cover first and repeated DELETE, empty collections, missing single resources, updates with and without representations, the 202 async branch, proxy forwarding, and SDK decoding. Generate at least one client from OpenAPI and verify that 204 does not trigger JSON parsing errors; check that monitoring separates 2xx, 404, and structured business errors.
Follow-up questions
Can 204 carry an ETag or other response headers?
Yes. Prohibiting message content does not prohibit metadata. ETag, cache controls, or a trace ID can support concurrency control and diagnosis, but document when they are present.
Should an empty page be 200 or 204?
If the representation is a list, prefer 200 with an empty array and pagination metadata. Consider 204 only when “success with no representation” is explicit and every client handles an absent body.
Must DELETE return 404 when the resource is missing?
Not always. If deletion means “ensure the resource is absent,” repeated requests can return 204. If callers need to know whether it existed, return 404. Record the choice consistently in docs, SDKs, and monitoring.