Skip to content

Smtplib Module

The smtplib module provides a Simple Mail Transfer Protocol (SMTP) client for sending emails.

Complexity Reference

Operation Time Space Notes
SMTP() O(1) + network latency O(1) Network connection; latency dominates
sendmail() O(n) O(n) n = message size
send_message() O(n) O(n) n = message size
login() O(n) O(n) n = handshake
quit() O(1) O(1) Close connection

Common Operations

Sending Simple Email

import smtplib
from email.message import EmailMessage

# O(n) where n = connection time
server = smtplib.SMTP('smtp.gmail.com', 587)

# O(n) - TLS handshake
server.starttls()

# O(n) - authenticate where n = handshake time
server.login('your_email@gmail.com', 'your_password')

# O(n) - send message where n = message size
message = EmailMessage()
message['Subject'] = 'Hello'
message['From'] = 'your_email@gmail.com'
message['To'] = 'recipient@example.com'
message.set_content('Email body')

server.send_message(message)

# O(1) - close connection
server.quit()

Sending with Authentication

import smtplib

def send_email(smtp_host, smtp_port, sender, password, recipient, subject, body):
    """Send email with full details - O(n)"""
    # O(n) - establish connection
    with smtplib.SMTP(smtp_host, smtp_port, timeout=10) as server:

        # O(n) - enable encryption
        server.starttls()

        # O(n) - authenticate
        server.login(sender, password)

        # O(n) - compose message where n = message size
        message = f"""Subject: {subject}

{body}"""

        # O(n) - send message
        server.sendmail(sender, [recipient], message)

# Usage - O(n)
send_email(
    'smtp.gmail.com',
    587,
    'user@gmail.com',
    'password',
    'recipient@example.com',
    'Test Subject',
    'Test message body'
)

Common Use Cases

Batch Email Sending

import smtplib
from email.message import EmailMessage

def send_bulk_emails(recipients, subject, body):
    """Send emails to multiple recipients - O(n*m)"""
    # O(n) - connection setup
    with smtplib.SMTP('smtp.gmail.com', 587) as server:
        server.starttls()
        server.login('sender@gmail.com', 'password')

        # O(k*n) where k = recipients, n = avg message size
        for recipient in recipients:
            message = EmailMessage()
            message['Subject'] = subject
            message['From'] = 'sender@gmail.com'
            message['To'] = recipient
            message.set_content(body)

            # O(n) to send each message
            server.send_message(message)

# Usage - O(k*n)
recipients = ['user1@example.com', 'user2@example.com', 'user3@example.com']
send_bulk_emails(recipients, 'Newsletter', 'Monthly update...')

Sending HTML Email with Attachments

import smtplib
from email.message import EmailMessage

def send_html_email(recipient, subject, html_body, attachments=None):
    """Send HTML email with attachments - O(n)"""
    # O(1) - create message
    message = EmailMessage()
    message['Subject'] = subject
    message['From'] = 'sender@gmail.com'
    message['To'] = recipient

    # O(n) - set HTML content where n = body length
    message.set_content('Plain text version')
    message.add_alternative(html_body, subtype='html')

    # O(k*m) where k = attachments, m = avg file size
    if attachments:
        for filename, filedata in attachments.items():
            message.add_attachment(
                filedata,
                maintype='application',
                subtype='octet-stream',
                filename=filename
            )

    # O(n) - send
    with smtplib.SMTP('smtp.gmail.com', 587) as server:
        server.starttls()
        server.login('sender@gmail.com', 'password')
        server.send_message(message)  # O(n)

# Usage - O(n)
html = '<html><body><h1>Hello</h1></body></html>'
send_html_email('user@example.com', 'HTML Email', html)

Email Queue System

import smtplib
from email.message import EmailMessage
from queue import Queue
import threading

class EmailQueue:
    """Background email queue - O(1) add, O(n) send"""

    def __init__(self, host, port, username, password):
        self.queue = Queue()
        self.host = host
        self.port = port
        self.username = username
        self.password = password

        # Start background worker - O(1)
        self.worker = threading.Thread(target=self._process_queue, daemon=True)
        self.worker.start()

    def queue_email(self, to, subject, body):
        """Add to queue - O(1)"""
        # O(1) - queue operation
        self.queue.put({
            'to': to,
            'subject': subject,
            'body': body
        })

    def _process_queue(self):
        """Background worker - O(n) per email"""
        # O(n) - connect once
        server = smtplib.SMTP(self.host, self.port)
        server.starttls()
        server.login(self.username, self.password)

        try:
            while True:
                # O(1) - get from queue
                email_data = self.queue.get()

                # O(n) - create and send
                message = EmailMessage()
                message['Subject'] = email_data['subject']
                message['From'] = self.username
                message['To'] = email_data['to']
                message.set_content(email_data['body'])

                server.send_message(message)
        finally:
            server.quit()

# Usage - O(1) to queue
queue = EmailQueue('smtp.gmail.com', 587, 'user@gmail.com', 'password')
queue.queue_email('user1@example.com', 'Subject 1', 'Body 1')  # O(1)
queue.queue_email('user2@example.com', 'Subject 2', 'Body 2')  # O(1)
# Background worker sends - O(n) per email

Error Handling and Retry

import smtplib
import time

def send_email_with_retry(host, port, sender, password, recipient, subject, body, retries=3):
    """Send with retry logic - O(n*r)"""
    # O(r*n) where r = retries, n = operation time
    for attempt in range(retries):
        try:
            # O(n) - send
            with smtplib.SMTP(host, port, timeout=10) as server:
                server.starttls()
                server.login(sender, password)

                message = f"Subject: {subject}\n\n{body}"
                server.sendmail(sender, [recipient], message)

            return True  # Success

        except smtplib.SMTPException as e:
            print(f"Attempt {attempt + 1} failed: {e}")

            if attempt < retries - 1:
                # O(1) - wait before retry
                time.sleep(2 ** attempt)  # Exponential backoff

    return False  # Failed after retries

# Usage - O(n*r)
success = send_email_with_retry(
    'smtp.gmail.com',
    587,
    'user@gmail.com',
    'password',
    'recipient@example.com',
    'Test',
    'Body'
)

Performance Tips

Reuse SMTP Connection

import smtplib

# Bad: Create connection for each email - O(n*k) per email
for recipient in recipients:
    server = smtplib.SMTP('smtp.gmail.com', 587)  # O(n)
    server.starttls()
    server.login(user, pwd)
    server.sendmail(user, recipient, msg)  # O(m)
    server.quit()  # O(1)

# Good: Reuse connection - O(n + k*m)
with smtplib.SMTP('smtp.gmail.com', 587) as server:  # O(n)
    server.starttls()
    server.login(user, pwd)

    for recipient in recipients:
        server.sendmail(user, recipient, msg)  # O(m) per email

Batch Multiple Recipients

import smtplib

def send_to_list(recipients, message):
    """Send to multiple recipients in one call - O(n)"""
    # O(n) - connection and auth
    with smtplib.SMTP('smtp.gmail.com', 587) as server:
        server.starttls()
        server.login(user, password)

        # O(n) - single sendmail call for all
        server.sendmail(user, recipients, message)

# Usage - O(n) where n = total recipient count
recipients = ['user1@example.com', 'user2@example.com']
send_to_list(recipients, msg_text)  # More efficient than multiple calls

Use Context Manager

import smtplib

# Good: Automatic cleanup with context manager
with smtplib.SMTP('smtp.gmail.com', 587) as server:  # O(n)
    server.starttls()
    server.login('user', 'pass')
    server.sendmail('from', 'to', msg)  # O(m)
# quit() called automatically

Version Notes

  • Python 2.6+: Core functionality
  • Python 3.x: All features available
  • Python 3.6+: EmailMessage support