S3 request fees explained: when they matter more than storage
PUT is $0.005/1K. GET is $0.0004/1K. For high-frequency small-object workloads, request fees can exceed storage fees by 100x. Here's the full request pricing matrix across all storage classes, when fees dominate, and the architectural patterns that keep them low.
Quick answer
S3 request fees split into Tier 1 (PUT, COPY, POST, LIST: $0.005 per 1,000) and Tier 2 (GET, HEAD, SELECT: $0.0004 per 1,000) at Standard. Glacier classes are 10-100x more expensive per request. Request fees matter most for workloads with many small objects and high access rates. Mitigations: batch writes, use multipart for large objects, avoid frequent LISTs on large prefixes, choose storage class based on access pattern.
Most S3 cost discussions focus on storage per GB-month. For many production workloads, that's the right focus. But for workloads with many small objects or high access rates, request fees can exceed storage fees. This post walks through how S3 request pricing actually works, when it dominates, and the patterns that keep it under control.
The full request pricing matrix
S3 Standard
- PUT, COPY, POST, LIST (Tier 1): $0.005 per 1,000 requests
- GET, HEAD, SELECT (Tier 2): $0.0004 per 1,000 requests
S3 Standard-IA and One Zone-IA
- PUT, COPY, POST, LIST: $0.01 per 1,000 requests (2x Standard)
- GET, HEAD, SELECT: $0.001 per 1,000 requests (2.5x Standard)
- Retrieval: $0.01 per GB read
S3 Glacier Instant Retrieval
- PUT, COPY, POST: $0.02 per 1,000 requests (4x Standard)
- GET: $0.01 per 1,000 requests (25x Standard)
- Retrieval: $0.03 per GB read
S3 Glacier Flexible Retrieval and Deep Archive
- PUT: $0.03 per 1,000 requests (6x Standard)
- GET: $0.0004 per 1,000 (same as Standard)
- Restore: variable depending on tier (Expedited, Standard, Bulk)
- Retrieval: $0.01-$0.025 per GB depending on tier
The trend: as storage class gets colder, PUT fees climb. Even at Deep Archive's very cheap storage rate, bulk-loading data has substantial PUT cost.
For the complete S3 pricing reference, see the aws_s3_bucket catalog page.
When request fees actually dominate
High-frequency small-object workloads
IoT telemetry, fine-grained log ingestion, request-by-request archival. A workload writing 100 small (1 KB) objects per second to S3 is doing 8.6M PUTs per day = 258M PUTs per month.
At Standard tier: 258M × $0.005/1K = $1,290/month in PUT fees alone. Storage of 258M × 1 KB = 258 GB = $5.93/month. Request fees are 217x larger than storage fees.
Mitigation: batch the writes. Kinesis Firehose buffers small events into larger files (5 MB+) before writing. 8.6M/day events in 5 MB batches = ~17K PUTs/day instead of 8.6M. Cost drops from $1,290 to $2.55/month — 500x reduction.
Object-heavy backup/archive operations
A backup tool that archives 10M individual files to Glacier Deep Archive pays 10M × $0.03/1K = $300 in PUT fees, even though Deep Archive storage is essentially free. For backup workflows, pre-zip or aggregate files before archival.
Frequent LIST operations
Applications that scan a bucket via LIST repeatedly are expensive. A cron job that LISTs a 5M-key bucket once per minute does 1,440 LISTs per day, requiring 5,000 paginated calls each (since LIST returns max 1,000 per call). That's 7.2M LISTs per day = 215M per month = $1,075 at Standard.
Mitigation: maintain an external index (DynamoDB, RDS) of object keys instead of scanning S3. Or use S3 Inventory which produces a daily CSV of all objects for $0.0025/M-listed objects.
Static site assets with high traffic
A public website serving 10M GETs per day directly from S3 pays 10M × $0.0004/1K × 30 = $120/month in request fees. Plus egress ($0.09/GB). Mitigation: put CloudFront in front. Cache hits at edge eliminate the S3 GET. For repeat-read traffic, a 95% cache hit rate cuts S3 requests to 5% of original.
The cost of multipart uploads
Uploading a large file as a single PUT can fail on network glitches. Multipart upload splits the object into chunks and uploads in parallel. Pricing detail:
- Each part is one PUT (counted separately)
- CompleteMultipartUpload is one additional PUT
- InitiateMultipartUpload is free
For a 10 GB file uploaded in 100 MB parts: 100 parts × $0.005/1K + 1 complete = ~$0.0005. Negligible vs single-PUT, but worth knowing that 10 GB doesn't become "$0.05 in PUT fees."
Risk: incomplete multipart uploads stay in S3 (and bill for storage) unless cleaned up. Set up an aws_s3_bucket_lifecycle_configuration with abort_incomplete_multipart_upload to clean stale ones.
S3 Express One Zone request pricing
S3 Express One Zone is optimized for single-digit-millisecond latency. Request pricing is significantly cheaper:
- PUT: $0.0025 per 1,000 (half of Standard)
- GET: $0.0002 per 1,000 (half of Standard)
But storage is much more expensive: $0.16/GB-month (7x Standard). Right when you have high request volume and small total storage, like ML training scratch space or hot analytical datasets.
How to triage request-fee surprises
Cost Explorer with usage type filter
Filter by Usage Type containing "Requests-Tier1" and "Requests-Tier2". Group by Resource (with tag tags) to find which bucket is the culprit. The output shows the request count and dollar amount per bucket.
CloudWatch S3 metrics
Free request metrics are available per bucket. The BucketRequests metric breaks down by HTTP method. Time-series view shows whether requests are constant, periodic (cron-driven), or burst.
S3 server access logs
For per-request detail (which client, which key, which method), enable server access logging. Logs go to another S3 bucket. Query with Athena to find the requesting agents and access patterns.
S3 Storage Lens
Storage Lens provides organization-wide analytics including request patterns. Free tier covers basic metrics; advanced analytics is paid. For multi-account environments, this is the fastest way to find request hot spots.
The optimization checklist
- Batch small writes via Kinesis Firehose, custom batching, or aggregator services. Targets like S3 Express One Zone are right for the resulting larger objects.
- Use multipart upload for objects over 100 MB (better resilience, minimal additional cost).
- Set lifecycle rules to abort_incomplete_multipart_upload after 7 days. Stale multipart uploads silently bill for storage.
- Front public static content with CloudFront. Cache hits eliminate S3 GETs.
- For frequent LIST operations, maintain external indexes or use S3 Inventory's daily CSV.
- For cold tier writes (Glacier Deep Archive), pre-aggregate files. The PUT fee dominates Deep Archive's storage cost.
Estimating request fees in C3X
Request volume is usage-based. Specify expected counts in c3x-usage.yml:
# c3x-usage.yml
resource_usage:
aws_s3_bucket.logs:
standard_storage_gb: 1000
monthly_tier_1_requests: 5000000 # PUTs, LISTs
monthly_tier_2_requests: 50000000 # GETsc3x calculates the request fees based on the storage class configured in the bucket's lifecycle policy. For more on the bucket pricing model, see the aws_s3_bucket catalog page.
FAQ
When do S3 request fees dominate the bill?
When you have many small objects with high access patterns. A bucket storing 1M small log files that are PUT once and GET 10 times each generates 11M requests. At Standard tier ($0.005/1K PUTs + $0.0004/1K GETs), that's $9 in PUT fees but only ~$23 in storage at 100 GB. For high-frequency low-volume access, request fees can exceed storage fees.
Are PUT requests really 10x more expensive than GET?
Roughly yes. Standard: PUT is $0.005/1K, GET is $0.0004/1K — 12.5x difference. The asymmetry exists across all storage classes. Reasoning: PUTs consume more cluster resources than GETs in S3's architecture. Optimization: batch PUTs where possible, use multipart upload for large objects (counts as one PUT plus completion).
Do LIST operations count as requests?
Yes. LIST is a Tier 1 request (same rate as PUT, $0.005/1K). Each LIST returns up to 1000 keys; longer prefixes can require multiple LIST calls (paginated). For buckets with millions of keys, scanning the bucket via LIST repeatedly is expensive.
How do I find which buckets generate the most requests?
Three approaches. CloudWatch S3 metrics by default show free request count per bucket — filter the BucketRequests metric. Enable S3 server access logging for per-request detail. S3 Storage Lens (paid) provides organization-wide request analytics. Most teams start with CloudWatch metrics for quick triage.
What about cross-account S3 requests?
Cross-account GET requests are billed to the bucket owner (the account hosting the bucket), not the requester. The bucket owner can flip this with the Requester Pays feature on the bucket. For shared data sets between organizations, Requester Pays moves the cost to the consumer.
Do S3 Intelligent-Tiering requests differ?
Yes. Intelligent-Tiering has a small per-object monitoring fee ($0.0025 per 1,000 objects per month) on top of standard request fees. For very small objects this can matter; for typical workloads it's a rounding error.
Summary
For most S3 workloads, storage dominates the bill. But for request-heavy patterns — IoT, log streaming, frequent backups, public sites — request fees can be the main line item. The mitigations are mostly architectural: batch writes, front public content with CloudFront, maintain indexes instead of LIST-scanning, choose storage class based on actual access pattern.
For the broader S3 pricing picture including storage classes and lifecycle policies, see S3 storage class comparison. For request-related data transfer (CloudFront vs direct S3 vs VPC endpoints), see AWS data transfer costs explained.
Share this post
Try C3X on your own Terraform
Free and open source. No API key required. One command to install, one command to estimate.