HTTP 201 Created

The server successfully created a new resource as a result of your request. The response typically includes a Location header pointing to the newly created resource, along with a representation of it in the body.

What Is HTTP 201 Created?

HTTP 201 Created is a success status code that tells the client a new resource has been successfully created on the server. Unlike HTTP 200 OK, which is a general success indicator, 201 carries the specific semantic meaning that something new now exists as a direct result of the request. This distinction is essential for building well-designed RESTful APIs that communicate intent clearly to consuming applications.

When a server returns 201, it should include two key pieces of information. First, a Location header containing the URI of the newly created resource, allowing clients to retrieve or reference it in future requests. Second, a representation of the created resource in the response body, which saves the client from making an immediate follow-up GET request. Modern API design guides, including those from Google, Microsoft, and Stripe, all recommend this pattern for POST endpoints that create records.

The 201 status code is most commonly returned in response to POST requests, though it can also result from PUT requests when the target URI did not previously exist. Understanding when to return 201 versus 200 versus 204 is a foundational skill for API developers. Using the correct status code makes your API self-documenting and easier for frontend developers, mobile engineers, and third-party integrators to consume correctly.

Common Causes

New Record via POST

A POST request to a collection endpoint created a new record in the database. The server returns 201 with the new resource's data and a Location header containing its URI.

File Upload Completed

A file was successfully uploaded and stored. The 201 response confirms creation and provides the URL where the uploaded file can be accessed.

User Registration

A new user account was created through a signup form or API call. The server returns 201 to distinguish creation from a simple data retrieval that would return 200.

PUT to New Resource

A PUT request targeted a URI that did not exist, and the server created a new resource at that location. This is less common than POST creation but is valid per the HTTP specification.

How to Fix

Include Location Header

Always include a Location header in 201 responses pointing to the URI of the newly created resource. This follows REST conventions and enables clients to navigate to the new resource.

Return the Created Resource

Include the full resource representation in the response body so clients do not need a follow-up GET request. This reduces round trips and improves perceived performance.

Use 201 Instead of 200 for Creation

If your API endpoint creates new records, return 201 rather than 200. This semantic distinction helps clients differentiate between creation and retrieval or update operations.

Validate Before Creating

Return 400 Bad Request or 422 Unprocessable Entity if validation fails. Only return 201 when the resource was actually persisted to the data store.

Code Examples

Express.js

// Express.js — return 201 when creating a resource
app.post('/api/posts', async (req, res) => {
  const { title, body, authorId } = req.body;

  // Validate required fields
  if (!title || !body) {
    return res.status(400).json({ error: 'Title and body are required' });
  }

  const post = await Post.create({ title, body, authorId });

  // Return 201 with Location header and created resource
  res.status(201)
    .location(`/api/posts/${post.id}`)
    .json({
      id: post.id,
      title: post.title,
      body: post.body,
      createdAt: post.createdAt
    });
});

Flask (Python)

# Flask — return 201 when creating a resource
from flask import Flask, jsonify, request

app = Flask(__name__)

@app.route('/api/posts', methods=['POST'])
def create_post():
    data = request.get_json()

    if not data.get('title') or not data.get('body'):
        return jsonify(error='Title and body are required'), 400

    post = Post(title=data['title'], body=data['body'])
    db.session.add(post)
    db.session.commit()

    # Return 201 with Location header
    response = jsonify(
        id=post.id,
        title=post.title,
        body=post.body,
        created_at=post.created_at.isoformat()
    )
    response.status_code = 201
    response.headers['Location'] = f'/api/posts/{post.id}'
    return response

Frequently Asked Questions

When should I return 201 instead of 200?

Return 201 whenever a request results in the creation of a new resource. Use 200 for requests that retrieve, update, or perform actions without creating something new. The semantic difference helps API consumers understand exactly what happened.

Is the Location header required for 201?

Yes, the HTTP specification states that a 201 response should include a Location header with the URI of the created resource. While some APIs omit it, including the header follows best practices and enables hypermedia-driven clients.

Should the 201 response body include the created resource?

Yes, including the full resource representation eliminates the need for a follow-up GET request. This reduces latency, lowers server load, and improves the developer experience when integrating with your API.

Can a PUT request return 201?

Yes. If a PUT request targets a URI that does not exist and the server creates a new resource at that location, 201 is the correct response. If the PUT updates an existing resource, return 200 or 204 instead.

What is the difference between 201 and 202?

HTTP 201 means the resource was created immediately and is available now. HTTP 202 Accepted means the server acknowledged the request but has not finished processing it yet. Use 202 for asynchronous operations like queued jobs or batch processing.

Monitor Your APIs & Services

Get instant alerts when your endpoints go down. 60-second checks, free forever.

Start Monitoring Free →