Join us and get access to thousands of tutorials and a community of expert Pythonistas.

Unlock This Lesson

This lesson is for members only. Join us and get access to thousands of tutorials and a community of expert Pythonistas.

Unlock This Lesson

Sending HTML Emails With Python

In this video, you’ll learn how to format your text to send valid HTML. Therefore, you’ll make use of the MIME standard, which you can access through Python’s email.mime module.

00:00 In the last video, you learned how to send a plain text email. This is pretty cool! But you still can’t add any formatting or attachments this way. Now you’re going to learn how to harness the power of HTML to allow for formatting and adding links. This is going to use the MIME standard, which stands for Multipurpose Internet Mail Extensions. And as you can imagine, Python has a library for this. So going back to the plain text email script, you’re going to add some more imports.

00:27 So from email.mime.text import MIMEText. This is a class that will contain the body information, so either your HTML or plain text body text.

00:42 Then, from email.mime.multipart import MIMEMultipart. You can think of this as the envelope which will contain your header information and then different MIMEText or attachment items. So now you’re going to group these together a little better, and then go ahead and make a new variable called message and set this equal to a MIMEMultipart(),

01:11 and pass in 'alternative'. This is because you’re going to send it two different body texts—one that’s plain text and one that’s HTML. And you need to do this because some mail clients will not display HTML content.

01:25 This just lets them know that there’s a plain text version that they can see. Next, set up your headers again. So, message['Subject'], just set it equal to something like 'Multipart Test'.

01:45 message['From'] you can set equal to the sender. And message['To'],

01:57 set that equal to the receiver. All right. Now that message is this MIMEMultipart, we should change the name here. And because this will be plain text, just call it something like text. And you can delete everything out from in here.

02:12 And let’s just set this equal to something like, """Hi, How are you?""", """Real Python has many great tutorials!""" And we’ll just put, like, a plain text link down here.

02:27 And you can get rid of this because there’s no more variables inside. Now create a new variable called html and do another multiline.

02:42 And I’m just going to type some basic HTML in here and speed it up so you don’t have to watch me type it all out.

03:13 Okay. So this is just some basic HTML with the <html> tag, a <body>. And then all inside this paragraph, you’ve got a couple of line breaks (<br>) and then this link here. Cool!

03:24 So now let’s go ahead and you’re going to make a—ah. Turn off caps lock. We’ll just call something part1 and set this equal to a MIMEText(), and because this is the plain text, you can just pass in text like that.

03:42 And call this 'plain'. Then for part2, make another MIMEText(). Now pass in that html, and because this is in HTML formatting, just pass in 'html' right there. To get these into the message, or the envelope from before, you can just call .attach() on message and attach part1 and attach part2, just like that.

04:09 The reason html is being attached after the plain text is because it’ll go from the last attached to the first attached. So whichever one you attach last is kind of the preferred one.

04:21 So if the email client is able to read HTML and display HTML, it’ll grab that one. Otherwise it’ll grab the first one. If you were to flip these, you would always get the plain text. Okay.

04:34 As for encryption, the context should be the same. You’re going to connect to the email server the same way. And now that message is this multipart object, you need to call .as_string() on it because SMTP needs to have, like, plain text characters, so this is just going to encode the object as a string of ASCII characters so that it can be sent using SMTP. Okay! So let’s try it out.

05:02 You’re going to save this, open up a terminal.

05:09 Enter the password.

05:14 And we didn’t see any errors! So let’s check this out. All right. We’re in the email, let me try refreshing. Hey, look at that! Multipart Test. Let me click on this.

05:27 It may look just like the plain text version, but you have a link in here. So if I click on this, it brings me right to Real Python. So, there you go! This was really straightforward HTML, and you can go as crazy as you want with your HTML and make some really professional-looking emails that you can then send using Python. In the next video, you’re going to learn how to add attachments using this MIME standard.

05:52 Thanks for watching.

konk on May 24, 2019

This worked as expected sending the email to my gmail account. It however didn’t work (no email received) sending to my xfinity account. Previous steps had worked sending plain-text to xfinity on port 465, but not now.

Joe Tatusko RP Team on May 24, 2019

Hi konk,

Have you tried checking your spam folder on your xfinity account?

Take a look at your sent messages from the email account you’re using to send the email to confirm that everything is correct.

Let me know if that works!

konk on May 24, 2019

Thanks for response. No, spam folder is empty. Unused account so easy to see all emails in inbox/ spam/ eleswhere.

Pygator on Aug. 25, 2019

Will this process work for outlook emails and university emails? Happy Pythoning.

Joe Tatusko RP Team on Aug. 27, 2019

It can, but it depends on the university mail server. In the course example, your process looks like this:

user -> Python script -> mail server (gmail in this case)

The Microsoft Outlook desktop client replaces the Python script, but it still receives and sends mail by interacting with the mail server. If this mail server is your university email, you’ll need the host and port information of the server. There is also a chance that the server will block these connections due to different security protocols or general configurations.

You could always take a look at the university mail configuration details, and this might point you in the right direction. Let me know if that helps!

gracetan on April 23, 2020

Hi Joe, thank you for your tutorial. I actually got two same body msg at my emails, I don’t know why, I followed what you said. Hi, how are you doing? testing email. Hi, how are you doing? testing email.

KatMac on April 23, 2021


The email was received with the image attachment at the receiver’s email address. However, the image (logo.jpg) I added, was embedded in the email with the word ‘noname’ on it and no file extension.

How can I fix this issue.

dstricks on Jan. 18, 2022

I’d recommend checking out email.message.EmailMessage as it abstracts away some of the multipart code. To achieve the same shown in the video, it would be something like:

message = EmailMessage()
message['Subject'] = "Multipart Test"
message['From'] = sender
message['To'] = receiver

html = "" # INSERT HTML HERE

message.add_alternative(html, subtype='html')

# some other code


Become a Member to join the conversation.