Skip to content
Webmaster Tools

201 Created

What HTTP 201 Created means, when an API should return it instead of 200, how to set the Location header, and what to put in the response body.

All status codes

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 in Location.
  • 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 Location header, so clients have to dig the new ID out of the body or guess the URL.
  • A relative Location that 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.

Test your create endpoint with the HTTP Request Builder: send a POST and check the status and the Location header in the response.