Flask Cheatsheet

JSON APIs

Use this Flask reference while you build software engineering projects, review code for technical interview prep, or polish examples for a software engineer resume.

Returning JSON

from flask import Flask, jsonify, request

app = Flask(__name__)

@app.route("/api/items")
def list_items():
    items = [{"id": 1, "name": "Widget"}, {"id": 2, "name": "Gadget"}]
    return jsonify(items)                 # 200 application/json

@app.route("/api/items/<int:item_id>")
def get_item(item_id):
    item = find_item(item_id)
    if item is None:
        return jsonify({"error": "Not found"}), 404
    return jsonify(item)

Flask 2.2+ auto-serializes dict and list returns:

@app.get("/api/user")
def get_user():
    return {"id": 1, "name": "Alice"}    # Content-Type: application/json

@app.get("/api/users")
def get_users():
    return [{"id": 1}, {"id": 2}]        # list also works

Receiving JSON

@app.post("/api/items")
def create_item():
    data = request.get_json()
    if data is None:
        return {"error": "JSON body required"}, 400

    name = data.get("name")
    if not name:
        return {"error": "name is required"}, 422

    item = {"id": 99, "name": name}
    return jsonify(item), 201

get_json() options:

OptionDefaultEffect
force=FalseFalseParse even without Content-Type: application/json
silent=FalseFalseReturn None on parse error instead of raising 400
cache=TrueTrueCache parsed result on request object

Standard API Response Patterns

def success(data, status=200):
    return jsonify({"data": data, "ok": True}), status

def error(message, status=400, details=None):
    body = {"ok": False, "error": message}
    if details:
        body["details"] = details
    return jsonify(body), status

@app.get("/api/users/<int:id>")
def get_user(id):
    user = db.session.get(User, id)
    if not user:
        return error("User not found", 404)
    return success(user.to_dict())

REST CRUD Example

items = {}

@app.get("/api/items")
def list_items():
    return jsonify(list(items.values()))

@app.post("/api/items")
def create_item():
    data = request.get_json(silent=True) or {}
    if "name" not in data:
        return {"error": "name required"}, 422
    new_id = max(items, default=0) + 1
    items[new_id] = {"id": new_id, "name": data["name"]}
    return jsonify(items[new_id]), 201, {"Location": f"/api/items/{new_id}"}

@app.get("/api/items/<int:item_id>")
def get_item(item_id):
    item = items.get(item_id)
    return jsonify(item) if item else ({"error": "not found"}, 404)

@app.put("/api/items/<int:item_id>")
def replace_item(item_id):
    if item_id not in items:
        return {"error": "not found"}, 404
    data = request.get_json(silent=True) or {}
    items[item_id] = {"id": item_id, "name": data.get("name", "")}
    return jsonify(items[item_id])

@app.patch("/api/items/<int:item_id>")
def update_item(item_id):
    if item_id not in items:
        return {"error": "not found"}, 404
    data = request.get_json(silent=True) or {}
    items[item_id].update({k: v for k, v in data.items() if k != "id"})
    return jsonify(items[item_id])

@app.delete("/api/items/<int:item_id>")
def delete_item(item_id):
    items.pop(item_id, None)
    return "", 204

Request Validation

Manual validation pattern:

def validate(data: dict, schema: dict) -> list[str]:
    """schema: {field: type}"""
    errors = []
    for key, typ in schema.items():
        if key not in data:
            errors.append(f"{key} is required")
        elif not isinstance(data[key], typ):
            errors.append(f"{key} must be {typ.__name__}")
    return errors

@app.post("/api/users")
def create_user():
    data = request.get_json(silent=True) or {}
    errors = validate(data, {"username": str, "age": int})
    if errors:
        return {"errors": errors}, 422
    # proceed

With marshmallow:

pip install marshmallow
from marshmallow import Schema, fields, ValidationError

class UserSchema(Schema):
    username = fields.Str(required=True)
    email = fields.Email(required=True)
    age = fields.Int(load_default=None)

schema = UserSchema()

@app.post("/api/users")
def create_user():
    try:
        data = schema.load(request.get_json(silent=True) or {})
    except ValidationError as err:
        return {"errors": err.messages}, 422
    return {"id": 1, **data}, 201

Error Handling for APIs

from flask import jsonify
from werkzeug.exceptions import HTTPException

@app.errorhandler(HTTPException)
def http_error(e):
    return jsonify({"error": e.name, "description": e.description}), e.code

@app.errorhandler(404)
def not_found(e):
    return jsonify({"error": "Not Found"}), 404

@app.errorhandler(422)
def unprocessable(e):
    return jsonify({"error": "Unprocessable Entity"}), 422

@app.errorhandler(Exception)
def server_error(e):
    app.logger.exception(e)
    return jsonify({"error": "Internal Server Error"}), 500

Content Negotiation

@app.route("/api/report")
def report():
    data = {"rows": 100, "status": "done"}
    best = request.accept_mimetypes.best_match(["application/json", "text/csv"])
    if best == "text/csv":
        csv = "rows,status\n100,done"
        return csv, 200, {"Content-Type": "text/csv"}
    return jsonify(data)

Pagination Pattern

Flask-SQLAlchemy 3.x style — db.paginate() with a select() statement (the legacy Model.query.paginate() still exists but Model.query is deprecated):

from sqlalchemy import select

@app.get("/api/posts")
def list_posts():
    page = request.args.get("page", 1, type=int)
    per_page = min(request.args.get("per_page", 20, type=int), 100)

    pagination = db.paginate(
        select(Post).order_by(Post.created_at.desc()),
        page=page, per_page=per_page, error_out=False,
    )
    return jsonify({
        "items": [p.to_dict() for p in pagination.items],
        "total": pagination.total,
        "page": pagination.page,
        "pages": pagination.pages,
        "has_next": pagination.has_next,
        "has_prev": pagination.has_prev,
    })

Authentication Headers

import functools

def require_api_key(f):
    @functools.wraps(f)
    def decorated(*args, **kwargs):
        key = request.headers.get("X-Api-Key")
        if key != app.config["API_KEY"]:
            return jsonify({"error": "Unauthorized"}), 401
        return f(*args, **kwargs)
    return decorated

@app.get("/api/secret")
@require_api_key
def secret():
    return {"data": "top secret"}

JWT bearer token check:

def get_token():
    auth = request.headers.get("Authorization", "")
    if auth.startswith("Bearer "):
        return auth[7:]
    return None

JSON Configuration

Flask 2.3 removed the JSON_SORT_KEYS / JSONIFY_PRETTYPRINT_REGULAR / JSONIFY_MIMETYPE config keys. Configure the JSON provider on app.json instead:

app.json.sort_keys = False              # preserve insertion order (default True)
app.json.compact = False                # pretty-print; None = compact unless debug
app.json.mimetype = "application/json"  # response Content-Type
app.json.ensure_ascii = False           # emit UTF-8 instead of \uXXXX escapes

Custom serialization (e.g., Decimal, dataclasses) via a custom provider:

from decimal import Decimal
from flask.json.provider import DefaultJSONProvider

class MyJSONProvider(DefaultJSONProvider):
    @staticmethod
    def default(o):
        if isinstance(o, Decimal):
            return str(o)
        return DefaultJSONProvider.default(o)

app.json = MyJSONProvider(app)

Async Views (Flask 2+)

import asyncio
import aiohttp

@app.get("/api/async-data")
async def async_data():
    async with aiohttp.ClientSession() as session:
        async with session.get("https://api.example.com/data") as resp:
            data = await resp.json()
    return jsonify(data)

Requires pip install "flask[async]" (installs asgiref).

flask-restful (Extension)

Maintenance mode. flask-restful accepts only critical fixes, and its own docs deprecate reqparse (recommending marshmallow instead). For new APIs prefer plain Flask views + marshmallow/pydantic, or actively maintained alternatives (flask-smorest, APIFlask). Shown here because it is still common in existing codebases.

pip install flask-restful
from flask_restful import Api, Resource

api = Api(app)

class UserResource(Resource):
    def get(self, user_id):
        return {"id": user_id, "name": "Alice"}

    def post(self):
        data = request.get_json(silent=True) or {}
        if "name" not in data:
            return {"error": "name is required"}, 422
        return {"name": data["name"]}, 201

    def delete(self, user_id):
        return "", 204

api.add_resource(UserResource, "/users", "/users/<int:user_id>")