Skip to main content
Flask provides built-in support for JSON serialization and deserialization with customizable behavior.

JSON Serialization

jsonify

jsonify(*args: Any, **kwargs: Any) -> Response
Serialize the given arguments as JSON and return a Response object with the application/json mimetype. This requires an active app context and calls app.json.response().
args
Any
A single value to serialize, or multiple values to treat as a list to serialize.
kwargs
Any
Treat as a dict to serialize.
Either positional or keyword arguments can be given, not both. If no arguments are given, None is serialized.
Returns: A Response object with JSON content and application/json mimetype. Example
from flask import jsonify

@app.route('/api/user/<int:id>')
def get_user(id):
    user = User.query.get_or_404(id)
    return jsonify(
        id=user.id,
        username=user.username,
        email=user.email
    )
    # Response: {"id": 1, "username": "john", "email": "[email protected]"}

@app.route('/api/users')
def get_users():
    users = User.query.all()
    return jsonify([u.to_dict() for u in users])
    # Response: [{...}, {...}, ...]

@app.route('/api/status')
def status():
    return jsonify({'status': 'ok', 'version': '1.0.0'})

# Multiple values as list
@app.route('/api/items')
def items():
    return jsonify('item1', 'item2', 'item3')
    # Response: ["item1", "item2", "item3"]
In debug mode, the output is formatted with indentation to make it easier to read.

dumps

dumps(obj: Any, **kwargs: Any) -> str
Serialize data as JSON to a string. If current_app is available, it will use its app.json.dumps() method, otherwise it will use json.dumps.
obj
Any
required
The data to serialize.
kwargs
Any
Arguments passed to the dumps implementation.
Returns: JSON string representation of the object. Example
from flask import json

data = {'name': 'John', 'age': 30, 'active': True}
json_string = json.dumps(data)
# Result: '{"name":"John","age":30,"active":true}'

# With custom options
json_string = json.dumps(data, indent=2, sort_keys=True)
# Result:
# {
#   "active": true,
#   "age": 30,
#   "name": "John"
# }

# Serialize Decimal
from decimal import Decimal
data = {'price': Decimal('19.99')}
json_string = json.dumps(data)
# Result: '{"price":"19.99"}'

dump

dump(obj: Any, fp: IO[str], **kwargs: Any) -> None
Serialize data as JSON and write to a file. If current_app is available, it will use its app.json.dump() method, otherwise it will use json.dump.
obj
Any
required
The data to serialize.
fp
IO[str]
required
A file opened for writing text. Should use the UTF-8 encoding to be valid JSON.
kwargs
Any
Arguments passed to the dump implementation.
Example
from flask import json

data = {
    'users': [
        {'id': 1, 'name': 'John'},
        {'id': 2, 'name': 'Jane'}
    ]
}

with open('data.json', 'w') as f:
    json.dump(data, f, indent=2)

# With custom serialization
with open('config.json', 'w', encoding='utf-8') as f:
    json.dump(config, f, ensure_ascii=False, indent=2)

JSON Deserialization

loads

loads(s: str | bytes, **kwargs: Any) -> Any
Deserialize data as JSON from a string or bytes. If current_app is available, it will use its app.json.loads() method, otherwise it will use json.loads.
s
str | bytes
required
Text or UTF-8 bytes to deserialize.
kwargs
Any
Arguments passed to the loads implementation.
Returns: The deserialized Python object. Example
from flask import json

json_string = '{"name":"John","age":30}'
data = json.loads(json_string)
# Result: {'name': 'John', 'age': 30}

# From bytes
json_bytes = b'{"active":true}'
data = json.loads(json_bytes)
# Result: {'active': True}

# From request data
@app.route('/api/data', methods=['POST'])
def receive_data():
    data = json.loads(request.data)
    process_data(data)
    return jsonify({'status': 'success'})

load

load(fp: IO[AnyStr], **kwargs: Any) -> Any
Deserialize data as JSON read from a file. If current_app is available, it will use its app.json.load() method, otherwise it will use json.load.
fp
IO[AnyStr]
required
A file opened for reading text or UTF-8 bytes.
kwargs
Any
Arguments passed to the load implementation.
Returns: The deserialized Python object. Example
from flask import json

# Load from file
with open('data.json', 'r') as f:
    data = json.load(f)

# Load configuration
with open('config.json', 'r', encoding='utf-8') as f:
    config = json.load(f)
    app.config.update(config)

# Load from uploaded file
@app.route('/upload', methods=['POST'])
def upload():
    file = request.files['data']
    data = json.load(file.stream)
    return jsonify({'received': len(data)})

Custom JSON Provider

Flask allows you to customize JSON serialization behavior by overriding the default JSON provider.

Default JSON Provider

from flask.json.provider import DefaultJSONProvider

class CustomJSONProvider(DefaultJSONProvider):
    def dumps(self, obj, **kwargs):
        # Custom serialization logic
        return super().dumps(obj, **kwargs)
    
    def loads(self, s, **kwargs):
        # Custom deserialization logic
        return super().loads(s, **kwargs)

app.json = CustomJSONProvider(app)
Example: Custom Serialization
from flask.json.provider import DefaultJSONProvider
from datetime import datetime
from decimal import Decimal
import dataclasses

class CustomJSONProvider(DefaultJSONProvider):
    def default(self, obj):
        # Handle datetime objects
        if isinstance(obj, datetime):
            return obj.isoformat()
        
        # Handle Decimal objects
        if isinstance(obj, Decimal):
            return float(obj)
        
        # Handle dataclasses
        if dataclasses.is_dataclass(obj):
            return dataclasses.asdict(obj)
        
        # Fallback to default behavior
        return super().default(obj)

app.json = CustomJSONProvider(app)

# Now you can serialize these types
@app.route('/api/order/<int:id>')
def get_order(id):
    order = Order.query.get_or_404(id)
    return jsonify(
        id=order.id,
        created_at=order.created_at,  # datetime object
        total=order.total,  # Decimal object
        items=[item for item in order.items]  # dataclass objects
    )
Example: Compact JSON
from flask.json.provider import DefaultJSONProvider

class CompactJSONProvider(DefaultJSONProvider):
    def dumps(self, obj, **kwargs):
        # Remove all whitespace for smaller response size
        kwargs.setdefault('separators', (',', ':'))
        return super().dumps(obj, **kwargs)

app.json = CompactJSONProvider(app)
Example: Pretty JSON in Debug
from flask.json.provider import DefaultJSONProvider

class PrettyJSONProvider(DefaultJSONProvider):
    def dumps(self, obj, **kwargs):
        if self.app.debug:
            kwargs.setdefault('indent', 2)
            kwargs.setdefault('sort_keys', True)
        return super().dumps(obj, **kwargs)

app.json = PrettyJSONProvider(app)

Request JSON Data

Access JSON data from incoming requests:
@app.route('/api/create', methods=['POST'])
def create():
    # Get JSON data from request body
    data = request.get_json()
    
    # With error handling
    if not request.is_json:
        return jsonify({'error': 'Content-Type must be application/json'}), 400
    
    data = request.get_json()
    if data is None:
        return jsonify({'error': 'Invalid JSON'}), 400
    
    # Access specific fields
    name = data.get('name')
    email = data.get('email')
    
    # Create resource
    user = User(name=name, email=email)
    db.session.add(user)
    db.session.commit()
    
    return jsonify(user.to_dict()), 201

# Force JSON parsing even if Content-Type is wrong
@app.route('/api/data', methods=['POST'])
def receive_data():
    data = request.get_json(force=True)
    return jsonify({'received': data})

Error Handling

Return JSON error responses:
from flask import jsonify

@app.errorhandler(404)
def not_found(error):
    return jsonify({
        'error': 'Not Found',
        'message': str(error)
    }), 404

@app.errorhandler(400)
def bad_request(error):
    return jsonify({
        'error': 'Bad Request',
        'message': str(error)
    }), 400

@app.errorhandler(500)
def internal_error(error):
    return jsonify({
        'error': 'Internal Server Error',
        'message': 'An unexpected error occurred'
    }), 500

# Custom error response
@app.route('/api/user/<int:id>')
def get_user(id):
    user = User.query.get(id)
    if user is None:
        return jsonify({
            'error': 'User not found',
            'user_id': id
        }), 404
    return jsonify(user.to_dict())

Best Practices

Always validate JSON input before processing it. Never trust user input.
Recommendations:
  1. Use jsonify for responses - It automatically sets the correct content type
  2. Validate input - Check required fields and data types
  3. Handle errors gracefully - Return meaningful error messages
  4. Use custom providers - Customize serialization for your needs
  5. Set proper status codes - Use appropriate HTTP status codes with JSON responses
  6. Consider performance - Use compact JSON for production, pretty JSON for development
from flask import jsonify, request
from marshmallow import Schema, fields, ValidationError

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

@app.route('/api/users', methods=['POST'])
def create_user():
    # Validate content type
    if not request.is_json:
        return jsonify({'error': 'Content-Type must be application/json'}), 415
    
    # Parse JSON
    try:
        data = request.get_json()
    except Exception as e:
        return jsonify({'error': 'Invalid JSON', 'message': str(e)}), 400
    
    # Validate schema
    schema = UserSchema()
    try:
        validated_data = schema.load(data)
    except ValidationError as e:
        return jsonify({'error': 'Validation failed', 'details': e.messages}), 400
    
    # Create user
    user = User(**validated_data)
    db.session.add(user)
    db.session.commit()
    
    return jsonify(user.to_dict()), 201

Build docs developers (and LLMs) love