Documents
All documents are mappings such as python dicts, dataclasses, or pyndantic models.
["k"] = {"n": 1} # fine
["k"] = [1, 2, 3] # TypeError: document must be a mapping
["k"] = "not a mapping" # TypeErrorTypes
| Python | BSON | Notes |
|---|---|---|
None | Null | |
bool | Boolean | |
int | Int32 / Int64 | narrowed automatically; beyond Int64 raises ValueError |
float | Double | |
str | String | |
bytes | Binary | generic subtype |
datetime | UTC datetime | millisecond precision (see below) |
list | Array | |
dict | Document | nests to any depth |
Anything else raises TypeError, and the message names where in the document
the offending value sits. This example has a python set which is not supported.
["k"] = {"a": {"b": [1, {1, 2}]}}
# TypeError: unsupported type set at $.a.b[1]Datetimes
Millisecond precision. Microseconds are truncated.
from datetime import datetime, timezone
# Insert a time with microsecond precision
["times"] = {"at":(2026, 8, 2, 12, 0, 0, 123456, tzinfo=.)}
# Returns a time with millisecond precision: 123000, not 123456
["times"]["at"].microsecond
# Naive datetimes are treated as UTC
["naive"] = {"at":(2026, 8, 2, 12, 0, 0)}
# Returns datetime.datetime(2026, 8, 2, 12, 0, tzinfo=datetime.timezone.utc)
["naive"]["at"]
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:
["k"] = {"n": 2**63} # ValueError: int out of 64-bit range at $.nWhy 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.