APIs and pages both need the right status codes and headers, not just a body.
Flask converts whatever a view returns into a Response:
- a string becomes an HTML response with status 200
- a dict or list is serialized to JSON with
Content-Type: application/json - a tuple adds metadata:
(body, status),(body, headers)or(body, status, headers) - a Response object is sent as is
So return {"error": "not found"}, 404 is a complete JSON error response.
jsonify(...) builds a JSON response explicitly and is still handy for things returned from helpers. Flask's JSON provider handles datetime (as an HTTP date string), UUID, dataclasses and Decimal; other types need a custom provider or converting first.
When you need to modify the response object, for example to set a cookie, create it with make_response(...):
resp = make_response(render_template("home.html"))
resp.set_cookie("theme", "dark", httponly=True)redirect(url) returns a 302 by default; pass code=303 after a POST or 301/308 for permanent moves. For files, send_file and send_from_directory stream a file with the right headers; the latter refuses paths that escape the directory, so use it for anything that comes from user input.
Use status codes deliberately: 201 Created for a new resource, 204 No Content for an empty success, 400 for bad input, 404 for missing, 409 for conflicts.
Example
from flask import Flask, make_response
app = Flask(__name__)
USERS = {1: "ann"}
@app.get("/users/<int:uid>")
def get_user(uid):
if uid not in USERS:
return {"error": "user not found"}, 404
return {"id": uid, "name": USERS[uid]}
@app.get("/greet")
def greet():
resp = make_response("hello")
resp.headers["X-Hello"] = "world"
resp.set_cookie("seen", "1", httponly=True)
return resp
c = app.test_client()
r = c.get("/users/2"); print(r.status_code, r.json, r.content_type)
# 404 {'error': 'user not found'} application/json
r = c.get("/greet"); print(r.headers["X-Hello"], r.headers["Set-Cookie"])
# world seen=1; HttpOnly; Path=/Remember
- str -> HTML, dict/list -> JSON, tuple adds status and headers
- make_response() when you need to set cookies or headers on the object
- redirect() defaults to 302; choose codes deliberately
Watch out for
- Returning 200 with an error message in the body instead of a proper 4xx status.