Python

Python json.dumps() TypeError: Object of Type datetime Is Not JSON Serializable

Python's built-in json module has no built-in support for serializing datetime objects (or several other common Python types) — JSON itself has no native date/time type, so Python correctly refuses to guess how you want it represented, rather than silently picking a format that might not match what your application actually needs.

The Problem

Serializing a dictionary or object containing a datetime value fails:

TypeError: Object of type datetime is not JSON serializable

Similar errors occur for other types the json module doesn't natively understand, like Decimal, set, or custom class instances:

TypeError: Object of type Decimal is not JSON serializable
TypeError: Object of type set is not JSON serializable

Why It Happens

The JSON specification itself only defines a small set of native types: strings, numbers, booleans, null, arrays, and objects — there's no standard JSON representation for dates, decimals, sets, or arbitrary Python objects. Python's json.dumps() only knows how to convert Python's own built-in types that have an obvious JSON equivalent (str, int, float, bool, None, list, dict), and raises this error for anything else, since there's no single universally correct way to represent these other types as JSON.

The Fix

Provide a custom default function to json.dumps(), which is called specifically for any object it doesn't know how to serialize natively, letting you define the conversion yourself:

import json
from datetime import datetime

def json_serial(obj):
    if isinstance(obj, datetime):
        return obj.isoformat()
    raise TypeError(f"Type {type(obj)} is not JSON serializable")

data = {"created_at": datetime.now(), "name": "example"}
json.dumps(data, default=json_serial)

obj.isoformat() produces a standard, widely-recognized ISO 8601 string representation (like "2026-07-28T14:30:00"), which is the most common and interoperable convention for representing dates and times in JSON, since most languages and libraries can parse this format back into their own native date/time types without ambiguity.

Extend the default function to handle multiple different non-serializable types your application commonly needs to serialize, rather than writing a separate one-off function for each:

from decimal import Decimal

def json_serial(obj):
    if isinstance(obj, datetime):
        return obj.isoformat()
    if isinstance(obj, Decimal):
        return float(obj)
    if isinstance(obj, set):
        return list(obj)
    raise TypeError(f"Type {type(obj)} is not JSON serializable")

For a more reusable, object-oriented approach, especially if you're serializing frequently throughout a larger codebase, create a custom JSONEncoder subclass rather than passing a function every time:

class CustomJSONEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, datetime):
            return obj.isoformat()
        if isinstance(obj, Decimal):
            return float(obj)
        return super().default(obj)

json.dumps(data, cls=CustomJSONEncoder)

This pattern is particularly convenient in web frameworks, where you can often configure this custom encoder once globally, so every JSON response throughout the application automatically handles these common types without needing to remember to pass default= or cls= explicitly on every individual json.dumps() call.

If you're deserializing this JSON back into Python later and need actual datetime objects again (not just ISO strings), implement a corresponding custom decoder using object_hook, converting recognized ISO date strings back into datetime objects during parsing:

def datetime_parser(dct):
    for key, value in dct.items():
        if isinstance(value, str):
            try:
                dct[key] = datetime.fromisoformat(value)
            except ValueError:
                pass
    return dct

json.loads(json_string, object_hook=datetime_parser)

Be aware this specific approach attempts to parse every string value as a potential datetime, which can be imprecise if some legitimate string fields happen to also look like valid ISO dates — for stricter round-tripping, consider a more explicit schema-aware serialization library (like Pydantic) rather than this general-purpose heuristic approach, if type fidelity through a full serialize/deserialize cycle matters significantly for your application.

Still Not Working?

If you're using a web framework with its own built-in JSON response handling (Flask, FastAPI, Django), check whether that framework already provides its own datetime-aware JSON serialization mechanism you should be using instead of Python's raw json module directly — many frameworks handle common types like datetime automatically in their response serialization layer, and manually implementing your own custom encoder alongside an existing framework mechanism can lead to inconsistent behavior between different parts of your application if both systems are handling serialization in slightly different, uncoordinated ways.