2026-07-15

How do I send an email with a .csv attachment using Python

Stefan Bogdanescu

Stefan Bogdanescu

Founder & Senior Architect

How do I send an email with a .csv attachment using Python

How to Send an Email with a .csv Attachment Using Python: A Complete Guide

Sending emails with attachments is a very common requirement in backend development, whether you are building a simple script or a complex application. As a developer, you often need to handle file I/O and network protocols simultaneously. While the basic smtplib module exists, attaching files requires more than just passing a filename; you need to correctly format the email as a MIME (Multipurpose Internet Mail Extensions) message to properly embed the binary data of your CSV file.

If you are new to Python, this process can seem daunting. Let's walk through exactly how to achieve this reliably, moving beyond simple attempts that often fail.

Why Simple Attempts Fail: Understanding MIME

The reason the basic code snippet you encountered doesn't work is that the smtplib module focuses on sending plain text messages. To send binary data like a CSV file as an attachment, you must package that data into a structure called a MIME message. This involves encoding the file content (usually using Base64) so it can be safely transmitted across the email protocol, and then defining the correct boundaries for the attachment within the email body.

In essence, we need to perform three main steps:

  1. Read the CSV file's binary contents.
  2. Encode those contents into Base64 text.
  3. Construct a complete email message that includes this encoded data as an attachment part.

Step-by-Step Guide and Code Implementation

We will use Python’s built-in smtplib for the connection and the email package to handle the complex structuring of the message, which is far more robust than trying to force raw file paths into the sendmail function.

Prerequisites

Before running the code, ensure you have your CSV file ready and that you know your email credentials (e.g., using an App Password if you are using services like Gmail).

The Complete Python Solution

Here is a comprehensive example demonstrating how to read a CSV and attach it to an outgoing email:

import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.base import MIMEBase
import base64

# --- Configuration ---
SENDER_EMAIL = "your_email@example.com"
PASSWORD = "your_app_password"  # Use an App Password for security
RECEIVER_EMAIL = "recipient@example.com"
SMTP_SERVER = "smtp.gmail.com"
SMTP_PORT = 587

CSV_FILENAME = "data_export.csv"

try:
    # 1. Read the CSV file content in binary mode
    with open(CSV_FILENAME, 'rb') as f:
        csv_data = f.read()

    # 2. Encode the data to Base64 for safe transmission
    encoded_data = base64.b64encode(csv_data).decode('utf-8')

    # 3. Construct the MIME message
    msg = MIMEMultipart()
    msg['From'] = SENDER_EMAIL
    msg['To'] = RECEIVER_EMAIL
    msg['Subject'] = "Data Export attached"

    # Attach the CSV file as a separate part
    part = MIMEBase('application', 'octet-stream')
    part.set_payload(encoded_data)

    # Write the encoded data to the message
    with open("attachment.bin", "wb") as out_file:
        out_file.write(part.as_bytes())

    # Attach the file part to the main message
    part.add_header('Content-Disposition', f'attachment; filename="{CSV_FILENAME}"')
    msg.attach(part)

    # 4. Send the email via SMTP
    server = smtplib.SMTP(SMTP_SERVER, SMTP_PORT)
    server.starttls()  # Secure the connection
    server.login(SENDER_EMAIL, PASSWORD)
    text = msg.as_string()
    server.sendmail(SENDER_EMAIL, RECEIVER_EMAIL, text)
    server.quit()

    print("Email sent successfully with attachment!")

except FileNotFoundError:
    print(f"Error: The file {CSV_FILENAME} was not found.")
except Exception as e:
    print(f"An error occurred: {e}")

Best Practices and Developer Insights

As a senior developer, I want to emphasize security and structure. Notice how we moved from trying to use raw sendmail commands to using the dedicated email package. This is crucial because it allows us to correctly handle headers, encoding, and MIME boundaries automatically.

Security Note: Never hardcode passwords directly into production code. For real-world applications, always load sensitive information from environment variables or a secure configuration store. This principle of separating configuration from code is vital in building scalable systems, much like the architectural focus found in modern frameworks like Laravel, where handling data securely and cleanly is paramount.

When dealing with file uploads or attachments on a server, treating the data as streams (binary) rather than simple strings is always the correct approach. This pattern of correctly structuring complex communications is something that developers must master when integrating external services.

Conclusion

Sending an email with a CSV attachment in Python requires understanding how email protocols handle binary data. By leveraging the email package along with Base64 encoding, you can reliably construct a complete MIME message. While it might seem complex initially, mastering this step is fundamental to building robust backend services. Practice separating your concerns—handle file reading separately from network communication—and you will find that even the most complex tasks become straightforward and secure.

Tags:

Enhance your marketing setup with your own email marketing platform.

Join the growing number of SaaS platforms using Laravel Mail to offer email marketing solutions to their customers.