Intermediate13 min read

DynamoDB Limits and Quotas, Verified on the Live Service

DynamoDB's limits come in two kinds: fixed constraints the API enforces on every request, and default quotas AWS raises on request. This page probes the first kind against the live service and cites the second.

What are the DynamoDB limits?

An item is capped at 400 KB (409,600 bytes, attribute names included), a partition key at 2,048 bytes and a sort key at 1,024 bytes. A batch write takes 25 items, a batch get 100 keys, a transaction 100 actions and 4 MB.

A table gets 5 local secondary indexes, a fixed limit, and 20 global ones, a default you can raise.

  • Fixed vs adjustable. Item, key, batch, transaction, expression and LSI limits are fixed. Throughput, table-count and GSI-count quotas are defaults AWS raises on request.
  • Probed vs cited. Each size and count limit in the first table was confirmed by sending the request to the live service; the rest are cited to AWS.
  • Each limit has a way around it, collected in Which limits you should design around.

How these numbers were established

AWS states its size units are binary (1 KB = 1,024 bytes), so 400 KB should mean 409,600 bytes. The probes confirm that, and they also show whether attribute names count and exactly what the service says when you cross a line.

For every limit in the first table below, a request was built to sit exactly on the documented value and sent to the live service in us-east-1; then a second request, one unit past it.

The first must be accepted and the second rejected; that pair locates the edge. The rejection message in the last column is the service's own sentence, captured verbatim.

Four rows could only be established from the rejection side: CreateTable limits whose accept side means building a table with twenty indexes, for a number the rejection states outright. The How it was established column says which.

Verified limits

LimitValueHow it was establishedWhat the service returns when you cross it
Maximum item size409,600 bytesAccepted at 409,600, rejected at 409,601ValidationException: Item size has exceeded the maximum allowed size
Maximum partition key value2,048 bytesAccepted at 2,048, rejected at 2,049ValidationException: One or more parameter values were invalid: Size of hashkey has exceeded the maximum size limit of2048 bytes
Maximum sort key value1,024 bytesAccepted at 1,024, rejected at 1,025ValidationException: One or more parameter values were invalid: Aggregated size of all range keys has exceeded the size limit of 1024 bytes
Maximum nesting depth32 levelsAccepted at 32, rejected at 33ValidationException: 1 validation error detected: Nesting Levels have exceeded supported limits: Attributes in the item have nested levels beyond supported limit
Maximum expression length4,096 bytesAccepted at 4,096, rejected at 4,097ValidationException: 1 validation error detected: Invalid ConditionExpression: Expression size has exceeded the maximum allowed size;
Maximum items per BatchWriteItem25 itemsAccepted at 25, rejected at 26ValidationException: 1 validation error detected: Value '<your request>' at 'requestItems' failed to satisfy constraint: Map value must satisfy constraint: [Member must have length less than or equal to 25, Member must have length greater than or equal to 1]
Maximum keys per BatchGetItem100 itemsAccepted at 100, rejected at 101ValidationException: 1 validation error detected: Value at 'RequestItems.<table-name>.member.Keys' failed to satisfy constraint: Member must have length less than or equal to 100
Maximum actions per TransactWriteItems100 itemsAccepted at 100, rejected at 101ValidationException: 1 validation error detected: Value '<your request>' at 'transactItems' failed to satisfy constraint: Member must have length less than or equal to 100
Global secondary indexes per table20Rejection onlyValidationException: One or more parameter values were invalid: GlobalSecondaryIndex count exceeds the per-table limit of 20
Local secondary indexes per table5Rejection onlyValidationException: One or more parameter values were invalid: Number of LocalSecondaryIndexes exceeds per-table limit of 5
Projected non-key attributes per index20Rejection onlyValidationException: 1 validation error detected: Value '<your request>' at 'globalSecondaryIndexes.1.member.projection.nonKeyAttributes' failed to satisfy constraint: Member must have length less than or equal to 20
Projected non-key attributes per table100Rejection onlyValidationException: One or more parameter values were invalid: Number of projected attributes in all indexes exceeds limit of 100, number of projected attributes:120

Environment: Amazon DynamoDB, live service, us-east-1, probed 2026-08-27 with the AWS SDK for JavaScript v3.

Every limit in this table is fixed except one: 20 GSIs per table is a default quota AWS can raise on request. The 5-LSI limit is fixed, and LSIs can only be defined when the table is created.

What the probes turned up

400 KB means 409,600 bytes, and it counts your attribute names. An item measured at exactly 409,600 bytes was accepted; 409,601 was rejected. The measurement counts the UTF-8 length of every attribute name plus every value.

That is the same accounting the item size calculator implements; the probe builds its payload with that library, so the two agree by construction rather than by assertion.

The modeling problem behind this limit has its own guide: the DynamoDB item size limit.

An update that overflows says something else. The table quotes the PutItem rejection. An UpdateItem that pushes an item past 400 KB returns ValidationException: Item size to update has exceeded the maximum allowed size.

A log search for the first string misses every overflow on the update path.

The per-index projection limit is not on the Quotas page. The AWS Quotas page documents "up to 100 attributes combined for all of a table's local and global secondary indexes" and no per-index figure.

There is one, and it is 20. A CreateTable projecting 21 non-key attributes into a single index is rejected before the per-table total is anywhere near 100.

The 20 appears only in the API Reference's Projection page, as an array-member constraint: "Maximum number of 20 items".

Both caps apply only to an INCLUDE projection. If an index needs more than 20 non-key attributes, project ALL, which neither limit touches.

If storage matters more, project KEYS_ONLY and fetch the rest with a BatchGetItem on the base-table keys. See index projections.

Two of the messages contain AWS's own typos, reproduced here rather than silently corrected: maximum size limit of2048 bytes is missing a space, and number of projected attributes:120 is missing another.

If you are grepping logs for these strings, grep for what the service sends, not for what reads correctly.

The sort-key message says "aggregated". It reads "Aggregated size of all range keys". The probe used a table without a local secondary index, so it shows only the documented rule: each sort key value can be up to 1,024 bytes.

The 1 MB page, which raises nothing at all

Every limit above announces itself by rejecting you. The Query and Scan page limit does not. Cross it and DynamoDB returns a short page and a LastEvaluatedKey, with no error and no warning.

That is why "my Scan only returned part of the table" is such a common surprise, and why pagination is not optional. It also means there is no error message to quote, so it was measured instead of provoked:

At 1,000 bytes an item, a page held 1,029 items (1,029,000 bytes of item data) and returned a LastEvaluatedKey, leaving the 1,030th item for the next request. At 5,000 bytes an item, a page held 208 items (1,040,000 bytes of item data) and returned a LastEvaluatedKey, leaving the 209th item for the next request.

Neither page held 1 MiB of item data: the first fell 19,576 bytes short.

A simple model explains both pages: a page fills until items × (item bytes + per-item overhead) reaches a fixed budget. No zero-overhead budget fits both measurements. The overheads that do are between 13 and 19 bytes an item (7 whole-byte values).

AWS defines its size units as binary, so if that budget is exactly 1 MiB (1,048,576 bytes), only 19 bytes an item fits, with the budget between 1,048,551 and 1,048,970 bytes.

Read that 19 bytes as a model fit, not a measured constant: it assumes the 1 MiB budget and rests on one run at each item size with one key shape. The two pages do establish that a page holds less than 1 MiB of item data.

Your code shouldn't depend on any of these numbers: keep requesting pages until LastEvaluatedKey is absent.

Quotas not probed

The limits below are cited to AWS, not measured. Most are account-level quotas you can raise on request, and reaching them means hourly-billed throughput, thousands of tables, or a one- or three-year reserved capacity term.

The source for every row is the AWS Quotas in Amazon DynamoDB page.

QuotaDefault valueAdjustableWhy it wasn't probed
Tables per account per Region2,500YesCreating 2,500 tables to watch the 2,501st fail leaves an account someone has to unwind.
Provisioned throughput per table40,000 RCU and 40,000 WCUYesProvisioning 40,000 units bills by the hour whether or not a single request is made.
Provisioned throughput per account80,000 RCU and 80,000 WCUYesSame reason, twice over, and it changes an account-wide setting.
On-demand throughput per table40,000 RRU and 40,000 WRUYesReaching it means sustaining 40,000 requests a second, which is a load test with a bill.
Active reserved capacity per account1,000,000 capacity unitsYesReserved capacity is a one- or three-year purchase commitment, not a probe.
Table sizeNo practical limit—AWS states tables are unconstrained in items and bytes; there is no edge to find.

Other documented limits

A probe table can't hold every limit. These are cited to the AWS constraints page (and, for vector indexes, the quotas page), with the way around each.

LimitValueAdjustableWay around it
Transaction size100 distinct items and 4 MB of data, for TransactWriteItems and TransactGetItemsNoSplit into smaller transactions where correctness allows; use BatchWriteItem for bulk loads
BatchWriteItem request25 put or delete requests; 16 MB of items written (API reference: of the request)NoChunk at 25, and by serialized request size for tag-heavy items
BatchGetItem response100 items, 16 MB in totalNoRe-request the UnprocessedKeys the response returns
Item plus its LSI entries400 KB, per local secondary indexNoProject fewer attributes into LSIs (KEYS_ONLY is smallest)
Attribute name length64 KB; 255 bytes for index key names and LSI INCLUDE projected namesNoKeep names short: they count toward item size on every item anyway
Expression placeholder length255 bytes per #name or :value; 2 MB for all substitutions in a requestNoUse short placeholder names
Operators per expression300NoSplit a long OR chain across several requests and merge the results
IN operands100NoRun several requests of up to 100 values each
Table and index names3 to 255 characters: A-Z, a-z, 0-9, _, -, .NoNot a design constraint
Provisioned → on-demand switches4 per table in a rolling 24 hours (on-demand → provisioned: any time)NoPick a mode per workload, not per traffic spike
Provisioned capacity decreasesUp to 27 per table per UTC day (4 at once, 1 more each hour), per GSI tooDefault quotaScale down in fewer, larger steps
Streams readers per shard2 for a single-Region table; 1 recommended for global tablesNoFan out from one consumer, or use Kinesis Data Streams for DynamoDB
Vector indexes per table5YesRequest an increase through AWS Support

Throughput and partition limits

These three decide how a table behaves under load. All are cited to AWS, not probed: finding a throughput edge means sustaining the load that crosses it.

3,000 read units and 1,000 write units per second, per partition. A single item always lives in one partition, and on a table with an LSI so does its whole item collection.

Without an LSI, DynamoDB can split a busy partition key's items across partitions by sort key. One hot item still tops out at those numbers however much capacity the table has. Physical partitions covers how splits work.

The way out is to spread the key. Find it with CloudWatch Contributor Insights, shard its writes with a key suffix, and cache its reads with DAX; the hot partition guide has the full treatment.

10 GB per item collection, only on tables with a local secondary index. An item collection is every item sharing one partition key value, base table plus LSI entries.

Past 10 GB, writes that grow the collection are rejected with ItemCollectionSizeLimitExceededException; writes that shrink it still succeed.

Set ReturnItemCollectionMetrics: 'SIZE' on writes and alert well before the cap (AWS suggests 8 GB). If a collection can grow without bound, use a GSI instead of an LSI.

LSIs exist only from table creation, so that means a new table and a copy. See item collections.

On-demand absorbs up to double the previous peak. A new on-demand table can sustain up to 4,000 writes and 12,000 reads per second. Exceed double the previous peak within 30 minutes and requests can be throttled.

For a known spike (a launch, a sale), pre-warm: raise the table's and each GSI's warm throughput ahead of time with UpdateTable and WarmThroughput: {ReadUnitsPerSecond, WriteUnitsPerSecond}.

Pre-warming doesn't change the billing mode; the increase is charged once, and it can't be lowered afterwards. Otherwise, ramp traffic over at least 30 minutes. Throttling covers what to do when requests are rejected anyway.

Sources: AWS partition key design for the per-partition figures, partitions and data distribution for collection splits, the LSI page for the item-collection cap, and the on-demand mode and warm throughput pages for the ramp.

Which limits you should design around

Most of these you will never meet. The handful that shape real designs, each with its way out:

  • 400 KB per item is fixed. An item that approaches it is usually an unbounded list embedded in a parent: split it into items, compress large text, or move blobs to S3. See the item size limit.

  • 2,048-byte partition keys, 1,024-byte sort keys. Keys are identifiers: hash a long natural key and keep the full value as an ordinary attribute.

  • The 1 MB page governs every Query and Scan. Loop until LastEvaluatedKey is absent; code that ignores it drops results the day your data outgrows one page.

  • 25 items per batch write and 100 per batch get shape your bulk-load loops: chunk, then retry UnprocessedItems / UnprocessedKeys with backoff. See batch operations.

  • 100 actions and 4 MB per transaction. Split the work into smaller transactions where correctness allows, group attributes that change together into one item, and use BatchWriteItem for bulk loads. See transactions.

  • 20 GSIs, 5 LSIs, 20 projected attributes per index and 100 per table constrain access-pattern design more often than throughput quotas do.

    Project ALL past the INCLUDE caps, overload one GSI with generic key names before adding another (see single-table design), and decide on LSIs before you create the table. See index projections and GSI vs LSI.

To check a single item against the 400 KB line without writing it, the item size calculator runs in your browser. To check the size of an item already in a table, open it in DynoTable's Quick View, which shows its estimated size.

FAQ

What is the DynamoDB item size limit? 400 KB per item, 409,600 bytes exactly, verified by probe, counting the UTF-8 bytes of every attribute name and value. An item that approaches it is usually an unbounded relationship stored as an embedded list; split it into separate items.

How much throughput can a single DynamoDB partition handle? 3,000 read units and 1,000 write units per second. A single item always lives in one partition, so one hot item throttles at those numbers however much capacity the table has. Without an LSI, a busy partition key's items can be split across partitions by sort key; spread a hot key with write sharding, or cache its reads with DAX.

What is the 10 GB limit in DynamoDB? It is the item-collection cap, and it exists only on tables with a local secondary index: all items sharing one partition key value, across the base table and its LSIs, must fit in 10 GB. Tables without an LSI have no such cap. Watch it with ReturnItemCollectionMetrics, and use a GSI instead of an LSI when a collection can grow without bound.

Is there a limit on DynamoDB table size? No practical one. AWS places no ceiling on a table's item count or total bytes. The limits that bind are per item (400 KB), per page (1 MB), and per partition (3,000 read units and 1,000 write units per second).

How many actions fit in one DynamoDB transaction? 100, with at most 4 MB of data. A TransactWriteItems with 100 actions is accepted and one with 101 is rejected, both verified against the live service. Split larger work into several transactions, or use BatchWriteItem for bulk loads.

By Dmitriy · Updated