Skip to content

201 Created

What is HTTP 201 Created?

201 Created
When you send a message to a website asking it to create something new (like pos...
HTTP 201 Created status code illustration

Explain Like I’m 3

You asked for something new to be made, and it worked! Like when you ask to build a sandcastle and it’s done - you made something new that wasn’t there before!

Example: You draw a picture and put it on the refrigerator. Now there’s a new picture that didn’t exist before - you created it!

Explain Like I’m 5

When you send a message to a website asking it to create something new (like posting a picture or making an account), the website says ‘201 Created’ to tell you it worked and the new thing was made. It’s like when you give your teacher a drawing and they put it on the wall - they’re saying ‘I got it and I put it up!’ The website also tells you where to find what you just created.

Example: You sign up for a new game account. You send your username and password, and the website creates your account and says ‘201 Created!’ It tells you where your new account lives (like ‘Your account is at /users/coolgamer123’) so you can find it later.

Jr. Developer

HTTP 201 Created means your request successfully created a new resource on the server. This is the proper response for successful POST requests that result in resource creation. The server should include a Location header pointing to the newly created resource’s URL, and typically includes the created resource in the response body. This is different from 200 OK, which indicates general success but doesn’t specifically mean something was created. Use 201 when your API endpoint creates a new user, post, product, or any other resource.

Example: A user submits a form to create a blog post. Your API creates the post in the database, assigns it ID 42, and responds with 201 Created, a Location header pointing to /posts/42, and the full post object in the response body.

Code Example

// Express.js creating a new user
app.post('/users', async (req, res) => {
const newUser = await db.createUser(req.body);
res.status(201)
.location(`/users/${newUser.id}`)
.json(newUser);
});

Crash Course

201 Created is the semantically correct response for successful resource creation, primarily used with POST requests. The response should include a Location header with the URI of the created resource, enabling clients to immediately access or reference it. The response body typically contains the created resource’s representation, including any server-generated fields like IDs or timestamps. Unlike 200 OK (generic success), 201 explicitly signals resource creation, which is important for RESTful API design and client behavior. Some APIs return 201 for PUT requests that create resources at specific URIs. The status helps distinguish between creation and other successful operations like updates, searches, or processing actions that don’t create persistent resources.

Example: An e-commerce API receives POST /products with product data. The server validates the data, creates the product with ID 789, generates a slug, adds timestamps, and responds with 201 Created. The Location header contains https://api.shop.com/products/789, and the body includes the complete product object with all generated fields.

Code Example

// REST API creating a resource with validation
app.post('/api/articles', async (req, res) => {
try {
const { title, content, author } = req.body;
// Validate input
if (!title || !content) {
return res.status(400).json({ error: 'Missing required fields' });
}
// Create resource
const article = await Article.create({
title,
content,
author,
slug: generateSlug(title),
createdAt: new Date()
});
// Return 201 with Location header
res.status(201)
.location(`/api/articles/${article.id}`)
.json(article);
} catch (error) {
res.status(500).json({ error: 'Creation failed' });
}
});

Deep Dive

The 201 Created status code, defined in RFC 9110 Section 15.3.2, indicates the request has been fulfilled and has resulted in one or more new resources being created. Per the specification, the origin server SHOULD send a 201 response with a Location header field identifying the primary resource created. If multiple resources were created, the Location header should identify the primary or most specific resource. The response payload typically includes a representation of the created resource’s current state, though this is not required. The 201 status is cacheable, though in practice, POST responses are rarely cached unless explicit Cache-Control directives are provided.

Technical Details

The semantic distinction between 201 and 200 carries protocol significance. 201 specifically indicates resource creation, enabling clients to update their local state, follow the Location header for subsequent operations, or display success messages appropriate to creation. The Location header’s URI may be relative or absolute, following RFC 3986 URI resolution rules. Validators (ETags, Last-Modified) in the response apply to the created resource at the Location URI.

Idempotency considerations: POST requests returning 201 are typically not idempotent - repeating the request creates multiple resources. Some APIs implement idempotency keys or unique constraints to prevent duplicate creation. PUT requests creating resources at client-specified URIs are idempotent and may return 201 on first creation, 200 or 204 on subsequent identical requests.

RESTful APIs should distinguish creation scenarios: POST to collections creates subordinate resources (POST /users → 201 with Location: /users/123), while PUT to specific URIs creates or replaces resources at that location (PUT /users/123 → 201 if created, 200 if replaced). Some HTTP-based protocols use 201 with async processing: the Location header may point to a status endpoint rather than the final resource, with the resource becoming available after background processing completes.

Security considerations include validating that Location URIs remain within expected domains to prevent header injection attacks. Rate limiting is crucial for creation endpoints to prevent resource exhaustion. Authorization checks must ensure clients can only create resources they’re permitted to. Response bodies should not leak sensitive information about resource creation mechanics or internal identifiers unless intentional.

Modern API patterns may deviate from strict REST: GraphQL mutations don’t use 201 (always 200 for successful queries), JSON:API specs recommend 201 with specific formatting requirements, and some minimalist APIs return 201 with empty bodies and only Location headers to reduce bandwidth.

Code Example

// Production-grade resource creation with idempotency
const crypto = require('crypto');
app.post('/api/orders', async (req, res) => {
const idempotencyKey = req.header('Idempotency-Key');
try {
// Check for duplicate request using idempotency key
if (idempotencyKey) {
const existing = await Order.findByIdempotencyKey(idempotencyKey);
if (existing) {
// Return existing resource, but with 200 not 201
return res.status(200)
.location(`/api/orders/${existing.id}`)
.json(existing);
}
}
// Validate request
const { items, shippingAddress } = req.body;
if (!items?.length || !shippingAddress) {
return res.status(400).json({
error: 'Missing required fields',
required: ['items', 'shippingAddress']
});
}
// Create order with transaction
const order = await db.transaction(async (trx) => {
const newOrder = await Order.create({
items,
shippingAddress,
status: 'pending',
total: calculateTotal(items),
idempotencyKey,
createdAt: new Date()
}, { transaction: trx });
// Create associated line items
await OrderItem.bulkCreate(
items.map(item => ({ orderId: newOrder.id, ...item })),
{ transaction: trx }
);
return newOrder;
});
// Generate ETag for the created resource
const etag = crypto
.createHash('md5')
.update(JSON.stringify(order))
.digest('hex');
// Log creation for audit trail
logger.info('Order created', {
orderId: order.id,
userId: req.user?.id,
total: order.total
});
// Return 201 with comprehensive headers
res.status(201)
.location(`/api/orders/${order.id}`)
.set({
'ETag': `"${etag}"`,
'Cache-Control': 'no-cache',
'Content-Type': 'application/json'
})
.json(order);
} catch (error) {
logger.error('Order creation failed', { error, body: req.body });
res.status(500).json({
error: 'Failed to create order',
message: error.message
});
}
});

Frequently Asked Questions

When should I use 201 Created instead of 200 OK?

Use 201 when your request created a new resource that persists on the server (user, post, order, etc.). Use 200 for successful requests that don't create resources, like searches, calculations, or updates. If your POST endpoint performs an action without creating a persistent resource, 200 is appropriate.

Is the Location header required in 201 responses?

The RFC says servers SHOULD (not MUST) include a Location header. It's strongly recommended as best practice since it tells clients where to find the created resource. Omitting it forces clients to guess or parse the response body for the resource URI.

Should I return the created resource in the response body?

It's common practice and user-friendly, but not required. Returning the resource saves clients from making a follow-up GET request. Include server-generated fields (ID, timestamps, computed values) that the client needs. Some minimalist APIs return only the Location header with an empty body.

Can PUT requests return 201 Created?

Yes! When a PUT request creates a resource at the specified URI (rather than updating an existing one), 201 is appropriate. Subsequent identical PUT requests to the same URI should return 200 or 204 since they're updating, not creating. This maintains PUT's idempotent semantics.

How do I prevent duplicate resource creation with POST?

Implement idempotency keys: clients send a unique Idempotency-Key header with their request. Store this key with the created resource. If the same key appears again, return the existing resource with 200 OK instead of creating a duplicate. This handles network retries gracefully.

Common Causes

  • Successful POST request creating a new user, account, or profile
  • API endpoint creating a new post, comment, or content item
  • Form submission that generates a new database record
  • File upload endpoint creating a new file resource
  • E-commerce order placement creating an order record
  • PUT request creating a resource at a client-specified URI (first time only)

Implementation Guidance

  • Ensure your POST handler actually creates a resource before returning 201
  • Always include a Location header pointing to the new resource’s URI
  • Return the created resource in the response body with all generated fields
  • Use 200 OK instead if the request doesn’t create a persistent resource
  • Implement proper error handling to return 400/500 on creation failures
  • Add idempotency key support to prevent duplicate resource creation
  • Validate input data before creation to return 400 for invalid requests
  • For PUT creating resources, ensure idempotency (repeated calls don’t create duplicates)

Comments