DynamoDB Data Types: A Complete Reference

Every DynamoDB data type — scalars (S, N, B, BOOL, NULL), documents (M, L) and sets (SS, NS, BS) — how they're stored, sorted, and when to use each.

5 min read· Updated Jun 15, 2026
On this page

DynamoDB has a small but precise set of attribute types, and knowing exactly how each is stored, sorted, and constrained will save you from subtle bugs — empty-set rejections, lexicographic number sorting, and oversized items among them.

This guide is a complete reference to every type, grouped into scalars, documents, and sets.

The type system at a glance

Every attribute value in DynamoDB is tagged with a one- or two-letter type descriptor. You see these directly when working with the low-level API or BatchWriteItem JSON:

CategoryTypeDescriptorHolds
ScalarStringSUTF-8 text
ScalarNumberNup to 38 digits of precision
ScalarBinaryBraw bytes (base64 on the wire)
ScalarBooleanBOOLtrue / false
ScalarNullNULLa typed null marker
DocumentMapMnested key-value object
DocumentListLordered, mixed-type array
SetString SetSSunique strings
SetNumber SetNSunique numbers
SetBinary SetBSunique byte values

High-level SDKs (like boto3’s resource interface or the AWS SDK Document clients) hide these descriptors and map them to native language types, but the underlying storage rules still apply.

Scalar types

String (S)

UTF-8 encoded text. Strings can be empty ("") as non-key attributes, but a key attribute cannot be empty. Maximum length is bounded only by the 400 KB item size limit.

Strings sort lexicographically by UTF-8 byte value, which is why they’re so useful as sort keys when formatted carefully:

  • ISO-8601 timestamps (2026-06-16T09:30:00Z) sort chronologically.
  • Zero-padded numbers ("00042") sort numerically.
  • Mixed-case sorts by byte: uppercase A (0x41) precedes lowercase a (0x61).

Number (N)

A single numeric type covering integers and decimals, with up to 38 digits of precision. There is no separate int/float/decimal — N is variable-length and decimal-based, so you avoid floating-point rounding. On the wire it’s transmitted as a string to preserve precision:

{ "price": { "N": "19.99" }, "quantity": { "N": "3" } }

Numbers sort numerically in keys and indexes. Use N for anything you’ll do arithmetic on or range-query as a true number. Note that values are normalized: trailing zeros after the decimal are trimmed (1.50 stored as 1.5).

Binary (B)

Arbitrary bytes — compressed blobs, encrypted payloads, thumbnails. You supply base64-encoded data over the wire; DynamoDB stores the raw bytes and counts the decoded size against the 400 KB limit. Binary values sort by unsigned byte order.

Boolean (BOOL)

A simple true / false. Stored compactly; can be used in expressions with comparisons and as a filter.

Null (NULL)

A typed marker meaning “this attribute exists and is explicitly null,” distinct from the attribute simply being absent. In most data models you should omit an attribute rather than store NULL, because absent attributes are free and sparse — and sparse attributes enable sparse-index patterns. Reach for NULL only when “explicitly empty” is semantically different from “missing.”

Document types

Document types let you nest structure inside a single item. They can be arbitrarily deep (up to a 32-level nesting limit), and the entire item still must fit within 400 KB.

Map (M)

An unordered collection of key-value pairs — essentially a nested object. Values can be any type, including other maps and lists.

{
  "address": { "M": {
    "city":  { "S": "Lisbon" },
    "zip":   { "S": "1100-001" },
    "geo":   { "M": { "lat": { "N": "38.72" }, "lng": { "N": "-9.13" } } }
  }}
}

You can read and update nested attributes directly with document paths in expressions, e.g. SET address.city = :c, without rewriting the whole map. See expressions for path syntax.

List (L)

An ordered array that can hold mixed types and duplicates:

{ "tags": { "L": [ { "S": "urgent" }, { "N": "5" }, { "BOOL": true } ] } }

You can append to a list atomically with list_append in an UpdateExpression, and address elements by index (tags[0]). Lists preserve order, which sets do not.

Set types

Sets hold unique values of a single scalar type. They’re unordered and enforce uniqueness automatically.

TypeDescriptorElement type
String SetSSstrings
Number SetNSnumbers
Binary SetBSbinary
{ "roles": { "SS": ["admin", "editor", "viewer"] } }

The big advantage of sets is atomic membership operations. ADD inserts elements (no-op if already present) and DELETE removes them, both atomically and idempotently — no read-modify-write race:

table.update_item(
    Key={"userId": "U#1"},
    UpdateExpression="ADD roles :r",
    ExpressionAttributeValues={":r": {"newrole"}},
)

Critical constraints:

  • Sets cannot be empty. DynamoDB rejects an empty set; if a DELETE would empty it, the attribute is removed entirely.
  • Single type only. You can’t mix strings and numbers in one set — use a List for that.
  • No ordering. Don’t rely on element order; use a List if order matters.

Set vs List: choosing correctly

List (L)Set (SS/NS/BS)
Orderpreservednone
Duplicatesallowedrejected
Mixed typesyesno
Atomic add/removeappend onlyADD / DELETE
Can be emptyyesno

Use a Set for tags, roles, or unique IDs where you do membership math. Use a List for ordered sequences, mixed-type collections, or anything that can contain repeats.

Types and primary keys

Key attributes (partition and sort keys) must be scalar — only S, N, or B are allowed. You cannot key on a Boolean, set, document type, or null. This is covered in detail in primary keys, but it’s worth remembering when you design an item: the attributes you query by must be plain scalars.

Item size and the 400 KB ceiling

The total size of an item — all attribute names plus all values, including nested structure — must stay under 400 KB. Names count too, so terse attribute names save real bytes at scale. For large blobs, store the object in S3 and keep only a pointer (a Binary or String reference) in DynamoDB.

Inspecting types in real data

Because high-level SDKs hide the type descriptors, it’s easy to lose track of whether a field is stored as N or S — a difference that silently breaks sort order and range queries. Tablyne, a native DynamoDB GUI, shows each attribute’s exact type, renders nested maps and lists as a tree, and flags items approaching the 400 KB limit, which makes type mismatches visible before they bite a query.

Master these types and you’ll model data that’s compact, correctly sorted, and cheap to query — and you’ll never be surprised by an empty-set error again.

Frequently asked questions

Why does DynamoDB store numbers as strings?

Internally DynamoDB stores Number (N) values as variable-length, sign-and-magnitude decimal, and the low-level wire format represents them as strings to preserve up to 38 digits of precision without floating-point rounding errors.

Can a DynamoDB set be empty?

No. Sets (SS, NS, BS) cannot be empty — DynamoDB rejects an empty set. If a set would become empty, remove the attribute instead. Empty strings and empty lists/maps, however, are allowed.

What's the difference between a List and a Set in DynamoDB?

A List (L) is ordered, can hold mixed types, and allows duplicates. A Set (SS/NS/BS) is unordered, must hold a single type, and enforces uniqueness. Use a Set when you need uniqueness and atomic ADD/DELETE operations.

Keep reading