DynamoDB On-Demand Throughput: Measured
How much throughput does a new DynamoDB on-demand table get?
A brand-new table serves about 4,130 writes per second — AWS documents 4,000 — and at least 12,700 eventually-consistent reads per second. We measured both on 2026-08-27 against a table created minutes earlier: writes pinned at 4,130 ±2/s across every over-baseline load we offered, and reads never throttled at all before our own load generator ran out of headroom.
Those two numbers, and everything else on this page, come from counting real requests against the live service — not from restating the documentation. The method, the raw data, and the three failed attempts are written up in the benchmark story; this page is the reference the numbers live on.
The write ceiling on a fresh table
Offered load ramped in 30-second windows against a table that had never seen traffic. Every request carried a ~1 KB item with a uniformly random key — no involved:
| Offered (writes/s) | Achieved | Throttled requests |
|---|---|---|
| 1,000 | 1,000 | 0 |
| 2,000 | 2,000 | 0 |
| 3,000 | 3,000 | 0 |
| 4,000 | 4,000 | 0 |
| 5,000 | 4,132 | 25,992 |
| 6,000 | 4,131 | 55,966 |
| 8,000 | 4,134 | 115,922 |
The documented 4,000 write/s baseline holds, with about 3% of headroom above it. The ceiling is remarkably flat: 4,132, 4,131, 4,134 achieved per second at 5,000, 6,000 and 8,000 offered. Latency does not degrade as you cross it — p50 write latency stayed at 4–5 ms in-region in every window. The service does not slow down; it rejects.
What the throttle actually returns
The first rejection arrived 0.9–3.8 seconds into each over-baseline window (the higher the offered rate, the sooner it came). Verbatim:
ThrottlingException: Throughput exceeds the current capacity of your table or index. DynamoDB is automatically scaling your table or index so please try again shortly. If exceptions persist, check if you have a hot key: https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/bp-partition-key-design.htmlTwo things to plan around. It is an HTTP 400, not a 5xx — a retry policy or alarm that only watches 5xx will miss on-demand throttling entirely (the SDKs do retry it by default; see ). And the hot-key hint is a default suggestion, not a diagnosis — our keys were uniformly random, so at small scale the first suspect for this message is the table-level ceiling, not your key schema. The four distinct throttling causes have their own guide.
The read ceiling
Reads ran against a second fresh table seeded with 1,000 items, as
GetItems (~1 KB, 0.5 read units each):
| Offered (reads/s) | Achieved | Throttled requests |
|---|---|---|
| 4,000 | 4,000 | 0 |
| 8,000 | 7,941 | 0 |
| 12,000 | 11,119 | 0 |
| 16,000 | 12,762 | 0 |
Zero throttles at every rate. The documented 12,000 read/s baseline holds, and we could not find its actual edge: at 16,000/s offered, five of our eight runners saturated client-side, so 12,762/s is where our fleet topped out — not where DynamoDB did. Reads answered at 2–4 ms p50 in-region.
How the ceiling grows under sustained load
AWS documents that on-demand capacity grows to accommodate up to double the previous peak, and that exceeding double the previous peak within 30 minutes may throttle. We held 8,000 writes/s of offered load against one table for 34 contiguous minutes (four 8-minute waves with under a minute of pause between them) and watched the ceiling move, minute by minute:
| Wave | Started | Achieved writes/s, minute by minute |
|---|---|---|
| 1 | 06:06 UTC | 4,019 → 4,001 → 4,001 → 3,999 → 3,998 → 4,000 → 4,000 → 4,000 |
| 2 | 06:15 UTC | 5,046 → 5,000 → 4,996 → 4,990 → 4,991 → 4,998 → 4,992 → 4,993 |
| 3 | 06:24 UTC | 5,046 → 4,973 → 4,991 → 4,981 → 4,983 → 4,989 → 4,993 → 5,978 |
| 4 | 06:32 UTC | 7,006 → 6,991 → 6,979 → 6,996 → 6,991 → 6,988 → 7,002 → 6,990 |
Reading the timeline:
- The first ceiling is sticky. Through the entire first 8 minutes the table held at its ~4,000/s baseline — sustained over-demand did not move it within that window.
- Growth arrives in ~1,000/s steps, not a ramp. The ceiling stepped to ~5,000/s around minute 9, ~6,000/s around minute 26, and ~7,000/s a minute later, then held 7,000/s flat to the end. Each step lands abruptly between one minute and the next. Two of the three steps landed near our wave boundaries, so the sub-minute pauses may interact with the growth mechanism — we report the timing as observed.
- Half an hour of sustained demand did not double the ceiling. After 34 minutes at 8,000/s offered, the table served 7,000/s — 1.75× its starting ceiling, still short of both the offered rate and a clean doubling. Throttled requests fell wave over wave (1.9M → 0.5M) as capacity grew.
If your launch-day traffic will exceed ~4,000 writes/s on a new table, pre-warm it: either drive synthetic load ahead of the event, or set the table's maximum on-demand throughput explicitly and let AWS provision for it. The on-demand vs provisioned guide covers when each mode wins, and auto scaling is the provisioned-mode counterpart of what you watched happen automatically here.
Two numbers that surprised us
- Create-to-ACTIVE time varies 3×. A fresh on-demand table reached
ACTIVEin 7.4 seconds on one run and 22 seconds on two others, same region, same schema. Budget for the slow case in any table-per-tenant or table-per-test design. - The whole benchmark cost $0.97. 672,116 billed writes and 1.08 million reads. The 34-minute sustained-growth run cost $12.67 more. Measuring the service yourself is cheaper than one wrong capacity decision — and you can price a workload like this ahead of time with the DynamoDB pricing calculator.
Scope and method, honestly
Everything above is one table per phase, one day, one region (us-east-1), ~1 KB items, uniform random keys. Per-partition limits (3,000 read units / 1,000 write units per second) sit below the table-level behavior measured here and have their own failure modes. Account-level quotas and the hard service limits are on the DynamoDB limits reference, measured the same way. And if you work against DynamoDB daily, DynoTable is our desktop client for it — built by the same team, with the same habit of checking claims against the live service before repeating them.