Amazon S3
list bucket contents
modified date
AWS
cloud storage

How list Amazon S3 bucket contents by modified date?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

The S3 API does not support sorting objects by modification date natively. You must retrieve the object listing and sort it client-side. With the AWS CLI, pipe the output through sort. With Python and Boto3, sort the results list in memory. For large buckets with millions of objects, use S3 Inventory reports instead of repeated API calls to avoid high costs and slow performance.

How S3 Stores Object Metadata

Every object in S3 has a LastModified timestamp that records when the object was created or last overwritten. This timestamp is part of the object's metadata and is returned by both ListObjectsV2 and HeadObject API calls. S3 stores this as a UTC timestamp with millisecond precision.

Important: S3 does not maintain a modification-date index. The ListObjectsV2 API returns objects sorted lexicographically by key (the object's path), not by date. Any date-based sorting must happen after retrieval.

Method 1: AWS CLI

Basic listing with date information

bash
1aws s3api list-objects-v2 \
2  --bucket my-bucket \
3  --query "Contents[].[LastModified,Size,Key]" \
4  --output text

This outputs three columns: modification date, size in bytes, and key name.

Sort by modification date

bash
1aws s3api list-objects-v2 \
2  --bucket my-bucket \
3  --query "Contents[].[LastModified,Size,Key]" \
4  --output text | sort -k1

The sort -k1 flag sorts by the first column (LastModified). Because S3 timestamps are in ISO 8601 format, lexicographic sorting produces correct chronological order.

Most recently modified objects

bash
1aws s3api list-objects-v2 \
2  --bucket my-bucket \
3  --query "Contents[].[LastModified,Size,Key]" \
4  --output text | sort -k1 -r | head -20

The -r flag reverses the sort order so the newest objects appear first.

Filter by prefix

bash
1aws s3api list-objects-v2 \
2  --bucket my-bucket \
3  --prefix "logs/2026/" \
4  --query "Contents[].[LastModified,Key]" \
5  --output text | sort -k1 -r

Filtering by prefix reduces the number of objects returned, which speeds up both the API call and the client-side sort.

Using aws s3 ls (simpler but less flexible)

bash
aws s3 ls s3://my-bucket/ --recursive | sort -k1,2

The aws s3 ls command outputs date and time in the first two columns. Sorting by both columns (-k1,2) produces chronological order. This is simpler but does not support JMESPath queries for filtering.

Method 2: Python with Boto3

Basic script

python
1import boto3
2
3s3 = boto3.client("s3")
4bucket_name = "my-bucket"
5
6response = s3.list_objects_v2(Bucket=bucket_name)
7
8if "Contents" in response:
9    objects = sorted(response["Contents"], key=lambda obj: obj["LastModified"])
10    for obj in objects:
11        print(f"{obj['LastModified']}  {obj['Size']:>10}  {obj['Key']}")

Handling pagination for large buckets

list_objects_v2 returns at most 1,000 objects per call. For buckets with more objects, use the paginator:

python
1import boto3
2
3s3 = boto3.client("s3")
4bucket_name = "my-bucket"
5paginator = s3.get_paginator("list_objects_v2")
6
7all_objects = []
8for page in paginator.paginate(Bucket=bucket_name):
9    if "Contents" in page:
10        all_objects.extend(page["Contents"])
11
12# Sort by LastModified descending (newest first)
13all_objects.sort(key=lambda obj: obj["LastModified"], reverse=True)
14
15for obj in all_objects[:50]:
16    print(f"{obj['LastModified']}  {obj['Size']:>10}  {obj['Key']}")

Filter by prefix and date range

python
1import boto3
2from datetime import datetime, timezone
3
4s3 = boto3.client("s3")
5bucket_name = "my-bucket"
6paginator = s3.get_paginator("list_objects_v2")
7
8cutoff = datetime(2026, 1, 1, tzinfo=timezone.utc)
9
10matching = []
11for page in paginator.paginate(Bucket=bucket_name, Prefix="uploads/"):
12    for obj in page.get("Contents", []):
13        if obj["LastModified"] >= cutoff:
14            matching.append(obj)
15
16matching.sort(key=lambda obj: obj["LastModified"], reverse=True)
17
18for obj in matching:
19    print(f"{obj['LastModified']}  {obj['Key']}")

Find the N most recently modified objects

python
1import boto3
2import heapq
3
4s3 = boto3.client("s3")
5bucket_name = "my-bucket"
6paginator = s3.get_paginator("list_objects_v2")
7
8# Use a heap to track the top 10 without sorting everything
9top_n = 10
10heap = []
11
12for page in paginator.paginate(Bucket=bucket_name):
13    for obj in page.get("Contents", []):
14        item = (obj["LastModified"], obj["Key"], obj["Size"])
15        if len(heap) < top_n:
16            heapq.heappush(heap, item)
17        else:
18            heapq.heappushpop(heap, item)
19
20# Sort the top N in descending order
21results = sorted(heap, reverse=True)
22for modified, key, size in results:
23    print(f"{modified}  {size:>10}  {key}")

This heap-based approach uses O(N) memory for the top N results instead of O(total_objects) for sorting the entire listing.

Method 3: S3 Inventory (For Very Large Buckets)

For buckets with millions of objects, calling list_objects_v2 repeatedly is slow and expensive. S3 Inventory generates a daily or weekly manifest of all objects, delivered as CSV, ORC, or Parquet files to a destination bucket.

Configure S3 Inventory

bash
1aws s3api put-bucket-inventory-configuration \
2  --bucket my-bucket \
3  --id daily-inventory \
4  --inventory-configuration '{
5    "Id": "daily-inventory",
6    "IsEnabled": true,
7    "Destination": {
8      "S3BucketDestination": {
9        "Bucket": "arn:aws:s3:::my-inventory-bucket",
10        "Format": "CSV",
11        "Prefix": "inventory"
12      }
13    },
14    "Schedule": {
15      "Frequency": "Daily"
16    },
17    "IncludedObjectVersions": "Current",
18    "OptionalFields": ["LastModifiedDate", "Size", "StorageClass"]
19  }'

Query the inventory with Athena

Once the inventory is delivered, query it with Amazon Athena for fast, indexed lookups:

sql
1SELECT key, last_modified_date, size
2FROM s3_inventory.my_bucket_inventory
3WHERE last_modified_date > TIMESTAMP '2026-01-01 00:00:00'
4ORDER BY last_modified_date DESC
5LIMIT 100;

This is dramatically faster and cheaper than paginating through the list API for buckets with tens of millions of objects.

Comparison of Methods

MethodBest ForMax ObjectsCostSort Support
AWS CLI + sortQuick checks, small bucketsUp to 100KStandard API pricingClient-side
Boto3 paginatorAutomation, scripting, filteringUp to 1M+Standard API pricingClient-side
Boto3 + heapqFinding top N in large bucketsAny sizeStandard API pricingHeap-based (efficient)
S3 Inventory + AthenaMillions of objectsUnlimitedInventory + Athena pricingSQL ORDER BY

IAM Permissions Required

The IAM user or role needs the following permissions:

json
1{
2  "Version": "2012-10-17",
3  "Statement": [
4    {
5      "Effect": "Allow",
6      "Action": [
7        "s3:ListBucket",
8        "s3:GetBucketLocation"
9      ],
10      "Resource": "arn:aws:s3:::my-bucket"
11    },
12    {
13      "Effect": "Allow",
14      "Action": "s3:GetObject",
15      "Resource": "arn:aws:s3:::my-bucket/*"
16    }
17  ]
18}

s3:ListBucket is the permission required for list_objects_v2. It is a bucket-level permission (applied to the bucket ARN without /*), not an object-level permission.

Common Pitfalls

  • Assuming the S3 API returns objects sorted by date. It does not. Objects are always returned in lexicographic key order. You must sort client-side.
  • Not paginating for large buckets. list_objects_v2 returns at most 1,000 objects per request. Without pagination, you only see the first 1,000 objects (sorted by key), which may not include the objects you are looking for.
  • Sorting millions of objects in memory. For very large buckets, loading all object metadata into memory and sorting is both slow and expensive. Use S3 Inventory with Athena instead.
  • Confusing LastModified with upload date for multipart uploads. For multipart uploads, LastModified reflects when the multipart upload was completed, not when it was initiated.
  • Using s3:GetObject instead of s3:ListBucket. Listing bucket contents requires the s3:ListBucket permission on the bucket resource, not s3:GetObject on the objects.
  • Ignoring API costs. Each list_objects_v2 call is a LIST request. At $0.005 per 1,000 requests, listing a bucket with 10 million objects costs about $50 per full scan. S3 Inventory is much cheaper for repeated analysis.

Summary

  • S3 does not support server-side sorting by modification date. All sorting must happen client-side.
  • For quick checks, use aws s3api list-objects-v2 piped through sort -k1.
  • For automation, use Boto3 with the paginator to iterate through all objects and sort in Python.
  • For finding the top N most recent objects efficiently, use a heap to avoid sorting the entire listing.
  • For buckets with millions of objects, configure S3 Inventory and query the results with Athena.
  • Always paginate when listing objects. The API returns at most 1,000 objects per call.

Course illustration
Course illustration

All Rights Reserved.