boto3
AWS S3
Python
file metadata
cloud storage

Getting S3 objects' last modified datetimes with boto

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

Introduction

S3 stores a last-modified timestamp for each object, and in Python boto3 exposes that timestamp as a timezone-aware datetime. The practical questions are usually which API to call, how to handle pagination, and how to compare the returned timestamp safely.

Use head_object for One Known Key

If you already know the object key, the cleanest choice is head_object.

python
1import boto3
2
3s3 = boto3.client('s3')
4response = s3.head_object(Bucket='example-bucket', Key='reports/summary.csv')
5print(response['LastModified'])

This avoids listing a whole prefix just to retrieve metadata for one object.

Use list_objects_v2 When You Are Already Listing

If you are enumerating keys anyway, list_objects_v2 includes LastModified in each object summary.

python
1import boto3
2
3s3 = boto3.client('s3')
4response = s3.list_objects_v2(Bucket='example-bucket', Prefix='reports/')
5
6for obj in response.get('Contents', []):
7    print(obj['Key'], obj['LastModified'])

This is convenient for prefix-wide reporting or when you want to find the newest object under a path.

The Value Is a Real datetime

A very useful detail is that LastModified is not returned as a plain string. It is already a Python datetime with time-zone information attached.

python
1from datetime import datetime, timezone
2import boto3
3
4s3 = boto3.client('s3')
5obj = s3.head_object(Bucket='example-bucket', Key='reports/summary.csv')
6last_modified = obj['LastModified']
7
8print(last_modified.isoformat())
9print(last_modified < datetime.now(timezone.utc))

Because the value is aware rather than naive, compare it to other aware datetimes such as datetime.now(timezone.utc).

The Resource API Exposes It Too

If your codebase prefers the resource-style API, the same metadata is available there.

python
1import boto3
2
3s3 = boto3.resource('s3')
4obj = s3.Object('example-bucket', 'reports/summary.csv')
5print(obj.last_modified)

Choose the client or resource style that matches the rest of your application. The metadata itself is the same idea either way.

Find the Most Recent Object Under a Prefix

S3 does not sort objects by last-modified time for you. If you need the newest object, fetch the candidates and compute it in Python.

python
1import boto3
2
3s3 = boto3.client('s3')
4response = s3.list_objects_v2(Bucket='example-bucket', Prefix='reports/')
5objects = response.get('Contents', [])
6
7latest = max(objects, key=lambda item: item['LastModified'], default=None)
8if latest is not None:
9    print(latest['Key'], latest['LastModified'])

For a small prefix, this is straightforward. For large prefixes, combine the same idea with pagination.

Paginate Large Listings

list_objects_v2 returns at most 1000 keys per response, so large prefixes require a paginator.

python
1import boto3
2
3s3 = boto3.client('s3')
4paginator = s3.get_paginator('list_objects_v2')
5
6for page in paginator.paginate(Bucket='example-bucket', Prefix='reports/'):
7    for obj in page.get('Contents', []):
8        print(obj['Key'], obj['LastModified'])

If you are searching for the newest object, keep a running maximum while paging instead of storing every result in memory.

One more practical concern is clock interpretation across systems. Even though S3 metadata is returned in a timezone-aware form, your application logic should still normalize timestamps consistently before combining them with database values, logs, or user-facing time displays.

If your goal is change detection rather than display, pair the timestamp with the object key and other metadata such as size or ETag when appropriate. Relying on one field alone can be too simplistic for some workflows.

Common Pitfalls

Treating LastModified like a plain string and comparing it lexicographically is unnecessary and error-prone because boto already gives you a datetime.

Using bucket listing when you already know the exact key adds extra requests and latency.

Ignoring pagination can make your code silently miss objects beyond the first 1000 results.

Summary

  • Use head_object when you know the key and only need one object's metadata.
  • Use list_objects_v2 when you are listing keys and need metadata for many objects.
  • 'LastModified is returned as a timezone-aware Python datetime.'
  • Paginate large prefixes and compare timestamps as datetimes, not as strings.

Related reading
Course
Beginner
27 lessons
10 hours
System Design Fundamentals

Build a strong foundation in designing scalable, reliable distributed systems.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

All Rights Reserved.