TypeError: Object of type datetime is not JSON serializable
1 · The lesson
readWhat this error means
json.dumps (or json.dump) met a value it doesn't know how to write as JSON. The standard encoder handles exactly these types: dict, list, tuple, str, int, float, bool and None. Anything else — a datetime, a Decimal, a set, a numpy number, an instance of your own class — and it gives up. The type name in the message tells you which value it choked on; it's datetime here, but you'll see Decimal, set, int64, ndarray or your own class name just as often.
When you see it
Traceback (most recent call last): File "main.py", line 4, in <module> json.dumps(order) ~~~~~~~~~~^^^^^^^ File ".../json/__init__.py", line 235, in dumps return _default_encoder.encode(obj) ~~~~~~~~~~~~~~~~~~~~~~~^^^^^ File ".../json/encoder.py", line 202, in encode chunks = self.iterencode(o, _one_shot=True) File ".../json/encoder.py", line 263, in iterencode return _iterencode(o, 0) File ".../json/encoder.py", line 182, in default raise TypeError(f'Object of type {o.__class__.__name__} ' f'is not JSON serializable') TypeError: Object of type datetime is not JSON serializable
The same error with other types, last line only:
TypeError: Object of type Decimal is not JSON serializable TypeError: Object of type set is not JSON serializable TypeError: Object of type int64 is not JSON serializable TypeError: Object of type Order is not JSON serializable
Notice the traceback doesn't say which key held the bad value. In a big nested payload you have to hunt for it — print the dict and look for anything that isn't plain text, a number or a list.
Why it happens
JSON has no date type, no decimal type and no set type, so there's no single "correct" way to write them. Python refuses to guess. A date could reasonably be "2026-10-09T14:30:00", a Unix timestamp, or "09/10/2026", and picking the wrong one silently is worse than failing loudly.
import json from datetime import datetime order = {"id": 1042, "customer": "Ada", "placed_at": datetime(2026, 10, 9, 14, 30)} json.dumps(order) # TypeError: Object of type datetime is not JSON serializable
The usual suspects:
datetime/date— from a database row,datetime.now(), or a parsed timestamp.Decimal— money columns from PostgreSQL, MySQL or SQLAlchemy come back asDecimal.set— you deduplicated tags withset(...)and put the result straight in the payload.- numpy and pandas numbers —
df["qty"].sum()returnsnumpy.int64, notint. Oddly,numpy.float64does serialise, because it subclasses Python'sfloat;int64doesn't subclassint. - Your own classes —
jsonhas no idea what anOrderorUserobject should look like.
How to fix it
Convert at the source when there's only one or two fields. It's the most explicit option and the output format is exactly what you chose:
import json from datetime import datetime placed_at = datetime(2026, 10, 9, 14, 30) order = {"id": 1042, "customer": "Ada", "placed_at": placed_at.isoformat()} print(json.dumps(order)) # {"id": 1042, "customer": "Ada", "placed_at": "2026-10-09T14:30:00"}
ISO 8601 (isoformat()) is the format to pick for dates in JSON. Every language parses it, and it sorts correctly as a string.
default=str for logs and debugging. default is called for every value the encoder doesn't recognise. str is the lazy catch-all:
import json from datetime import datetime from decimal import Decimal invoice = {"number": "INV-2041", "total": Decimal("312.40"), "issued": datetime(2026, 10, 9, 9, 0)} print(json.dumps(invoice, default=str)) # {"number": "INV-2041", "total": "312.40", "issued": "2026-10-09 09:00:00"}
Fine for a log line. For an API I wouldn't ship it: str(datetime) puts a space where ISO 8601 wants a T, and anything unknown gets stringified without complaint — including objects you never meant to send.
A small default function for real payloads. Handle the types you expect and keep raising for the rest, so a stray object still fails loudly:
import json from datetime import date, datetime from decimal import Decimal def to_json(value): if isinstance(value, (datetime, date)): return value.isoformat() if isinstance(value, Decimal): return str(value) # keep every digit; float(value) can lose pennies if isinstance(value, set): return sorted(value) raise TypeError(f"Object of type {type(value).__name__} is not JSON serializable") order = { "id": 1042, "customer": "Grace", "placed_at": datetime(2026, 10, 9, 14, 30), "total": Decimal("89.99"), "tags": {"priority", "gift"}, } print(json.dumps(order, default=to_json, indent=2))
Sending money as a string surprises people, but it's the safe choice. float(Decimal("0.10")) + float(Decimal("0.20")) is 0.30000000000000004, and the client can parse a string back into an exact decimal.
numpy values: call .item() or .tolist(). .item() turns a numpy scalar into the matching Python type; .tolist() does the same for a whole array:
import json import numpy as np units = np.array([12, 40, 3]) report = {"total_units": units.sum().item(), "per_store": units.tolist()} print(json.dumps(report)) # {"total_units": 55, "per_store": [12, 40, 3]}
If the data is already in a DataFrame, skip json entirely: df.to_json(orient="records", date_format="iso") knows about numpy types and timestamps.
Your own classes: a dataclass and asdict. dataclasses.asdict converts the object (and nested dataclasses) to plain dicts. It leaves field values alone, so a datetime field still needs the default:
import json from dataclasses import dataclass, asdict from datetime import date @dataclass class Order: id: int customer: str placed_on: date order = Order(1042, "Hedy", date(2026, 10, 9)) print(json.dumps(asdict(order), default=lambda v: v.isoformat())) # {"id": 1042, "customer": "Hedy", "placed_on": "2026-10-09"}
For API models with validation, pydantic's model_dump_json() handles dates, decimals and nested models out of the box — worth it if you're already in FastAPI land, overkill for a script.
When you'd actually see this in real code
- Returning a database row from a Flask view: the
created_atcolumn is adatetime, andjsonifyorjson.dumpsrefuses it. - Writing a sales summary to JSON after
df.groupby(...).sum(): every number isnumpy.int64. - Caching a result in Redis with
json.dumps, where the result contains asetof user IDs. - Logging an object with
json.dumps(self.__dict__)and one attribute is aDecimalprice or a nested object.
Related errors
- json.JSONDecodeError: Expecting value — the reading side: the text you're parsing isn't valid JSON.
- TypeError: unhashable type: 'list' — the other set-related surprise, when you try to put a list inside one.
- AttributeError: 'dict' object has no attribute 'X' — after
json.loads, everything is a dict again.
See Also
- All Python errors — the full index, by type and by when it happens.
- CSV, JSON, and JSONL
- Dates, Times, and Timezones
- Dataclasses
- Consuming REST APIs
Found this useful? Share it with a friend who’s learning Python.