HTTP Status Codes for CRUD APIs: A 2026 Practitioner Guide

The HTTP specification defines dozens of status codes you can return from a REST API. In practice, a well-designed CRUD API uses about fifteen of them across its entire surface. The right choice per endpoint is what lets HTTP clients, proxies, browsers, and API SDKs behave predictably without parsing your response body to figure out what just happened.

Learn More About Moesif Debug And Fix API Issues Quickly With Customer-Centric API Logging 14 day free trial. No credit card required. Try for Free

This guide walks through every CRUD action (create, read, update, delete), the status codes appropriate for each, the patterns for redirects and errors, and the 2026 wrinkles around idempotent retries and AI agent traffic.

How HTTP status codes work

A status code is a three-digit number between 100 and 599 returned with every HTTP response. The first digit tells the client which class of response it is dealing with: success, redirect, client error, or server error. The second and third digits narrow down the specific reason.

Every status code is paired with a reason phrase. 200 OK, 404 Not Found, 500 Internal Server Error. The number is for programmatic recognition; the phrase is for humans reading the response. The two-rule pattern holds even for custom codes that some APIs define (though custom 6xx-range codes are not part of the standard and should be avoided).

For the complete reference across all five families, see our HTTP status codes guide. This post focuses specifically on the CRUD slice.

The five status code classes

Understanding the class makes diagnosing problems faster:

  • 1xx Informational. Rarely seen in REST APIs. They exist for low-level HTTP plumbing, like 100 Continue (the server is willing to receive the request body) or 101 Switching Protocols (the connection is being upgraded, usually to WebSocket).
  • 2xx Success. The request was accepted. For asynchronous processing (202 Accepted), this only means the request met validation; the work may still be in progress.
  • 3xx Redirection. The resource is somewhere else. Some HTTP clients follow redirects automatically; others surface them so the application can decide.
  • 4xx Client errors. The request was wrong: bad input, missing auth, invalid URL, exceeded rate limit. The client should fix something and retry.
  • 5xx Server errors. The server failed to process a valid request. These can be temporary (retry) or permanent (give up). The client did nothing wrong.

Status codes outside the 100-599 range are not registered with IANA, and most HTTP clients treat them as invalid, but some legacy APIs use them anyway. API client implementations have to be defensive about them. As an API designer, you should not be the source of one.

CRUD: which code for which action

CRUD covers the four basic operations on a persistent resource: create, read, update, delete. Each maps cleanly to one HTTP method and a small set of valid status codes.

CREATE

Use POST for create. The standard responses:

  • 201 Created: the right code for most creates. Include a Location response header pointing at the new resource, and ideally the resource itself in the body so the client does not need a follow-up GET.
  • 200 OK: acceptable when the create does not produce a server-side resource accessible via URL. Issuing an access token, for example, returns 200 because there is no URL to point at.
  • 202 Accepted: for asynchronous creates. The request was valid but the resource will exist sometime in the future. Include a URL the client can poll for status, plus an estimated completion time when known.
  • 303 See Other: used with a Location header when the create succeeds and the client should GET a different URL to see the result. Useful for the post-redirect-get pattern.

READ

Use GET for read. Asynchronous reads are rare; if the resource needs to be computed first, that is really a create followed by a read, so use POST for the computation request.

  • 200 OK: the default for successful reads.
  • 206 Partial Content: for paginated or range-based reads. The client sends a Range header; the server returns the specified slice. Common for video streaming and large file downloads.
  • 300 Multiple Choices: if there are multiple representations of the resource and the client must choose. Rare in practice.
  • 304 Not Modified: for conditional requests. The client sent If-None-Match (with an ETag) or If-Modified-Since, and the resource has not changed. The server returns 304 with no body so the client uses its cached copy.
  • 307 Temporary Redirect: when the resource is temporarily available at a different URL.
  • 308 Permanent Redirect: when the resource has moved permanently. Tells the client to update its bookmarks/cached URLs.

UPDATE

Use PUT for full replacement (client sends the entire updated resource) or PATCH for partial updates (client sends only the fields that changed).

  • 200 OK: the most common response for successful updates, with the updated resource in the body.
  • 204 No Content: when the update succeeds and you do not need to return the resource. Common for “save draft” patterns.
  • 202 Accepted: for asynchronous updates that will complete later.

DELETE

Use DELETE.

  • 204 No Content: the most appropriate response. Reduces traffic and the resource is gone, so there is nothing meaningful to return.
  • 200 OK: if you want to include a representation of what was deleted in the response body. Some teams prefer this for audit trails.
  • 202 Accepted: for asynchronous deletes in distributed systems where the delete propagates over time.

The discipline here matters more than picking the single correct code. Pick one for each CRUD action and apply it consistently across the entire API. Mixing 200 and 204 for deletes across endpoints is the kind of inconsistency that costs trust.

Handling API changes and migrations

APIs evolve. Endpoints get renamed, paths get restructured, resources move. The redirect class of status codes is how you communicate those changes to clients that have your URLs hard-coded.

  • 307 Temporary Redirect: use when the resource might be at a different URL right now but the original URL is still the canonical one. The client should keep using the original URL for future requests.
  • 308 Permanent Redirect: use when the resource has moved permanently and the client should update its references. Preserves the HTTP method across the redirect (unlike 301 and 302, which clients have historically treated inconsistently).

For deprecation specifically, pair the redirect with two HTTP response headers: Sunset (defined in IETF RFC 8594) and Deprecation (defined in IETF RFC 9745). Well-behaved SDKs warn the developer in their build logs when they see them, so the deprecation message reaches the consumer’s terminal before reaching their inbox.

Multiple endpoints for one resource

If your API offers nested and root paths for the same resource (/users/kay/comments/456, /posts/123/comments/456, and /comments/456 all referring to the same comment), one common pattern is to implement the actual logic on the root path and redirect the nested ones with 308 Permanent Redirect plus a Location header. This keeps the implementation DRY and lets the framework’s redirect machinery do the routing. Apply this only for GET requests; POST, PUT, PATCH, and DELETE should not redirect.

Error responses: client and server errors

Default error handling in most frameworks returns either 500 Internal Server Error or 404 Not Found for any unexpected condition. Both are blunt instruments. There are more specific codes that communicate more.

Wrong URL or method

  • 404 Not Found: the URL did not match any resource. Use this for missing IDs (/users/9999) but also for routes the API does not define at all.
  • 405 Method Not Allowed: the URL exists but the API does not accept the method the client used. Include an Allow header listing the methods that are supported.
  • 406 Not Acceptable: the URL exists but the API cannot return a representation in the format the client’s Accept header requested.
  • 410 Gone: like 404 but with the explicit signal that the resource used to exist and was intentionally removed. Useful for deprecated endpoints.
  • 414 Request-URI Too Long: the URL itself exceeds the server’s parsing limit.
  • 501 Not Implemented: the method is not supported for any resource (the whole API has not been built for that method).

Authentication and permissions

  • 401 Unauthorized: credentials are missing or invalid. Re-authenticating may fix the request.
  • 403 Forbidden: credentials are valid but the user is not allowed to access this resource. Re-authenticating will not help.
  • 404 Not Found: sometimes used in place of 403 when you do not want to leak the existence of a protected resource to unauthorized callers.

A clean error response body includes a machine-readable code (e.g., payment_method_declined), a human-readable message, and where applicable the field that caused the error. Consumers parse the code to handle errors programmatically without string-matching on the message.

CRUD status codes in 2026: idempotency and agent retries

This is the part of the conversation that did not exist five years ago and that matters most for APIs designed in 2026.

AI agents retry aggressively. When an LLM-backed application calls your POST /orders endpoint as part of a multi-step task and the request times out, the agent’s retry layer fires the call again, often within seconds. Without idempotency, you create duplicate orders.

The convention popularized by Stripe and widely adopted across payments and infrastructure APIs: clients send an Idempotency-Key request header on POST and PATCH requests, and the server returns the same response on retry. (The IETF httpapi working group has an Internet-Draft titled “The Idempotency-Key HTTP Header Field” progressing toward standardization.) Reserve a slot in your status code semantics for this:

  • First call succeeds → 201 Created (or 200/204 as appropriate)
  • Retry with the same key, response cached → return the same status code with the same body
  • Retry with the same key but different body → 409 Conflict (the key is being reused with mismatched intent)

429 Too Many Requests is the agent-safe rate-limit signal. Agents respect 429 with a Retry-After header far better than they respect 5xx errors, which they interpret as server outage and retry aggressively. If your API is being hammered, return 429 with backoff instructions and the agent layer slows down. Return 503 and you get a thundering herd.

Sunset and Deprecation headers reach agents too. When you deprecate an endpoint, agents that read OpenAPI specs and respect HTTP headers will warn the developer or refuse to use the endpoint. This is one of the cheapest ways to communicate API evolution to a non-human audience.

These three patterns are now table stakes for CRUD APIs that expect any agent traffic, which in 2026 is most of them.

How Moesif tracks status code distribution in production

A clean spec and well-chosen codes only matter if the implementation actually returns them. The common failure mode is code drift: the spec says POST /orders returns 201, the live API returns 200, and consumers branching on status code are silently broken.

Moesif records the status code distribution per endpoint and per customer in production. The visualization makes the question “which endpoint is returning the wrong code?” answerable in seconds rather than hours. Pair it with the API design principles we cover separately, and the design-time decisions stay aligned with the live behavior.

Conclusion

The HTTP creators thought about many status codes when designing the spec, and have added several more over the years. Used correctly they improve developer experience by taking advantage of automatic client behavior (redirects, retries, caching) and by communicating exactly what happened without forcing every consumer to parse the response body.

Sometimes more than one code could fit a particular case. The important discipline is to pick one and stay consistent across the entire API surface.

If you want to see the actual status code distribution your API is returning per endpoint and per customer, start a 14-day Moesif free trial. No credit card required.

Frequently asked questions

Which HTTP status code should I return for a successful POST? 201 Created when the request creates a new server-side resource, with a Location header pointing at the new resource. 200 OK is acceptable for creates that do not produce an accessible URL (like issuing an access token). 202 Accepted if the create is asynchronous.

What is the right status code for an asynchronous request? 202 Accepted. The request was valid but the work will complete later. Include a URL the client can poll for status and, where known, an estimated completion time.

Should DELETE return 200 or 204? 204 No Content is the most appropriate code for most deletes. Return 200 OK only if you want to include a representation of what was deleted in the response body (useful for audit-trail patterns).

What is the difference between 401 and 403? 401 Unauthorized means the credentials are missing or invalid; re-authenticating may fix it. 403 Forbidden means the credentials are valid but the user is not allowed to access the resource; re-authenticating will not help.

When should I use 308 vs 301? Both are permanent redirects. 308 preserves the HTTP method across the redirect (a POST stays a POST); 301 does not. In 2026, prefer 308 for any redirect that needs to survive non-GET requests.

Do AI agents respect HTTP status codes? Yes, increasingly. Agents respect 429 with Retry-After, follow 307/308 redirects, and warn or refuse on Deprecation/Sunset headers. Returning the right status code is now part of designing for the agent audience as much as the human one.

Learn More About Moesif Deep API Observability with Moesif 14 day free trial. No credit card required. Try for Free
Debug And Fix API Issues Quickly With Customer-Centric API Logging Debug And Fix API Issues Quickly With Customer-Centric API Logging

Debug And Fix API Issues Quickly With Customer-Centric API Logging

Learn More