Python
HTML Email
Email Automation
Programming
Email Development

Send HTML emails with Python

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Sending HTML email from Python is mostly about building the message correctly and using SMTP securely. The practical default is a multipart email with both plain-text and HTML versions, because mail clients and spam filters handle that more reliably than HTML-only messages.

Build the Message With EmailMessage

Modern Python code should usually use email.message.EmailMessage instead of manually assembling MIME parts.

python
1import smtplib
2from email.message import EmailMessage
3
4msg = EmailMessage()
5msg["Subject"] = "Weekly report"
6msg["From"] = "[email protected]"
7msg["To"] = "[email protected]"
8
9msg.set_content("This email contains an HTML version of the report.")
10
11msg.add_alternative("""
12<html>
13  <body>
14    <h1>Weekly report</h1>
15    <p>The build completed successfully.</p>
16    <p><a href="https://example.com/reports/weekly">View the full report</a></p>
17  </body>
18</html>
19""", subtype="html")

That gives you a clean multipart message with a text fallback and an HTML body.

Send It Through SMTP Securely

Once the message exists, send it over SMTP with TLS.

python
1import os
2import smtplib
3
4password = os.environ["SMTP_PASSWORD"]
5
6with smtplib.SMTP("smtp.example.com", 587) as smtp:
7    smtp.starttls()
8    smtp.login("[email protected]", password)
9    smtp.send_message(msg)

Using an environment variable is much safer than hardcoding a password in the script.

Why the Plain-Text Part Matters

Some developers send only HTML because that is what the user sees in a rich mail client. A plain-text part is still worth including because:

  • some clients display only text
  • spam filters often prefer well-formed multipart messages
  • accessibility tools may rely on the text version
  • some environments strip HTML for security reasons

So the combination of set_content() and add_alternative() is usually the right baseline.

Keep HTML Conservative

HTML email is not the same as browser HTML. Many clients ignore advanced CSS, strip scripts, or render layout inconsistently.

A conservative example:

python
1html = """
2<html>
3  <body style="font-family: Arial, sans-serif; color: #222;">
4    <h2 style="color: #0b6;">Deployment complete</h2>
5    <p>The new release was deployed successfully.</p>
6  </body>
7</html>
8"""

In email, simple inline styling is usually more reliable than external stylesheets or modern layout techniques.

Add Attachments When Needed

EmailMessage also makes attachments straightforward.

python
1from pathlib import Path
2
3report_path = Path("report.csv")
4
5with report_path.open("rb") as f:
6    msg.add_attachment(
7        f.read(),
8        maintype="text",
9        subtype="csv",
10        filename=report_path.name
11    )

The message should still make sense without the attachment whenever possible, because some recipients or filters may block attachments.

Send to Multiple Recipients Carefully

You can send to more than one visible recipient by joining the addresses:

python
recipients = ["[email protected]", "[email protected]"]
msg["To"] = ", ".join(recipients)

If you need private recipients, use blind-copy handling carefully and keep in mind that headers and SMTP envelope recipients are not identical concepts.

Test Rendering and Delivery Separately

A message can be syntactically correct and still render badly or land in spam. Testing should cover:

  • MIME structure
  • HTML rendering in real mail clients
  • authentication success
  • deliverability and spam handling

A sandbox SMTP server or mail-testing service is usually better than blasting repeated tests into a production mailbox.

Common Pitfalls

The biggest mistake is sending HTML-only email without a plain-text alternative. That reduces compatibility and can hurt deliverability.

Another common issue is hardcoding SMTP credentials in source code. Use environment variables, app passwords, or another secret-management mechanism instead.

People also copy browser-style HTML and expect it to render the same way in email clients. Email HTML support is much more limited.

Finally, do not confuse successful SMTP submission with actual inbox delivery. Deliverability also depends on reputation and mail-domain setup such as SPF, DKIM, and DMARC.

Summary

  • Use EmailMessage to build multipart email with both text and HTML content.
  • Send mail through SMTP with TLS and environment-based credentials.
  • Keep HTML simple because email clients support less than browsers do.
  • Add attachments and multiple recipients carefully, with deliverability in mind.
  • Test structure, rendering, and delivery separately because successful sending is only part of the problem.

Course illustration
Course illustration

All Rights Reserved.