HTTP 201 Created

HTTP 201 Created means the request succeeded and a new resource was created. It is the correct response for POST endpoints that create entities in a REST API, and for PUT requests that create a resource at a specified URL. The server should include a Location header pointing to the newly created resource, and typically returns the created resource in the response body.

HTTP 201 quick reference โ†’

Quick reference

Code201
NameCreated
Category2xx Success
SpecificationRFC 9110 ยง15.3.2
CacheableNo โ€” not cached by default
Location headerShould be included โ€” URL of the new resource

When to return 201

Return 201 whenever a POST request creates a new resource that is now addressable โ€” a new user, order, document, record, or any entity with a URL. Return 201 from a PUT request only when the resource did not previously exist and was created by that PUT. If a PUT updates an existing resource, return 200 or 204 instead.

Do not return 201 when no new resource was created. A login endpoint succeeds without creating a new resource โ€” that is 200. A delete endpoint removes a resource โ€” 200 or 204. An update endpoint modifies an existing resource โ€” 200 or 204. 201 is specifically for creation.

The Location header

RFC 9110 says the server SHOULD include a Location header in a 201 response. The header value is the URL of the newly created resource โ€” the URL a GET request would hit to retrieve it.

HTTP/1.1 201 Created
Location: https://api.example.com/users/4821
Content-Type: application/json

{
  "id": 4821,
  "email": "user@example.com",
  "created_at": "2026-04-25T10:30:00Z"
}

The URL in Location should be absolute. The response body here contains the same data a GET to that URL would return, saving the client a follow-up request. RFC 9110 allows but does not require a body with 201.

Omitting the Location header is technically valid but forces clients to parse the body to extract the new resource's ID and construct the URL themselves. Always include it in REST APIs.

Common mistakes

Returning 200 instead of 201

The most common error. The client gets its data, but the semantic signal is lost. 201 specifically tells tools, documentation generators, and client libraries that a new addressable resource was created. A 200 from a creation endpoint is technically wrong per RFC 9110 semantics.

201 from PUT on an existing resource

A PUT to an existing URL that updates the resource should return 200 or 204, not 201. Returning 201 on every PUT regardless of whether the resource was created or updated incorrectly signals creation on updates. Check whether the resource existed before the PUT and respond accordingly.

201 for bulk creation

When a single request creates multiple resources, there is no single URL for the Location header. Use 200 with a body listing the created URLs, or 202 if creation is asynchronous. Alternatively, design bulk creation as multiple requests.

Missing Location header in framework responses

Some frameworks and ORMs return 201 automatically without setting Location. Check that your serializer or response builder includes the header. In Express: res.location('/users/' + user.id).status(201).json(user). In Django REST Framework: the CreateModelMixin sets Location automatically when get_success_url is defined.

201 vs 200 vs 202 vs 204

CodeUse when
200Action succeeded, no new resource created โ€” login, search, update
201New addressable resource was created โ€” POST to a collection, PUT to a new URL
202Creation request accepted but not yet complete โ€” async processing
204Action succeeded, nothing to return โ€” bulk delete, preferences update

Framework examples

Express (Node.js)

app.post('/users', async (req, res) => {
  const user = await db.users.create(req.body);
  res
    .location(\`/users/\${user.id}\`)
    .status(201)
    .json(user);
});

Django REST Framework

class UserCreateView(CreateAPIView):
    serializer_class = UserSerializer
    # CreateAPIView automatically returns 201
    # Set get_absolute_url() on the model for Location header

Go (net/http)

w.Header().Set("Location", "/users/"+strconv.Itoa(user.ID))
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusCreated)
json.NewEncoder(w).Encode(user)

Related guides

HTTP 200 OK ยท HTTP 202 Accepted ยท HTTP 204 No Content

Comparisons

200 vs 201 ยท 201 vs 202

Standards reference

Definitions from the IANA HTTP Status Code Registry and RFC 9110 ยง15.3.2. Human-readable guidance by ErrorLookup. ยท HTTP 201 quick reference โ†’