201 Created
Success
The request succeeded and resulted in the creation of one or more new resources. The response should include a Location header pointing at the canonical URL of the newly created resource, and typically a body describing what was created. 201 is the correct response for a successful POST that creates a resource. Using 200 instead is a common API design mistake that hides creation semantics from clients and crawlers.
When does this happen?
Return 201 when a POST or PUT call has caused a new resource to come into existence. The classic example is POST /users producing a new user record, but the same applies to file uploads, signup endpoints, order creation, and any action that mints a new identifier. The server should include a Location header so the client knows the URL of the new resource, and ideally include the resource in the response body so the client does not have to issue a follow-up GET. A 201 may also be returned from PUT when the target URL did not previously exist and the client created it directly. Browsers treat 201 essentially the same as 200. They will render the body if one is present and not navigate anywhere unless instructed. Search engines treat 201 as a successful response but it is rarely seen by crawlers because it is almost always served on POST endpoints.
Common causes
- POST to a collection endpoint like /v1/users that created a new record.
- PUT to a specific URL that did not previously exist, where the client provides the identifier and the server accepts it.
- File upload endpoint that stored the file and returned its canonical URL in the Location header.
- Signup or registration endpoint that minted a new account.
- Order placement, booking creation, or any commerce flow that produced a new domain entity.
- Batch creation endpoint that returned 201 with an array describing every resource it created.
How to fix it
- No fix needed when this is the expected outcome. Verify the Location header points at the canonical URL of the new resource.
- If your POST endpoint returns 200 instead of 201, change it. Clients and crawlers can use the distinction to detect side effects.
- Confirm the response body includes the new resource's identifier so callers do not need a follow-up GET.
- If you return 201 without a Location header, document the URL pattern explicitly in your API spec so clients can construct it themselves.
- For batch creation, return 201 with a list of created resources rather than one row at a time. Round-trips matter.
- When PUT acts as upsert, distinguish 201 (created) from 200 (updated) so clients can react accordingly.
Real-world examples
- Client submits POST /v1/users with a JSON body containing the new user's email and password.
- Server creates the user, returns 201 with Location: /v1/users/42 and a JSON body containing the new user object including the generated id and created_at.
- Mobile app uploads an avatar image via POST /v1/uploads with multipart/form-data.
- Server stores the file in object storage, returns 201 with Location: /v1/uploads/abc123 and a JSON body containing the file's public URL.
- API client issues PUT /v1/configs/staging with a config payload when staging does not yet exist.
- Server creates the config, returns 201 with Location: /v1/configs/staging. A subsequent PUT to the same URL would return 200 because the config now exists.
- Webhook receiver accepts a POST from a third-party service that minted a new event record.
- Server stores the event, returns 201 with the new event id in the body. The webhook sender treats anything in 2xx as delivered.
Debugging
- Run curl -i -X POST https://example.com/v1/users -H 'Content-Type: application/json' -d '{"email":"[email protected]"}' and confirm the response line starts with HTTP/1.1 201.
- Inspect the Location response header. It should be an absolute or root-relative URL pointing at the new resource.
- Follow the Location header with curl -I and confirm it returns 200 with the same identifier embedded in the body.
- In DevTools Network tab, click the POST request and switch to Headers. Verify Status Code: 201 Created and look for Location under Response Headers.
How it differs from related codes
HTTP 200
200 means the request succeeded but does not promise that a new resource was minted. Use 201 specifically when the side effect of the request was the creation of a resource with its own URL.
HTTP 202
202 means the server accepted the request for processing but the resource is not yet created. Useful for async pipelines where creation happens later. 201 means creation is already complete.
HTTP 204
204 means success with no body. 201 always implies a new resource exists and should include either the resource or a Location header pointing at it.
Related status codes
See HTTP 201 in your redirect chains?