Documents

A value must be a mapping — a dict, a dataclass instance, or a model. BSON has no other top-level form, and neither does MonaDB.

c["k"] = {"n": 1}               # fine
c["k"] = [1, 2, 3]              # TypeError: document must be a mapping
c["k"] = "not a mapping"        # TypeError

Lists and scalars are welcome inside a document, just not as the whole of one.

Types

PythonBSONNotes
NoneNull
boolBoolean
intInt32 / Int64narrowed automatically; beyond Int64 raises ValueError
floatDouble
strString
bytesBinarygeneric subtype
datetimeUTC datetimemillisecond precision — see below
listArray
dictDocumentnests to any depth

Anything else raises TypeError, and the message names where in the document the offending value sits:

c["k"] = {"a": {"b": [1, {1, 2}]}}
# TypeError: unsupported type set at $.a.b[1]

Nesting

Documents nest freely, and round-trip exactly:

c["k"] = {
    "user": {"name": "alice", "tags": ["a", "b"]},
    "counts": [1, 2, [3, {"deep": True}]],
    "blob": b"\x00\xff",
}
c["k"] == ...                   # identical structure back

Datetimes

Two BSON properties show through, and it is better to know them than to discover them:

Millisecond precision. Microseconds are truncated.

from datetime import datetime, timezone

c["k"] = {"at": datetime(2026, 8, 2, 12, 0, 0, 123456, tzinfo=timezone.utc)}
c["k"]["at"].microsecond        # 123000, not 123456

Naive datetimes are treated as UTC. Reads always return tz-aware UTC.

c["k"] = {"at": datetime(2026, 8, 2, 12, 0, 0)}     # naive
c["k"]["at"]
# datetime.datetime(2026, 8, 2, 12, 0, tzinfo=datetime.timezone.utc)

If you care about a local wall-clock time, attach the zone yourself before storing it.

Integers

Integers are stored as Int32 when they fit and Int64 otherwise. That is a storage detail — you always read back a Python int. Beyond 64 bits it is an error, not a silent truncation:

c["k"] = {"n": 2**63}           # ValueError: int out of 64-bit range at $.n

Why BSON

BSON keeps datetime and bytes as first-class types, which JSON cannot, and its files are readable from any language with a BSON library. The alternative considered was msgpack, which is smaller but weaker on exactly those two types.