Python smtplib Send Email
Mateen Kiani

Mateen Kiani @kiani0x01

About: Full stack developer.

Location:
Pakistan
Joined:
Jul 24, 2019

Python smtplib Send Email

Publish Date: Aug 5
0 0

Python’s built-in smtplib library makes sending emails from your scripts a breeze. Yet, many developers rush to sample code without understanding the setup quirks that lead to authentication failures or SSL errors. What if a missing PATH entry or a blocked port is the real culprit behind your script’s silent failures?

By digging into the environment and connection details before crafting your message, you’ll save hours of debugging. In this guide, you’ll learn how to configure SMTP, build a proper email, secure the connection, handle attachments, troubleshoot errors, and optimize for bulk sends. With this knowledge, you’ll write reliable email senders that just work.

Environment Setup

Before you can send a single message, you need Python installed and reachable. If your system doesn’t recognize the python command, add Python to your PATH as explained in add Python to your PATH. A virtual environment also keeps dependencies tidy:

python -m venv venv
source venv/bin/activate  # macOS/Linux
venv\Scripts\activate     # Windows
Enter fullscreen mode Exit fullscreen mode

Next, install any helpers like email-validator with pip:

pip install email-validator
Enter fullscreen mode Exit fullscreen mode

Check your SMTP server details. For Gmail, use smtp.gmail.com on port 587 (TLS) or 465 (SSL). Corporate servers may require custom ports or firewalls. Always test connectivity with a simple telnet smtp.server.com 587 before scripting.

Tip: If you face blocked ports, try a different network or contact your IT team early.

Crafting the Message

The core of your script is the email object. Python’s email.message.EmailMessage class makes this neat:

from email.message import EmailMessage

msg = EmailMessage()
msg['Subject'] = 'Test Email'
msg['From'] = 'you@example.com'
msg['To'] = 'friend@example.com'
msg.set_content('Hello! This is a test.')
Enter fullscreen mode Exit fullscreen mode

For HTML content, use add_alternative:

html = """<html><body><h1>Hi!</h1></body></html>"""
msg.add_alternative(html, subtype='html')
Enter fullscreen mode Exit fullscreen mode

Keep headers clear:

  • Subject: Short and descriptive
  • From: Your valid email
  • To: Single or comma-separated list

Tip: Validate email addresses with a library to avoid delivery failures.

Secure Connections

SMTP servers often require SSL or TLS. Use TLS (STARTTLS) on port 587 for compatibility:

import smtplib

with smtplib.SMTP('smtp.gmail.com', 587) as server:
    server.ehlo()
    server.starttls()
    server.ehlo()
    server.login('you@example.com', 'app_password')
    server.send_message(msg)
Enter fullscreen mode Exit fullscreen mode

For SSL on port 465:

with smtplib.SMTP_SSL('smtp.gmail.com', 465) as server:
    server.login('you@example.com', 'app_password')
    server.send_message(msg)
Enter fullscreen mode Exit fullscreen mode

Tip: Use application-specific passwords instead of your main account password.

Adding Files

Attachments enrich your emails, but you need to prepare:

  1. Create an attachments folder if needed. See mkdir if not exist.
  2. Read the file in binary mode.
  3. Guess the MIME type or set it manually.
  4. Attach using add_attachment.

Example:

import mimetypes

filename = 'report.pdf'
ctype, encoding = mimetypes.guess_type(filename)
maintype, subtype = ctype.split('/', 1)

with open(filename, 'rb') as fp:
    msg.add_attachment(fp.read(), maintype=maintype, subtype=subtype, filename=filename)
Enter fullscreen mode Exit fullscreen mode

Tip: For large files, consider streaming or zipping before attaching.

Handling Errors

Email sending can fail silently. Wrap your calls in try/except and log details:

import logging

logging.basicConfig(level=logging.INFO)
try:
    server.send_message(msg)
    logging.info('Email sent')
except smtplib.SMTPException as e:
    logging.error(f'SMTP error: {e}')
except Exception as e:
    logging.error(f'Unexpected error: {e}')
Enter fullscreen mode Exit fullscreen mode

Common issues:

  • Authentication failures: Check credentials and app passwords.
  • Connection timeouts: Verify ports and network.
  • Invalid recipients: Catch SMTPRecipientsRefused.

Tip: Increase server.set_debuglevel(1) to see SMTP dialogue.

Multiple Recipients

Sending to a group needs care. You can place multiple addresses in To, Cc, or Bcc:

recipients = ['a@example.com', 'b@example.com']
msg['To'] = ', '.join(recipients)
server.send_message(msg, from_addr, recipients)
Enter fullscreen mode Exit fullscreen mode

For privacy, use Bcc:

msg['Bcc'] = ', '.join(recipients)
Enter fullscreen mode Exit fullscreen mode

Best practices:

  • Personalize messages in loops if content differs.
  • Respect rate limits: add time.sleep() between sends.
  • Use email.utils.make_msgid() for unique message IDs.

Performance Tips

Bulk sending can overwhelm SMTP servers. Optimize:

  • Reuse the SMTP connection instead of reconnecting for each email.
  • Batch sends in groups (e.g., 50 at a time).
  • Implement exponential backoff on failures.
  • Consider asynchronous libraries like aiosmtplib for high throughput.

Example reuse:

with smtplib.SMTP_SSL('smtp.gmail.com', 465) as server:
    server.login(user, pwd)
    for msg in messages:
        server.send_message(msg)
Enter fullscreen mode Exit fullscreen mode

Tip: Monitor send rates to avoid account lockouts.

Conclusion

By mastering smtplib’s setup, message crafting, security layers, and error handling, you’ll transform email sending from guesswork into a reliable feature. Start with a solid environment, build clear messages, secure your connection, and handle attachments and failures gracefully. Whether you’re notifying users, sending reports, or managing bulk newsletters, these practices will save you time and headaches. Now, fire up your code editor, plug in your SMTP details, and send that first email with confidence!

Takeaway: Reliable email automation starts with understanding each step. Apply these tips to build robust, maintainable scripts that just work.

Comments 0 total

    Add comment