What 201 means
The request succeeded and created one or more new resources. 201 is defined in RFC 9110 §15.3.2. The primary new resource is identified by the Location header; if there is no Location, it is the URL the request was sent to (typical for a PUT that creates the resource at its own URL).
HTTP/1.1 201 Created
Location: /api/orders/8412
Content-Type: application/json
ETag: "v1"
{"id": 8412, "status": "pending", "total": 49.90}
An ETag or Last-Modified header in a 201 describes the newly created representation, so a client can make conditional requests against it later. Unlike 200, a 201 is not cacheable by default.
When to send it
- POST to a collection (
POST /api/orders) that creates a new item. Put the new item's URL inLocation. - PUT to a URL that didn't exist yet. RFC 9110 §9.3.4 says the server must answer 201 when a PUT creates a resource, and 200 or 204 when it replaces an existing one.
- Uploads that store a new file and give it a URL.
Don't send 201 when the work isn't done yet. If the request was queued (a video encode, a large import), 202 Accepted is the honest answer, with a URL where the client can check the job status.
Common mistakes
- No
Locationheader, so clients have to dig the new ID out of the body or guess the URL. - A relative
Locationthat resolves wrongly behind a proxy or API gateway that rewrites paths. Relative references are allowed (they resolve against the request URL), but check them from the outside. - Returning 200 for every create. It works, but clients and API tooling (OpenAPI generators, SDKs) lose the distinction between "created" and "already there".
- Returning 201 for an idempotent repeat. If a client retries a create with the same idempotency key and you return the existing resource, 200 is clearer; if the resource conflicts with an existing one, use 409 Conflict.
How to send it
In Express:
app.post('/api/orders', async (req, res) => {
const order = await createOrder(req.body);
res.status(201).location(`/api/orders/${order.id}`).json(order);
});
In most frameworks the pattern is the same: set the status, set Location, and return the created resource (or a short summary of it) so the client doesn't need a second request. Returning the representation is optional; an empty body with a Location header is valid too.
201 and SEO
201 is an API status. Browsers navigating to a page never expect it, and search engines don't crawl POST endpoints, so it has no direct SEO effect. If a public HTML page ever answers 201 to a GET, that's a bug: fix it to 200.
Related codes
- 200 OK: success that didn't create anything.
- 204 No Content: success with nothing to return, common for updates and deletes.
- 409 Conflict: the resource couldn't be created because it clashes with the current state.
- 422 Unprocessable Content: the data was well-formed but invalid.
Test your create endpoint with the HTTP Request Builder: send a POST and check the status and the Location header in the response.