HTTP 201 Created
201 tells the client that something new now exists on the server. Per RFC 9110, the main created resource is identified by the Location header if there is one, and otherwise by the request URL. So a client can store the new URL without parsing the body, which a bare 200 doesn't allow.
201 Created
The request succeeded and created one or more new resources.
Who fixes it: Nobody (success). A success code. The things to get right are on the server: the Location header, the body, and not creating duplicates on retries.
What 201 means
It's usually the answer to POST /collection. A PUT /items/42 that creates an item that didn't exist yet should also return 201, and 200 or 204 if it replaced an existing one.
The body is up to you. Most APIs return the created resource, including server-generated fields such as id, createdAt and defaults, so the client doesn't need a follow-up GET.
Source: RFC 9110 §15.3.2.
When to return 201, and when not to
POST /usersinserted a new row: return 201 withLocation: /users/123.PUT /files/report.pdfuploaded a file to a new path: 201. Replacing an existing file is 200 or 204.- The work was only queued (a video transcode, a bulk import): it's not created yet. Return 202 Accepted with a URL the client can poll.
- The POST was an action that created nothing (search, login, sending an email): return 200.
- The resource already existed (duplicate unique key): return 409 Conflict, or point to the existing resource if your API is idempotent by design.
How to return a 201
Express
res.location() sets the Location header. Relative URLs are allowed; RFC 9110 resolves them against the request URL.
app.post('/api/users', async (req, res) => {
const user = await db.users.create(req.body);
res
.status(201)
.location(`/api/users/${user.id}`)
.json(user);
});Next.js Route Handler
Response.json() takes the status and headers in its second argument.
// app/api/users/route.ts
export async function POST(request: Request) {
const input = await request.json();
const user = await createUser(input);
return Response.json(user, {
status: 201,
headers: { Location: `/api/users/${user.id}` },
});
}Make retries safe with an idempotency key
If a client times out and retries a POST, you may create two resources. The Idempotency-Key convention (popularised by Stripe, now an IETF draft) fixes this: store the key with the result and replay the original 201 for repeat keys.
curl -i https://api.example.com/users \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: 6f1c2a4e-9b1d-4c1e-8a57-1f0e2b7d9c11' \
-d '{"email":"ada@example.com"}'
HTTP/1.1 201 Created
Location: /users/123
Content-Type: application/jsonHeaders that matter with 201
- Location
- The URL of the new resource. It can be relative, e.g.
/users/123. - Access-Control-Expose-Headers
Locationisn't a CORS-safelisted response header, so a cross-originfetch()can't read it unless the server sendsAccess-Control-Expose-Headers: Location.- ETag / Last-Modified
- If sent with a 201, they describe the newly created resource, so the client can make a conditional update straight away.
201 vs similar status codes
- 200 OK: Generic success. Fine for actions that return a result, but loses the "something was created" signal.
- 202 Accepted: The request was accepted but the resource doesn't exist yet. Return a status URL instead of the resource.
- 409 Conflict: The resource you tried to create already exists, or clashes with the current state.
Related status codes
200OK
The request succeeded, and the response content is the result.
204No Content
The request succeeded and there is intentionally no content in the response.
409Conflict
The request conflicts with the current state of the target resource.
422Unprocessable Content
The server understands the content type and syntax of the request, but can't process the instructions it contains.
Looking for another code? Search the full HTTP status codes list, or read which code your API should return in the REST API status codes guide.
Learn more
HTTP Status Codes for REST APIs: 200 vs 201 vs 204, 400 vs 422 vs 429
A practical guide to choosing the right HTTP status code in REST API design. Covers the most confusing pairs: 200 vs 201, 400 vs 422, 401 vs 403, and when to use 503 vs 429.
REST API Design Best Practices: A Practical Guide
The conventions that make REST APIs intuitive and consistent. Covers URL design, HTTP methods, status codes, versioning, pagination, and error responses.
CORS Explained: Why It Happens and How to Fix It
CORS errors happen when a browser blocks a request to a different origin. Learn how the Same-Origin Policy works, what preflight requests are, and how to configure CORS correctly in Express, FastAPI, and Nginx.
Frequently Asked Questions
Does a 201 response need a body?▾
No, the body is optional. Many APIs return the created resource so clients get server-generated fields such as the id without a second request. The Location header is the standard way to say where the new resource lives.
Is it wrong to return 200 instead of 201 after creating something?▾
It works, and plenty of APIs do it, but it hides the fact that a resource was created. 201 with a Location header lets generic clients, SDK generators and tests understand the outcome from the status line alone.
Why can't my frontend read the Location header of a 201?▾
For cross-origin requests the browser only exposes CORS-safelisted response headers to JavaScript, and Location isn't one of them. Add Access-Control-Expose-Headers: Location on the server, or include the new id or URL in the JSON body.