Use 404 Not Found for REST API GET Missing Resource
Return 404 Not Found for GET /api/{id} when the requested REST resource doesn't exist. Include a concise JSON error body. Use 200 for empty collections.
What is the correct HTTP status code for a REST API GET request when the resource does not exist, such as GET /api/users/{id} where no user is found?
Context
Building a REST API with Node.js and Express, following best practices:
app.get('/api/users/:id', async (req, res) => {
const user = await User.findById(req.params.id);
if (!user) {
// Which HTTP status code and response body?
}
res.json(user);
});
Options Considered
- 404 Not Found
- 204 No Content
- 200 OK with empty response body (e.g.,
{}or[])
Questions
- What is the REST-recommended status code for non-existent resources?
- Should the response include an error message or be empty?
- Does this differ for GET vs. POST or other methods?
Best practices and references appreciated.
For a rest api get request like GET /api/users/{id} where no user exists, return the http status code 404 Not Found. The 404 Not Found status tells clients the endpoint was reached but there’s no representation for that resource; include a concise JSON error body (for example {"error":"User not found"}). Don’t use 204 No Content or 200 OK for a missing single resource — 204 is for successful no-payload operations and 200 is appropriate for empty collections or queries.
Contents
- REST API GET: Use 404 Not Found for missing resource
- Why not 204 No Content or 200 OK for a missing resource?
- Response body for 404 Not Found: what to return
- Does this differ by HTTP method (GET vs DELETE vs POST vs PUT)?
- Express / Node example
- Best practices: caching, consistency and client handling
- Sources
- Conclusion
REST API GET: Use 404 Not Found for missing resource
When a client requests a specific resource by identifier (GET /api/users/{id}) and the server has no representation for that identifier, the REST-recommended status is 404 Not Found. RFC 7231 defines 404 as the server not finding “a current representation for the target resource,” which matches the situation where the route exists but the item does not (see the RFC text). The developer community and API-design discussions reach the same point: returning 404 makes the contract explicit — the requested resource is absent, not merely empty or suppressed — and helps clients handle the situation correctly (see community threads for examples).
The upshot: for lookups by id, return 404 and a small JSON error. That cleanly separates “resource not found” from other successful-but-empty results.
(Inline references: RFC 7231 defines 404 as described — https://httpwg.org/specs/rfc7231.html; community discussions on 404 vs 204: https://softwareengineering.stackexchange.com/questions/322951/should-i-return-a-204-or-a-404-response-when-a-resource-is-not-found and https://stackoverflow.com/questions/34312023/http-get-request-status-204-vs-404.)
Why not 204 No Content or 200 OK for a missing resource?
204 No Content means “the server successfully processed the request and is not returning any content.” That’s fine for methods where no body is expected (for example, many successful DELETE responses or an update that intentionally returns nothing). But 204 does not mean “resource missing” — it means “nothing to send back for a successful request.” RFC 7231 covers the semantics of 204; use it when the operation succeeded and no representation is needed.
200 OK with an empty body (or {} / []) is commonly used for collection endpoints or queries that return no items. For example, GET /api/users returning an empty list should normally return 200 OK with []. API Handyman explains this distinction and recommends returning a 2xx success for empty collections rather than 404 or 204: an empty collection is still a valid, existing resource representation (see https://apihandyman.io/empty-lists-http-status-code-200-vs-204-vs-404/).
Why not return 200/204 for GET /api/users/{id} when the user is missing? Because that response would mislead the client: 200 implies the resource exists (even if empty), and 204 implies success with no representation — neither expresses “this ID doesn’t map to any resource.” Use 404 to clearly express absence.
Response body for 404 Not Found: what to return
Always return a small, machine-readable error body with a 404. Minimal and practical shapes:
- Minimal:
{
"error": "User not found"
}
- More informative (recommended for production APIs):
{
"status": 404,
"error": "Not Found",
"message": "User with id 12345 not found",
"path": "/api/users/12345",
"timestamp": "2026-01-09T12:34:56Z"
}
Why include a body? Clients benefit from a consistent error schema for logging, UX, and programmatic handling. Keep it concise, avoid leaking internal details, and use consistent field names across endpoints. If you prefer a formal standard, many teams adopt the “problem details” format; otherwise a small JSON object with status, error/message, and optionally path is sufficient.
Note: RFC 7231 doesn’t mandate the error-body shape — it only defines status semantics — so pick and document a consistent format for your API.
Does this differ by HTTP method (GET vs DELETE vs POST vs PUT)?
Short mappings and practical choices:
- GET (single resource): 404 Not Found when the resource ID doesn’t exist.
- GET (collection/query): 200 OK with
[]if no items match; 404 is inappropriate for an existing collection that happens to be empty. See API Handyman for this pattern. - DELETE: two common approaches:
- Return 204 No Content when the delete succeeded (or when you want DELETE to be idempotent and treat “already gone” as success).
- Return 404 if you want to explicitly tell clients the target wasn’t present. Both approaches are used; pick one and stay consistent.
- (Community discussions illustrate the trade-offs — see https://devjava.substack.com/p/http-status-204-vs-404 and related threads.)
- POST: on successful creation return 201 Created with a Location header. If POST fails because a referenced resource in the payload doesn’t exist, return 400/422 or 404 depending on the contract (404 is reasonable when the payload references a non-existent resource by URL).
- PUT/PATCH: return 200/204 on successful update; return 404 if the resource to update does not exist (unless your API treats PUT as upsert).
So yes — semantics vary by method. The guiding idea: choose the status that communicates whether the resource exists and whether the operation succeeded.
Express / Node example
Practical Express snippet for your route:
app.get('/api/users/:id', async (req, res) => {
try {
const user = await User.findById(req.params.id);
if (!user) {
return res.status(404).json({ status: 404, error: 'Not Found', message: 'User not found' });
}
return res.json(user); // 200 OK with user representation
} catch (err) {
// handle DB errors, validation, etc.
console.error(err);
return res.status(500).json({ status: 500, error: 'Internal Server Error' });
}
});
Examples for other endpoints:
- Collection endpoint (empty list):
app.get('/api/users', async (req, res) => {
const users = await User.find(query);
// even if users.length === 0, return 200 with []
res.json(users);
});
- DELETE (idempotent success):
app.delete('/api/users/:id', async (req, res) => {
const deleted = await User.deleteOne({ _id: req.params.id });
// if you treat delete-of-missing-resource as success:
return res.status(204).end();
// or, if you prefer to notify client that it wasn't found:
// if (deleted.deletedCount === 0) res.status(404).json({ error: 'User not found' });
});
Best practices: caching, consistency and client handling
- Be consistent. Pick one error schema and reuse it across all endpoints. Clients rely on stable shapes.
- Use 404 for missing single resources and 200 for empty collections. That keeps intent clear.
- Consider caching implications: 404 responses are cacheable by default (RFC 7231) unless you set Cache-Control headers. If a missing resource may appear soon (e.g., eventual consistency), avoid caching 404 for long periods.
- Keep error bodies concise and non-sensitive. Include enough info for clients to present a helpful message or retry logic.
- Log missing-resource requests server-side — repeated 404s can indicate client bugs or abuse.
- Document your choices in the API spec so integrators know when to expect 404 vs 204 vs 200.
Sources
- RFC 7231 — Hypertext Transfer Protocol (HTTP/1.1): Semantics and Content: https://httpwg.org/specs/rfc7231.html
- API Handyman — Empty list, HTTP status code 200 vs 204 vs 404: https://apihandyman.io/empty-lists-http-status-code-200-vs-204-vs-404/
- Should I return a 204 or a 404 response when a resource is not found? (Software Engineering Stack Exchange): https://softwareengineering.stackexchange.com/questions/322951/should-i-return-a-204-or-a-404-response-when-a-resource-is-not-found
- HTTP GET Request Status 204 Vs 404 (Stack Overflow): https://stackoverflow.com/questions/34312023/http-get-request-status-204-vs-404
- HTTP Status 204 vs 404 (DevJava): https://devjava.substack.com/p/http-status-204-vs-404
- Community discussion: Restful API Get: 204 or 404 for nothing found? (Reddit): https://www.reddit.com/r/webdev/comments/5ve0x7/restful_api_get_204_or_404_for_nothing_found/
- Community discussion: What HTTP status code should I use for a REST API if the queried content does not exist? (Reddit): https://www.reddit.com/r/webdev/comments/zdhbxc/what_http_status_code_should_i_use_for_a_rest_api/
Conclusion
Return 404 Not Found for a REST API GET when the requested resource (GET /api/users/{id}) doesn’t exist, and include a concise JSON error body to make handling straightforward. Use 200 OK for empty collections and 204 No Content for successful no-payload operations (like many DELETE responses), and document your choices so clients know what to expect.