HTTP 404 Not Found: Complete Guide for REST APIs
When to return 404, how to format the response, and how it differs from 403, 410, and other 4xx codes
What is HTTP 404 Not Found?
HTTP 404 Not Found is a client error response status code indicating that the server cannot find the requested resource. Defined in RFC 9110, 404 is one of the most common HTTP status codes and is part of the 4xx Client Error class — meaning the request itself is the problem, not the server.
Important distinction: a 404 does not mean the resource never existed. It means the resource cannot be found at the requested URL at this time. If a resource existed and was permanently removed, the correct code is 410 Gone. Many APIs return 404 for deleted resources as an acceptable simplification, but 410 provides more accurate semantics.
The 404 response says: "I understood your request, I looked for what you asked for, and I couldn't find it."
When Should Your REST API Return 404?
Use this decision tree to determine the correct status code:
| Situation | Correct Code | Reason |
|---|---|---|
Resource ID does not exist (e.g., GET /users/99999 and user 99999 doesn't exist) |
404 | Resource not found |
| Valid URL path, resource not found | 404 | Resource not found |
| URL path itself doesn't exist (no matching route) | 404 | Endpoint not found |
| Resource was deleted and is permanently gone | 410 Gone (preferred) or 404 (acceptable) | 410 communicates permanent removal more clearly |
| Resource exists but the caller doesn't have permission | 403 Forbidden (not 404) | The resource exists — this is an authorization error |
| Resource exists but the caller is not authenticated | 401 Unauthorized (not 404) | Authentication is required before checking resource existence |
List endpoint returns empty results (e.g., GET /users?status=inactive returns []) |
200 OK with empty array (not 404) | An empty list is a valid successful response |
Security Note: Intentional 404 for Authorization
Some APIs deliberately return 404 instead of 403 for resources the caller cannot access. The reason: returning 403 confirms the resource exists, which can leak information (for example, confirming that a private user ID exists in the system). Returning 404 in both "not found" and "no permission" cases prevents information disclosure. If your API uses this pattern, document it explicitly so API consumers understand the behavior.
404 Response Body Best Practices
Never return an empty body with a 404 status code. Clients need context to handle the error correctly. Always return a structured JSON body:
// Standard 404 response — always return a JSON body:
HTTP/1.1 404 Not Found
Content-Type: application/json
{
"error": {
"code": "RESOURCE_NOT_FOUND",
"message": "User with ID 12345 was not found.",
"resource": "users",
"id": "12345"
}
}
What to Include
code: A machine-readable string constant. Clients use this to handle specific error types programmatically. Use resource-specific codes likeUSER_NOT_FOUND,ORDER_NOT_FOUNDrather than the genericRESOURCE_NOT_FOUNDwhen possible — it improves debuggability.message: A human-readable description that identifies what was being looked for. Include the resource type and the ID that was searched for: "User with ID 12345 was not found."resourceandid(optional): Structured fields for programmatic access to the resource type and requested ID.
What Not to Include
- Database error messages or query details
- Stack traces or internal implementation details
- Information that reveals whether a resource ever existed (if you want to prevent information disclosure)
- Suggestions about which IDs do exist
404 vs 410: What's the Difference?
| Status Code | Meaning | Use When | SEO Implication |
|---|---|---|---|
| 404 Not Found | Resource cannot be found; may or may not exist in the future | Resource ID doesn't exist; unknown whether it ever did | Search engines continue checking the URL periodically — slower deindexing |
| 410 Gone | Resource existed and has been permanently removed; will not return | Resource was deliberately deleted and will not be replaced | Search engines deindex 410 pages faster — preferred when content is intentionally removed |
When 410 matters: If your API serves publicly indexed content (blog posts, product pages, user profiles) and you permanently remove content, returning 410 instead of 404 signals to search engines that the content has been intentionally removed. Google and other search engines treat 410 as a stronger signal to remove the URL from their index, whereas 404 may result in continued recrawling for weeks or months.
Practical implementation: Most REST API frameworks make 404 the default for "not found." Adding 410 support requires tracking deleted resource IDs so the server can distinguish "never existed" (404) from "existed and was deleted" (410). This is worth implementing for content-oriented APIs and publicly crawled URLs.
404 vs Other 4xx Status Codes
| Status Code | Name | Situation | Example |
|---|---|---|---|
| 400 | Bad Request | Request is malformed or fails validation | Missing required field, invalid date format |
| 401 | Unauthorized | Request is not authenticated | No token provided, expired token, invalid token |
| 403 | Forbidden | Authenticated but not authorized to access this resource | User trying to access another user's data; non-admin accessing admin endpoint |
| 404 | Not Found | Resource doesn't exist at the requested URL | GET /users/99999 when user 99999 doesn't exist |
| 410 | Gone | Resource existed and was permanently removed | GET /posts/123 where post 123 was deleted |
Handling 404 on the Client Side
Client applications should handle 404 responses deliberately, not treat them as generic errors:
- Check the status code before parsing the body. A 404 response body is an error object, not the resource you expected. Always check
response.statusbefore attempting to readresponse.body.data. - Show a user-friendly "not found" message. "The user you're looking for doesn't exist" is better than a generic error message or a blank page.
- Suggest alternatives. Offer navigation options: go back, search, or return to the home page. Remove or disable any links or buttons that reference the missing resource.
- Do not retry 404s automatically. Unlike 503 (Service Unavailable) or 429 (Rate Limited), a 404 will not resolve by retrying. Automatic retry loops on 404s waste resources and degrade user experience.
- Log unexpected 404s. A 404 from a URL your application constructed (not user input) indicates a bug — a broken internal link, stale reference, or deleted resource that wasn't cleaned up. Log these with context (which page, which resource ID) to identify and fix the underlying issue.
- Distinguish "user error" from "application error." If a user typed an ID into a URL bar and got 404, that's expected. If your application navigated to a URL and received 404, that's a bug worth investigating.
Real-World 404 Examples in REST APIs
Two complete request/response examples showing how 404s should look in practice:
Example 1: User Not Found
GET /api/v1/users/99999
Authorization: Bearer eyJhbGciOiJIUzI1NiJ9...
HTTP/1.1 404 Not Found
Content-Type: application/json
X-Request-ID: req_7f3a9b2c
{
"error": {
"code": "USER_NOT_FOUND",
"message": "No user found with ID 99999",
"resource": "users",
"id": "99999"
}
}
Example 2: Nested Resource Not Found
GET /api/v1/orders/abc-123/items/xyz-456
Authorization: Bearer eyJhbGciOiJIUzI1NiJ9...
HTTP/1.1 404 Not Found
Content-Type: application/json
X-Request-ID: req_4d8e1f5a
{
"error": {
"code": "ORDER_ITEM_NOT_FOUND",
"message": "Item xyz-456 not found in order abc-123",
"resource": "order_items",
"id": "xyz-456",
"parent_resource": "orders",
"parent_id": "abc-123"
}
}
Notice that both examples:
- Use a resource-specific error code (
USER_NOT_FOUND,ORDER_ITEM_NOT_FOUND) rather than a generic code - Include the resource type and ID in the message for human readability
- Return
Content-Type: application/json— never return a 404 with HTML body when the client expects JSON - Include a request ID for tracing (best practice for all API responses)
For broader status code coverage, see the HTTP Status Codes guide.
Frequently Asked Questions
Should I return 404 or 403 when a resource exists but the user can't access it?
It depends on your security requirements. Technically, 403 Forbidden is the correct code — the resource exists but the caller lacks permission. However, returning 403 confirms that the resource exists, which can be a security concern in multi-tenant or sensitive applications (confirming that a private user ID, order, or account exists in the system). Some APIs intentionally return 404 for resources the caller cannot access to prevent information disclosure. If you use this approach, document it clearly in your API documentation.
What should the response body look like for a 404?
Always return a structured JSON body with a 404 — never an empty response. Include: a machine-readable code field (e.g., USER_NOT_FOUND), a human-readable message field identifying the resource type and requested ID, and optionally structured resource and id fields for programmatic access. Never include database error messages, stack traces, or internal implementation details.
Is 404 the right code when a database query returns empty results?
It depends on the query. For a specific resource by ID (GET /users/123) where no user exists with that ID, return 404. For a list endpoint with filters (GET /users?status=inactive) that returns no matching records, return 200 with an empty array ({"data": []}). An empty list is a perfectly valid, successful response — it is not an error. Returning 404 for empty lists confuses clients and breaks the semantics of the status code.
What is the difference between 404 and 410 for SEO?
404 Not Found signals that a resource was not found but may exist in the future. Search engines will continue periodically recrawling the URL. 410 Gone explicitly signals that the resource has been permanently removed and will not return. Search engines (including Google) deindex 410 pages significantly faster than 404 pages, making 410 the better choice when you have intentionally removed content and do not plan to replace it at that URL.
Related Topics
All Status Codes
Complete reference for every HTTP status code used in REST APIs.
Error Handling
Consistent error formats and client-side handling patterns.
REST API Design Guide
Complete guide to designing production-ready REST APIs.
Authentication
When 404 vs 401 vs 403 — authentication and authorization errors.