
I Tried Loading Data from S3 into an Existing Table Using Amazon DynamoDB's Bulk Put Items
This page has been translated by machine translation. View original
Introduction
On November 5, 2026, the bulk put items feature of Amazon DynamoDB became generally available.
bulk put items is an asynchronous job that writes data placed in S3 into an existing DynamoDB table.
In this article, using the Standard performance tier in the Tokyo Region, I registered 10 KB items ranging from 100 items (about 1 MB) to 100,000 items (about 1 GB). In addition to checking the number and contents of the registered items, I wrote the same data to separate tables with BatchWriteItem and PutItem and compared the elapsed times. I also looked into the elapsed time when the input is split into multiple files, and the write cost.
Preparation
Input files
According to the official blog, the input can be newline-delimited DynamoDB JSON or Ion, and it can also be compressed with gzip / zstd.
The input for 100 items is a single file in DynamoDB JSON with one item per line. The file has 100 lines and 1,027,500 bytes, and each item is 10,240 bytes in total for attribute names and values. The format of one line is as follows. The payload is a random string of about 10,000 characters, replaced here with ....
{"Item":{"id":{"S":"item-0001-a6119290"},"payload":{"S":"..."}}}
Even in the conditions with more items, the size (10,240 bytes) and the format of each item are the same. The total input size was 10,275,000 bytes for 1,000 items, 51,375,000 bytes for 5,000 items, and 1,027,500,000 bytes for 100,000 items (the total of 10 files).
Execution and checking the results
Starting
With the 100-item input, I ran the following command.
aws dynamodb start-bulk-put-items \
--table-name <テーブル名> \
--input-s3-configuration "S3Bucket=<入力バケット>,S3KeyPrefix=input/,InputFormat=DYNAMODB_JSON" \
--output-s3-configuration "S3Bucket=<入力バケット>,S3KeyPrefix=report/run1/,OutputType=ERROR,CONDITION_CHECK_FAILURE" \
--performance-tier STANDARD
I ran it four times with the same input. In the first run, a response was returned about 0.75 seconds after the call. The response contained the status IN_PROGRESS and the ARN of the bulk operation.
Status until completion
Using the returned ARN, I checked the status with describe-bulk-operation.
aws dynamodb describe-bulk-operation --bulk-operation-arn <ARN>
Here are the fields related to the result, excerpted from the response at the completion of the first run.
{
"BulkOperationDescription": {
"BulkOperationStatus": "COMPLETED",
"StartTime": "2026-11-06T11:42:01.523000+09:00",
"EndTime": "2026-11-06T11:43:16.941000+09:00",
"ProcessedItemCount": 100,
"ProcessedInputBytes": 1027500,
"TotalInputBytes": 1027500,
"FailedItemCount": 0,
"PerformanceTier": "STANDARD"
}
}
All four runs reached COMPLETED, with 100 processed items and 0 failed items.
I recorded the progression of counts during execution only for the fourth run. Out of the polling at about 5-second intervals, I show the first and last IN_PROGRESS records and the COMPLETED record. All the records in between were also IN_PROGRESS, processed 0, failed 0.
POLL 2026-11-06T11:50:10.506+0900 status/processed/failed/processedBytes/totalBytes/endTime: IN_PROGRESS 0 0 None None None
POLL 2026-11-06T11:51:16.294+0900 status/processed/failed/processedBytes/totalBytes/endTime: IN_PROGRESS 0 0 None None None
POLL 2026-11-06T11:51:21.726+0900 status/processed/failed/processedBytes/totalBytes/endTime: COMPLETED 100 0 1027500 1027500 2026-11-06T11:51:16.767000+09:00
In the fourth run, the processed count stayed at 0 until COMPLETED, so I could not read the progress from intermediate values. The official blog says that for the first few minutes of a run the counters do not move because of preparation, and that this preparation time is a fixed startup time.
Report
Only one manifest-files.json was created in the output destination, and its content was a single line showing the processing status of the input file.
report/run1/AWSDynamoDB/BulkOperation/<ACCOUNT_ID>:<操作ID>/manifest-files.json 196 バイト
{"inputSourceArn":"arn:aws:s3:::<入力バケット>/input/items.jsonl","fileProcessingStatus":"FULLY_PROCESSED","processedRanges":[[0,99]],"etag":"<ETag>"}
I specified ERROR and CONDITION_CHECK_FAILURE as the output types, but no objects corresponding to them were output. This time FailedItemCount was 0, and the input had no condition expressions either. The output of the second and later runs was the same.
Registered contents
After the first run reached COMPLETED, I compared the contents of the table, which was empty before the run, against the input using a strongly consistent Scan. The following is the result of comparing the item count, the set of keys, the item size, and the SHA-256 of the payload.
{
"checked_at": "2026-11-06T02:43:35+00:00",
"table": "bulkput-verify-bulk",
"expected_count": 100,
"scan_count": 100,
"missing_keys": [],
"extra_keys": [],
"item_sizes_in_table": [
10240
],
"size_mismatch": [],
"hash_mismatch": [],
"ok": true
}
For the second to fourth runs as well, I performed the same comparison on a table that was empty before the run, and everything matched.
Comparing elapsed time
Measurement conditions
I wrote the same data to separate tables with BatchWriteItem (25 items at a time, sequentially) and PutItem (one item at a time, sequentially), and compared the elapsed time with bulk put items.
The measured interval differs by method, as follows.
- bulk put items: I measured from just before calling
start-bulk-put-itemsuntil thedescribe-bulk-operationpoll that returned COMPLETED. The upload to S3 was done before measurement. Because polling was at about 5-second intervals, there is a delay of up to about 5 seconds between completion and the record. - BatchWriteItem and PutItem: I measured from just before the first write until the completion of the last write. When UnprocessedItems were returned, I measured until they were resubmitted and all items were in. The input was loaded from a local file before measurement.
The execution environment is as follows.
- Connection: from a local Linux environment to the Tokyo Region over the internet
- bulk put items: AWS CLI (2.39.5 and 2.40.3)
- BatchWriteItem and PutItem: boto3 (retries in standard mode, max_attempts of 10)
- Tables: both the one for bulk put items and the one for comparison were on-demand, with only id as the key and no GSI
How the tables were used differs by condition, as follows.
- 1,000 items, 5,000 items: I created one table for bulk put items and one for comparison, and shared them across the two conditions. All items were deleted before each run.
- 100,000 items: I created new tables for each method and deleted the tables themselves after measurement. Because deletion also incurs write charges, I did not delete all items before each run.
Results with increased item counts
Each method was measured once. The result of splitting 5,000 items into 10 files is shown in the next section.
| Items (input size) | bulk put items (seconds) | Input files for bulk put items | BatchWriteItem (seconds) | PutItem (seconds) |
|---|---|---|---|---|
| 1,000 (about 10 MB) | 117.3 | 1 | 1.917 | 18.181 |
| 5,000 (about 51 MB) | 430.6 | 1 | 12.573 | 85.137 |
| 100,000 (about 1 GB) | 1,523.3 | 10 | 252.4 | Not measured |
I did not measure PutItem for 100,000 items because of cost and elapsed time. The elapsed time of bulk put items was longer than BatchWriteItem at every item count. The ratio to BatchWriteItem was about 61x for 1,000 items (1 file), about 34x for 5,000 items (1 file), and about 6.0x for 100,000 items (10 files).
Under every condition, all items entered the table without omission. Using a strongly consistent Scan, I compared the item count, the set of keys, the item size, and the SHA-256 of the payload against the input, and everything matched. All bulk put items runs reached COMPLETED with 0 failed items, and there were no errors in the reports either.
With BatchWriteItem, UnprocessedItems were returned as the item count increased. The measurement script resubmitted them, and in the end all items were written.
| Items | Total UnprocessedItems count | API calls (minimum calls) | SDK retries |
|---|---|---|---|
| 1,000 | 45 | 47 (40) | 0 |
| 5,000 | 394 | 277 (200) | 0 |
| 100,000 | 5,845 | 5,201 (4,000) | 5 |
Throttling exceptions did not occur under any condition.
For 100,000 items, WriteThrottleEvents during 20:47–20:52 (JST), when the writes took place, totaled about 5,800 as the sum of the per-minute values. This is almost the same as the total UnprocessedItems count. This write consumed 1 million WRU in about 252 seconds, which averaged about 3,960 WRU per second.
Splitting the input file
The official blog advises splitting large data into multiple objects of equal size, because the service processes input in parallel per object. So I split the 5,000-item input into 10 files of 500 items each, placed them under the same S3 prefix, and ran bulk put items once. The content of the input is the same as the single-file version, but the date and time of execution and the table are different.
split -l 500 -d -a 2 --additional-suffix=.jsonl items-5000.jsonl part-
| Input | bulk put items elapsed time (seconds) |
|---|---|
| 1 file | 430.6 |
| 10 files | 144.8 |
The elapsed time became about one third. Even so, it still takes about 11.5 times as long as BatchWriteItem (12.6 seconds).
Comparing write cost
The unit prices for the Tokyo Region are values from the AWS Price List obtained on November 15, 2026 (the version published on November 11, 2026).
| Item | Unit price |
|---|---|
| bulk put items (performance tier Standard, table class Standard) | $0.1144 / GB |
| On-demand writes (table class Standard) | $0.715 / million WRU |
The calculation for 100,000 items of 10 KB (about 1 GB) is as follows.
- bulk put items: BulkPutBytes in BulkOperationUsage in the
describe-bulk-operationresponse was 1,024,000,000 bytes. Taking 1 GB as 10^9 bytes, it comes to about $0.117 - BatchWriteItem: 1 WRU is a write of 1 KB, so a 10 KB item is 10 WRU. For 100,000 items that is 1,000,000 WRU, and the total of ConsumedWriteCapacityUnits in CloudWatch was the same value. Multiplied by the unit price, it is $0.715
Under the conditions of this test (10 KB items, an on-demand table without a GSI, and the Tokyo Region), I estimated the write cost of bulk put items to be about one sixth of BatchWriteItem.
According to the official blog, bulk put items writes using dedicated capacity, so it does not appear in the table's consumed capacity metrics. Even for the table used for the 100,000-item bulk put items run, ConsumedWriteCapacityUnits was 0 for every per-minute value from during the run until about 3 minutes after completion.
Summary
bulk put items is an asynchronous job that writes data placed in S3 into an existing table. From 100 items to 100,000 items, it registered the data in the table without omission.
In this measurement, the time to completion was longer than BatchWriteItem. On the other hand, splitting the input files made it faster, and the estimate showed the write cost to be lower than BatchWriteItem. The bulk put items writes did not consume the table's WRU.
If you want to ingest a large amount of data without consuming the table's WRU and you are not in a hurry for completion, give bulk put items a try.
