Python's imaplib module lets you connect to IMAP servers to read, search, and manage email. Unlike POP3, IMAP keeps emails on the server and supports folders.
Basic Connection
import imaplib
# Connect with SSL
mail = imaplib.IMAP4_SSL('imap.gmail.com')
mail.login('you@gmail.com', 'app-password')
# List folders
status, folders = mail.list()
for folder in folders:
print(folder.decode())
mail.logout()Reading Inbox
import imaplib
import email
from email.header import decode_header
mail = imaplib.IMAP4_SSL('imap.gmail.com')
mail.login('you@gmail.com', 'app-password')
# Select inbox
mail.select('INBOX')
# Search for all emails
status, messages = mail.search(None, 'ALL')
message_ids = messages[0].split()
# Get latest 5 emails
for msg_id in message_ids[-5:]:
status, data = mail.fetch(msg_id, '(RFC822)')
raw_email = data[0][1]
msg = email.message_from_bytes(raw_email)
# Decode subject
subject, encoding = decode_header(msg['Subject'])[0]
if isinstance(subject, bytes):
subject = subject.decode(encoding or 'utf-8')
print(f"From: {msg['From']}")
print(f"Subject: {subject}")
print("---")
mail.logout()Searching Emails
import imaplib
mail = imaplib.IMAP4_SSL('imap.gmail.com')
mail.login('you@gmail.com', 'app-password')
mail.select('INBOX')
# Search criteria
status, msgs = mail.search(None, 'UNSEEN') # Unread
status, msgs = mail.search(None, 'FROM', '"sender@example.com"')
status, msgs = mail.search(None, 'SUBJECT', '"invoice"')
status, msgs = mail.search(None, 'SINCE', '"01-Jan-2026"')
status, msgs = mail.search(None, 'BEFORE', '"31-Dec-2026"')
# Combined search
status, msgs = mail.search(None, 'UNSEEN', 'FROM', '"boss@company.com"')
# All criteria
status, msgs = mail.search(None, '(OR FROM "alice" FROM "bob")')Reading Email Body
import imaplib
import email
def get_email_body(msg):
"""Extract text body from email message."""
if msg.is_multipart():
for part in msg.walk():
content_type = part.get_content_type()
disposition = str(part.get('Content-Disposition'))
if content_type == 'text/plain' and 'attachment' not in disposition:
return part.get_payload(decode=True).decode()
else:
return msg.get_payload(decode=True).decode()
return None
mail = imaplib.IMAP4_SSL('imap.gmail.com')
mail.login('you@gmail.com', 'app-password')
mail.select('INBOX')
status, messages = mail.search(None, 'ALL')
latest_id = messages[0].split()[-1]
status, data = mail.fetch(latest_id, '(RFC822)')
msg = email.message_from_bytes(data[0][1])
body = get_email_body(msg)
print(body)Downloading Attachments
import imaplib
import email
import os
def save_attachments(msg, download_folder='attachments'):
os.makedirs(download_folder, exist_ok=True)
for part in msg.walk():
if part.get_content_maintype() == 'multipart':
continue
if part.get('Content-Disposition') is None:
continue
filename = part.get_filename()
if filename:
filepath = os.path.join(download_folder, filename)
with open(filepath, 'wb') as f:
f.write(part.get_payload(decode=True))
print(f"Saved: {filepath}")
mail = imaplib.IMAP4_SSL('imap.gmail.com')
mail.login('you@gmail.com', 'app-password')
mail.select('INBOX')
status, data = mail.fetch(b'123', '(RFC822)')
msg = email.message_from_bytes(data[0][1])
save_attachments(msg)Working with Folders
import imaplib
mail = imaplib.IMAP4_SSL('imap.gmail.com')
mail.login('you@gmail.com', 'app-password')
# List all folders
status, folders = mail.list()
for folder in folders:
print(folder.decode())
# Select a folder
mail.select('INBOX')
mail.select('[Gmail]/Sent Mail')
mail.select('[Gmail]/Drafts')
# Create folder
mail.create('MyFolder')
# Delete folder
mail.delete('MyFolder')
# Rename folder
mail.rename('OldName', 'NewName')Managing Emails
import imaplib
mail = imaplib.IMAP4_SSL('imap.gmail.com')
mail.login('you@gmail.com', 'app-password')
mail.select('INBOX')
# Mark as read
mail.store(b'123', '+FLAGS', '\\Seen')
# Mark as unread
mail.store(b'123', '-FLAGS', '\\Seen')
# Star/flag email
mail.store(b'123', '+FLAGS', '\\Flagged')
# Delete (move to trash)
mail.store(b'123', '+FLAGS', '\\Deleted')
mail.expunge() # Permanently remove deleted
# Move to folder (copy + delete)
mail.copy(b'123', 'Archive')
mail.store(b'123', '+FLAGS', '\\Deleted')
mail.expunge()Context Manager Pattern
import imaplib
from contextlib import contextmanager
@contextmanager
def imap_connection(host, user, password):
mail = imaplib.IMAP4_SSL(host)
try:
mail.login(user, password)
yield mail
finally:
try:
mail.close()
mail.logout()
except:
pass
# Usage
with imap_connection('imap.gmail.com', 'user', 'pass') as mail:
mail.select('INBOX')
status, messages = mail.search(None, 'UNSEEN')
print(f"Unread: {len(messages[0].split())}")Fetch Options
import imaplib
mail.select('INBOX')
# Just headers (faster)
status, data = mail.fetch(b'123', '(BODY[HEADER])')
# Just specific headers
status, data = mail.fetch(b'123', '(BODY[HEADER.FIELDS (FROM SUBJECT DATE)])')
# Full message
status, data = mail.fetch(b'123', '(RFC822)')
# Envelope data
status, data = mail.fetch(b'123', '(ENVELOPE)')
# Size only
status, data = mail.fetch(b'123', '(RFC822.SIZE)')Gmail-Specific
import imaplib
mail = imaplib.IMAP4_SSL('imap.gmail.com')
mail.login('you@gmail.com', 'app-password')
# Gmail uses X-GM-RAW for Gmail search syntax
status, messages = mail.search(None, 'X-GM-RAW', '"has:attachment"')
status, messages = mail.search(None, 'X-GM-RAW', '"in:anywhere"')
status, messages = mail.search(None, 'X-GM-RAW', '"label:important"')Error Handling
import imaplib
try:
mail = imaplib.IMAP4_SSL('imap.gmail.com')
mail.login('you@gmail.com', 'wrong-password')
except imaplib.IMAP4.error as e:
print(f"IMAP error: {e}")
except ConnectionRefusedError:
print("Could not connect to server")
finally:
try:
mail.logout()
except:
passSummary
imaplib provides full IMAP access:
IMAP4_SSL()for secure connectionssearch()for finding emailsfetch()for downloading messagesstore()for managing flagsselect()for choosing folders
Combine with the email module to parse message content and attachments. For Gmail, use App Passwords with 2FA enabled.
React to this post: