How to Return 201 Created From a POST Endpoint (REST Status Codes)

A Flask create endpoint returns a bare 200 OK by default, so a React UI that only treats res.status === 201 as success never shows its confirmation - the todo saves but the "Added!" message never fires. The fix is one line: return a (body, status) tuple so a successful create sends 201 Created. Status codes are an API contract - 201 for create, 200 for read, and a 400 bad request in a REST API for bad input.

Fullstack Engineerflaskpythonreact

Why a create should return 201, not 200

HTTP status codes are the contract between an API and everything that consumes it - browsers, caches, tests, and other services all branch on the number, not on the response body. REST assigns a specific code to each outcome:

Flask returns 200 by default. So a create endpoint that just returns a body silently sends the wrong code - the body looks right, but the contract is broken.

The bug: a bare return sends 200

The create handler builds the todo correctly but returns only the body, so Flask defaults to 200 OK:

@app.post("/api/todos")
def create_todo():
    title = (request.get_json(silent=True) or {}).get("title", "")
    todo_id = next(_next_id)
    TODOS[todo_id] = {"id": todo_id, "title": title}
    # BUG: returns a default 200 OK. A create should be 201 Created.
    return jsonify(TODOS[todo_id])

The React frontend only accepts 201 as success:

const res = await fetch('/api/todos', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ title: 'buy milk' }),
});
// Only treat 201 Created as success.
setStatus(res.status === 201 ? 'Added!' : `Unexpected status ${res.status}`);

The server saves the todo, the network call succeeds, but because the status is 200 and not 201, the check fails and the user sees "Unexpected status 200" instead of "Added!". The client is right to be strict - the API is the side that broke the convention.

The fix: return a (body, status) tuple

In Flask you set the status explicitly by returning a tuple whose second element is the code:

@app.post("/api/todos")
def create_todo():
    title = (request.get_json(silent=True) or {}).get("title", "")
    todo_id = next(_next_id)
    TODOS[todo_id] = {"id": todo_id, "title": title}
    return jsonify(TODOS[todo_id]), 201

That single , 201 is the whole fix. The body is unchanged - only the status code moves from 200 to 201, which is exactly what the client checks.

Verify with curl

Print just the status code so you can confirm the contract without reading the body:

pkill -f "python3 app.py"; python3 app.py > /tmp/flask.log 2>&1 &

curl -s -o /dev/null -w '%{http_code}\n' -X POST localhost:8000/api/todos \
  -H 'Content-Type: application/json' -d '{"title":"buy milk"}'

This should print 201. Reload the React page, click "Add todo", and the status line now reads "Added!". The lesson generalizes: pick the code that matches the outcome - 201 for a create, 200 for a read, 400 for a bad request - because clients depend on it even when the body is fine.

Want to try it hands-on? HeyDevJob gives you this exact setup in a live cloud workspace in your browser - edit it, run it, and see it work. Free, nothing to install.

Try it in a workspace →

What you'll practice

FAQ

What status code should a POST endpoint return when it creates a resource?

201 Created is the correct code for a successful create. It signals that a new resource now exists, unlike a generic 200 OK which is meant for reads and updates. A create response often also carries a Location header pointing at the new resource.

How do I return a 201 status code in Flask?

Return a tuple where the second element is the code: return jsonify(todo), 201. Without that second value Flask defaults to 200, so the status must be set explicitly on every create branch.

What is the difference between 200 and 201?

200 OK is a generic success used for reads and updates. 201 Created specifically means a new resource was created by the request, typically from a POST. Clients and tests branch on the difference, so a create should return 201 even though the body may look identical to a 200.

When should a REST API return a 400 bad request instead of 201?

A REST API returns 400 Bad Request when the client sends invalid input - a missing required field, malformed JSON, or a bad value - because that is a 4xx client error. It returns 201 Created only when the request was valid and a new resource was actually created. Validate the input first, then create and return 201.

Keep learning

Return 400 Instead of 500 on Bad InputFullstack projectReturn the Correct HTTP Status CodeFullstack projectFix a CORS Error in a Web AppFullstack projectFullstack roadmapStep by step to hiredFullstack interview questionsSTAR answersAll Fullstack projectsProjects hub

Learn it by doing. Open this in a live cloud workspace, make the change yourself, and keep a record of the work you can share.

Open the workspace →