Responses and JSON API
Introduction
View functions return strings, tuples, dicts, or Response objects. Flask sets status codes and headers accordingly. This chapter covers HTML and JSON responses, jsonify, make_response, common HTTP status codes, and a simple {code, message, data} API envelope teams often adopt.
Prerequisites
- The Request Object
- Basic JSON syntax
String and Tuple Responses
Code explanation:
(body, status)tuple sets status without custom headers(body, status, headers_dict)third element adds headers
HTML Responses
@app.route("/page")
def page():
return "<h1>Hello</h1>", 200, {"Content-Type": "text/html; charset=utf-8"}Prefer templates for real pages—chapter 8.
jsonify and Dict Returns
from flask import Flask, jsonify
app = Flask(__name__)
@app.route("/api/user/1")
def get_user():
return jsonify({"id": 1, "name": "Alice"})
@app.route("/api/status")
def status():
return {"status": "ok", "version": 1}Both produce application/json.
Custom status with jsonify:
return jsonify({"error": "Not found"}), 404make_response
Full control over body, status, headers, cookies:
from flask import Flask, make_response
app = Flask(__name__)
@app.route("/set-theme")
def set_theme():
resp = make_response({"theme": "dark"})
resp.set_cookie("theme", "dark", httponly=True, samesite="Lax")
resp.headers["X-App-Version"] = "1.0"
return respClear cookie:
resp.set_cookie("theme", "", expires=0)Common HTTP Status Codes
| Code | Meaning | Typical use |
|---|---|---|
| 200 | OK | Successful GET |
| 201 | Created | POST created resource |
| 204 | No Content | DELETE success |
| 400 | Bad Request | Validation failed |
| 401 | Unauthorized | Missing/invalid auth |
| 403 | Forbidden | Authenticated but not allowed |
| 404 | Not Found | Unknown route or resource |
| 500 | Server Error | Unhandled exception |
Example validation:
from flask import Flask, request, jsonify
app = Flask(__name__)
@app.route("/api/items", methods=["POST"])
def create_item():
data = request.get_json(silent=True) or {}
name = data.get("name", "").strip()
if not name:
return jsonify({"error": "name is required"}), 400
return jsonify({"id": 1, "name": name}), 201Unified API Envelope
Team convention:
def api_ok(data=None, message="success", code=0):
return jsonify({"code": code, "message": message, "data": data})
def api_error(message, http_status=400, code=1):
return jsonify({"code": code, "message": message, "data": None}), http_status
@app.route("/api/profile")
def profile():
return api_ok({"name": "Alice"})Clients always parse code, message, data—business errors may use code != 0 with HTTP 200 (document team rules clearly).
Tip
Pick One Error Style
Mixing raw HTTP errors and envelope errors confuses API clients—standardize in project docs.
Content Negotiation (Brief)
Same route, different format:
from flask import Flask, request, jsonify
app = Flask(__name__)
@app.route("/resource/1")
def resource():
if request.accept_mimes.accept_json:
return jsonify({"id": 1})
return "<p>Resource 1</p>", 200, {"Content-Type": "text/html"}Most APIs are JSON-only.
Error Handlers for APIs
@app.errorhandler(404)
def api_not_found(e):
if request.path.startswith("/api/"):
return jsonify({"error": "not found"}), 404
return "Not found", 404FAQ
jsonify vs return dict?
Equivalent for JSON in modern Flask—jsonify adds explicit JSON response type.
Chinese or emoji in JSON?
Return str; Flask JSON encoder handles UTF-8; set JSON_AS_ASCII = False in config if needed.
CORS errors in browser?
Browser blocks cross-origin—use Flask-CORS extension for API projects.
Return list at top level?
Valid JSON—return [{"id": 1}] works.
201 without body?
return "", 201 or return jsonify({}), 201.
Pretty-print JSON in dev?
app.config["JSONIFY_PRETTYPRINT_REGULAR"] = True—disable in production.