TechSchool
Home / Python3 / Python imaplib

Python imaplib

This page shows how to read email with the Python imaplib module. It covers a secure connection, login, search, fetch, parsing, and logout.

Connect and log in

Use imaplib.IMAP4_SSL to open a secure connection to the mail server. The host name goes in the constructor. The default port is 993.

Call the login method with a user name and an app password. Do not put the password in the source file. Read it from an environment variable.

The code below reads two environment variables and logs in. A correct login returns the string OK. A wrong password raises an imaplib.IMAP4.error exception.

import imaplib
import os

host = "imap.example.com"
user = os.environ["MAIL_USER"]
password = os.environ["MAIL_APP_PASSWORD"]

mail = imaplib.IMAP4_SSL(host)
mail.login(user, password)
print(mail.state)

Select the inbox and search

Call select with the folder name to open a mailbox. The name INBOX is the standard inbox on most servers. The method returns the number of messages.

Call search with None as the charset and a search string. The string UNSEEN finds messages that no one has read. The result is a list of message numbers.

The code below selects the inbox and searches for unread mail. The numbers in the result are byte strings. You pass one number to fetch in the next step.

status, count = mail.select("INBOX")
print(status, count)

status, data = mail.search(None, "UNSEEN")
numbers = data[0].split()
print(numbers)

Fetch and parse a message

Call fetch with a message number and the string (RFC822). This returns the full message, including the headers and the body. The body is in the second item of the result.

Pass the raw bytes to email.message_from_bytes. This function builds a message object. Read the subject with the get method on the subject header.

For a multipart message, walk the parts and find the part with the text/plain type. Call get_payload with decode set to True to get the text. The code below prints the subject and the text body.

import email

status, data = mail.fetch(numbers[0], "(RFC822)")
msg = email.message_from_bytes(data[0][1])
print(msg["Subject"])

for part in msg.walk():
    if part.get_content_type() == "text/plain":
        print(part.get_payload(decode=True).decode())
        break

Close and log out

Call close to release the selected mailbox. Then call logout to end the session. Both methods return a status string.

Always log out when your script ends. An open session can hold server resources. The code below closes the mailbox and logs out.

mail.close()
mail.logout()

Keep credentials out of the source

Never write a password in a Python file. A file in a repository can leak the password to other people. Store the password in an environment variable instead.

Set the variable in your shell before you run the script. The os.environ lookup reads the value at run time. If the variable is missing, the script stops with a KeyError.

Gmail does not accept your normal account password for IMAP. You must turn on two-step verification and create an app password. Use that 16-character app password in the login call.

Gmail folder names

Gmail uses special folder names. The inbox is INBOX. Sent mail is in the folder named "[Gmail]/Sent Mail".

Drafts are in "[Gmail]/Drafts". All mail is in "[Gmail]/All Mail". Trash is in "[Gmail]/Trash".

Put these names in quotes in the select call. A wrong name returns a NO status. Call list to print the exact folder names on your account.

Common questions

Why does my login fail on Gmail?

Gmail rejects your normal password for IMAP. Turn on two-step verification and create an app password, then use that value in the login call.

How do I read the body of a multipart email?

Call walk on the message object and find the part with the text/plain type. Then call get_payload with decode set to True.

Is imaplib still maintained?

Yes. imaplib is part of the Python standard library and receives fixes with each Python release.

Continue learning
Python3 module Python3 file io Python3 class