DynamoDB Capacity & Pricing: RCU, WCU, On-Demand vs Provisioned

How DynamoDB pricing works — read/write capacity units (RCU/WCU), on-demand vs provisioned, auto scaling, and how to estimate and cut your bill.

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

DynamoDB billing confuses people because you don’t pay for storage and CPU the way you do with a relational database — you pay for throughput, measured in capacity units. Understanding how those units are consumed is the difference between a $30 bill and a $3,000 one.

This guide covers how reads and writes are metered, the two capacity modes, and the levers that actually move your bill.

Capacity units: the unit of billing

Every read and write is converted into capacity units based on item size, rounded up.

UnitWhat one unit buys
RCU (read capacity unit)One strongly consistent read of up to 4 KB, or two eventually consistent reads of 4 KB
WCU (write capacity unit)One write of up to 1 KB

The math rounds up per item and per 4 KB / 1 KB block:

  • Reading a 5 KB item strongly consistent → ceil(5/4) = 2 RCU.
  • Reading the same item eventually consistent → 2 × 0.5 = 1 RCU.
  • Reading it in a transaction → 2 × 2 = 4 RCU.
  • Writing a 1.5 KB item → ceil(1.5/1) = 2 WCU. A transactional write doubles that to 4 WCU.

Item size includes attribute names plus values, so verbose attribute names quietly inflate cost. This is one reason single-table designs use short keys like PK and SK — see single-table design.

Reads bill on data scanned, not returned

This is the trap that surprises everyone. A Query or Scan consumes RCUs for every item read from the table, and FilterExpression is applied after the read. If you Scan a 1 GB table and filter down to three items, you pay for the full gigabyte. Always answer questions with key conditions, not filters — the difference is explained in query vs scan.

On-demand vs provisioned

DynamoDB offers two capacity modes per table, switchable once every 24 hours.

On-demand (pay-per-request)

You pay per million read and write request units, with zero capacity planning. DynamoDB scales instantly to whatever traffic arrives.

  • Good for: new apps, spiky traffic, unknown load, dev/test, anything you don’t want to babysit.
  • Cost shape: linear with traffic. You never pay for idle.
aws dynamodb create-table \
  --table-name Orders \
  --attribute-definitions AttributeName=PK,AttributeType=S \
  --key-schema AttributeName=PK,KeyType=HASH \
  --billing-mode PAY_PER_REQUEST

Provisioned

You reserve a fixed number of RCUs and WCUs per second. Requests beyond your reserved capacity get throttled (ProvisionedThroughputExceededException) unless you’ve enabled burst capacity or auto scaling.

  • Good for: steady, predictable traffic where you can keep utilization high.
  • Cost shape: you pay for what you reserve, used or not.
aws dynamodb create-table \
  --table-name Orders \
  --attribute-definitions AttributeName=PK,AttributeType=S \
  --key-schema AttributeName=PK,KeyType=HASH \
  --billing-mode PROVISIONED \
  --provisioned-throughput ReadCapacityUnits=25,WriteCapacityUnits=25

The break-even rule of thumb

On-demand request units cost roughly 6–7x a provisioned unit running at full utilization. So provisioned wins only when your sustained utilization stays above ~15–20%. If your traffic is bursty and your average utilization is low, on-demand is both cheaper and less work. Many teams launch on on-demand, measure for a month, and switch to provisioned only if the numbers clearly justify it.

Auto scaling

Provisioned tables can use Application Auto Scaling to track a target utilization (default 70%). It raises and lowers capacity within bounds you set.

aws application-autoscaling register-scalable-target \
  --service-namespace dynamodb \
  --resource-id "table/Orders" \
  --scalable-dimension "dynamodb:table:ReadCapacityUnits" \
  --min-capacity 5 --max-capacity 500

The catch: auto scaling reacts on a multi-minute timescale via CloudWatch alarms. It smooths gradual trends well but cannot absorb a sudden spike — by the time it scales up, the spike may be over and your users throttled. For genuinely spiky workloads, on-demand handles the spike instantly; auto scaling does not.

What else shows up on the bill

Throughput is usually the biggest line, but not the only one:

  • Storage — billed per GB-month of table data plus index data.
  • Global Secondary Indexes — every write that touches an indexed attribute incurs additional WCUs on the index. A table with three GSIs can cost 4x the write capacity of a bare table. Don’t over-index; see secondary indexes.
  • DynamoDB Streams — billed per million read requests against the stream.
  • Backups — on-demand and continuous (PITR) backups are billed by storage size.
  • Data transfer — egress out of AWS, standard rates.
  • Global Tables — replicated writes (rWCU) are billed in each replica Region.

Estimating before you build

Work it out from access patterns, not guesswork:

  1. Estimate average item size in KB (include attribute names).
  2. Estimate reads/sec and writes/sec at peak.
  3. RCUs = reads/sec × ceil(size/4) (halve for eventually consistent).
  4. WCUs = writes/sec × ceil(size/1).
  5. Add GSI write cost for any write that modifies an indexed attribute.

Then compare the provisioned monthly cost against the on-demand cost at the same volume. If you’re under ~20% projected utilization, on-demand almost always wins.

Cutting an existing bill

LeverEffect
Use eventually consistent reads where correctness allowsHalves read cost
Replace Scan + filter with Query on a keyOften 10–100x less data read
Drop unused GSIsRemoves their storage and write amplification
Shorten attribute names / compress large blobsSmaller items = fewer RCU/WCU
Move cold data to S3, keep a pointer in DynamoDBLess storage and smaller items
Set TTL on ephemeral dataFree deletes, lower storage
Switch idle provisioned tables to on-demandStop paying for unused capacity

Inspect ConsumedCapacity on your responses (pass ReturnConsumedCapacity=TOTAL or INDEXES) to see exactly what each call costs, and watch the ConsumedReadCapacityUnits / ThrottledRequests CloudWatch metrics in production.

When you’re tuning capacity, it helps to see consumed units per operation as you run queries rather than parsing raw CloudWatch graphs. A native DynamoDB GUI like Tablyne surfaces the ConsumedCapacity of each query and scan inline, which makes it obvious when a filter is quietly reading the whole table. That feedback loop is hard to get from the AWS console alone.

For deeper guidance on key design and avoiding the hot-partition problems that wreck capacity planning, see best practices and consistency.

Frequently asked questions

How many RCUs does a strongly consistent read use?

A strongly consistent read consumes one full RCU per 4 KB read (rounded up). An eventually consistent read of the same item costs half an RCU, so it's twice as cheap. Transactional reads cost two RCUs per 4 KB.

Is DynamoDB on-demand more expensive than provisioned?

Per request, yes — on-demand pricing is roughly 6–7x the cost of a fully-utilized provisioned unit. But provisioned only wins if your utilization is consistently high. For spiky or unpredictable traffic, on-demand is usually cheaper because you don't pay for idle capacity.

Does a Query cost more than a GetItem?

A Query is billed on the total size of items it reads before any FilterExpression is applied, not the number of items returned. A GetItem bills only for the single item fetched, so a filtered Query that scans many items can cost far more than it appears.

Keep reading