Read a file line by line from S3 using boto?
System Design practice on Codemia
Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.
Introduction
When an S3 object is large, reading it line by line is much safer than loading the whole file into memory. With boto3, the normal pattern is to stream the object body returned by get_object and iterate over its lines as bytes, decoding them only as needed.
Use iter_lines on the Response Body
The simplest streaming approach is Body.iter_lines().
This avoids reading the entire object into memory first, which is exactly what you want for log files, exports, and large text datasets.
Wrap It in a Reusable Generator
Application code is usually cleaner if the S3 streaming logic lives in one helper.
This keeps downstream code focused on processing rather than S3 plumbing.
Handle Gzip Files Without Losing Streaming
Many S3 text files are compressed. You can still stream them line by line by wrapping the response body.
This preserves the streaming behavior while correctly handling compressed content.
Decode Bytes Deliberately
S3 gives you bytes, not text. If the file encoding is not guaranteed, decode carefully.
Using errors="replace" can keep a long-running job alive even when a few malformed lines contain unexpected bytes.
Parse Structured Line Formats Incrementally
A common real-world case is JSON Lines. You can decode each line independently and continue past malformed rows if the workload allows it.
That keeps memory stable and isolates bad records to the single line that failed.
Add Retries and IAM Correctness
Production use is rarely just about the loop. Make sure the caller has s3:GetObject permission for the target key, and configure retries if the workload runs over unstable networks or long-lived sessions.
If the code runs inside AWS, IAM roles are preferable to hard-coded credentials. For local development, named profiles are usually cleaner.
Avoid Full Reads Unless You Actually Need Them
This is the main anti-pattern:
That works, but it loads the full object into memory and defeats the point of line-by-line processing. It is fine for small files. It is the wrong default for unknown or large file sizes.
Common Pitfalls
The most common mistake is calling read() first and only then splitting lines. That loads the whole object into memory and removes the streaming advantage.
Another issue is forgetting that the S3 response body yields bytes, not strings. Decode explicitly with the encoding you expect.
Developers also often ignore compression and try to parse gzip content as plain text. If the object is compressed, wrap it accordingly.
Finally, make sure permission and retry behavior are part of the design. A streaming loop is only useful if it is allowed to read the object and can recover from ordinary transient failures.
Summary
- Use
get_objectplusBody.iter_lines()to stream S3 text objects line by line. - Decode bytes explicitly instead of assuming you already have text.
- Wrap the logic in a generator when several parts of the codebase need it.
- Handle gzip objects with streaming decompression wrappers.
- Avoid full-body reads unless the file is known to be small and memory cost is irrelevant.
Related reading
- Read capacity cost of a DynamoDB table scan
- read dynamodb and get redundant Decimal word
- Read file content from S3 bucket with boto3
- Read file from aws s3 bucket using node fs
- Read in Large CSV File and feed into TensorFlow
- Read .mat files in Python
- Read, Modify and Update on AWS S3 atomically
- Reading a file from a private S3 bucket to a pandas dataframe

System Design Fundamentals
Build a strong foundation in designing scalable, reliable distributed systems.
View the courseTrack 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.