Simple Python library for reading verification emails via IMAP — OTP extraction, link parsing, auto IMAP detection.
Project description
imap_reader
Simple, clean Python library for reading verified emails via IMAP — with OTP extraction, verification link parsing, and auto IMAP detection.
Zero external dependencies — uses Python standard library only (imaplib, email, re, threading).
Installation
pip install imap_reader
or
pip install git+https://github.com/nabilunnuha/imap_reader.git
Quick Start
from imap_reader import ImapClient
with ImapClient(
email_address="user@gmail.com",
password_email="your_app_password",
) as client:
for msg in client.get_messages("UNSEEN"):
print(msg.subject)
print(msg.otp_codes) # ['123456']
print(msg.verification_links) # ['https://example.com/verify?token=...']
Features
- IMAP auto-detect — automatically detects the IMAP host of an email domain
- OTP extraction — extracts a 5–6-digit code from the email body
- Verification link extraction — finds and prioritizes verification/confirmation links
- HTML email parser — converts HTML emails to plain text for extraction
- Login timeout — raises an error if the connection timeout is exceeded (30–60 seconds)
- Context manager — use the
withstatement to automatically close the connection - Delete messages — delete emails based on criteria (
ALL,SEEN,UNSEEN) - Wait for message — poll the mailbox until a matching email arrives or timeout is reached
- Message count — get the number of matching emails without fetching content
- List folders — discover all available IMAP folders on the server
- Mark as read/unread — mark emails without deleting them
- Sender & subject filter — filter emails by sender or subject keyword directly via IMAP
- Type hints — fully typed, IDE-friendly, and autocomplete-friendly
Usage
Basic — Read unseen emails
from imap_reader import ImapClient
with ImapClient("user@gmail.com", "app_password") as client:
for msg in client.get_messages("UNSEEN"):
print(f"From : {msg.sender}")
print(f"Subject : {msg.subject}")
print(f"OTP : {msg.otp_codes}")
print(f"Links : {msg.verification_links}")
Retrieve only 1 recent email
with ImapClient("user@gmail.com", "app_password") as client:
msg = client.get_latest_message("UNSEEN")
if msg:
print(msg.otp_codes)
Custom timeout (30–60 seconds recommended)
with ImapClient(
email_address="user@gmail.com",
password_email="app_password",
timeout=60, # seconds
) as client:
msg = client.get_latest_message()
Read from specified folder
with ImapClient("user@gmail.com", "app_password") as client:
for msg in client.get_messages("ALL", folder="SPAM"):
print(msg.subject)
Filter by sender or subject
Filter emails directly at the IMAP level — faster than fetching all and filtering in Python.
with ImapClient("user@gmail.com", "app_password") as client:
# Filter by sender
for msg in client.get_messages("UNSEEN", from_sender="noreply@github.com"):
print(msg.otp_codes)
# Filter by subject keyword
for msg in client.get_messages("UNSEEN", subject_contains="verification code"):
print(msg.subject)
# Combine both filters
msg = client.get_latest_message(
"UNSEEN",
from_sender="noreply@google.com",
subject_contains="OTP",
)
Wait for a new email (polling)
Polls the mailbox until a matching email arrives or the timeout is reached. Ideal for automation workflows that need to wait for an OTP after triggering a login or signup.
with ImapClient("user@gmail.com", "app_password") as client:
msg = client.wait_for_message(
from_sender="noreply@github.com",
subject_contains="verification",
wait_timeout=90, # max seconds to wait
interval=5, # check every 5 seconds
on_waiting=lambda elapsed: print(f"Waiting... {elapsed}s"),
)
if msg:
print("OTP:", msg.otp_codes[0])
else:
print("Timed out, no email received.")
Count emails without fetching content
with ImapClient("user@gmail.com", "app_password") as client:
total = client.get_message_count("ALL")
unread = client.get_message_count("UNSEEN")
print(f"{unread} unread of {total} total")
# Also supports sender/subject filters
count = client.get_message_count("UNSEEN", from_sender="noreply@github.com")
List all available folders
with ImapClient("user@gmail.com", "app_password") as client:
folders = client.list_folders()
print(folders)
# ['INBOX', 'Sent', 'Spam', 'Trash', '[Gmail]/All Mail', ...]
Mark emails as read / unread
with ImapClient("user@gmail.com", "app_password") as client:
# Mark all unread emails as read
count = client.mark_as_read("UNSEEN")
print(f"Marked {count} emails as read.")
# Mark all read emails as unread
count = client.mark_as_unread("SEEN")
print(f"Marked {count} emails as unread.")
Delete email
with ImapClient("user@gmail.com", "app_password") as client:
# Delete all read emails
count = client.delete_messages("SEEN")
print(f"{count} deleted emails")
# Delete all emails in INBOX
count = client.delete_messages("ALL", folder="INBOX")
# Delete unread emails in SPAM
count = client.delete_messages("UNSEEN", folder="SPAM")
Add custom IMAP provider
from imap_reader import ImapClient
from imap_reader.models import DEFAULT_IMAP_LIST, ImapItemModel
DEFAULT_IMAP_LIST.append(
ImapItemModel(domain="mycompany.com", imap_host="imap.mycompany.com")
)
with ImapClient("user@mycompany.com", "password") as client:
for msg in client.get_messages():
print(msg)
Error handling
from imap_reader import ImapClient
from imap_reader.exceptions import (
AuthenticationError,
ConnectionTimeoutError,
ImapNotFoundError,
)
try:
with ImapClient("user@gmail.com", "wrong_password", use_default_imap=False, timeout=10) as client:
msg = client.get_latest_message()
except AuthenticationError:
print("Email or password is incorrect.")
except ConnectionTimeoutError:
print("Connection to IMAP server timed out.")
except ImapNotFoundError:
print("Email domain not recognized, add manually to imap_list.")
API Reference
ImapClient(email_address, password_email, imap_list=None, use_default_imap=True, timeout=30)
| Parameter | Type | Default | Description |
|---|---|---|---|
email_address |
str |
— | Complete email address |
password_email |
str |
— | IMAP password or App Password |
imap_list |
list |
DEFAULT_IMAP_LIST |
List of IMAP providers |
use_default_imap |
bool |
True |
Auto-fallback to mail.{domain} |
timeout |
int |
30 |
Login timeout in seconds |
Methods
| Method | Returns | Description |
|---|---|---|
get_messages(criteria, folder, limit, mark_seen, from_sender, subject_contains) |
Generator[EmailMessage] |
Fetch and yield messages |
get_latest_message(criteria, folder, from_sender, subject_contains) |
EmailMessage | None |
Fetch 1 most recent message |
wait_for_message(criteria, folder, from_sender, subject_contains, wait_timeout, interval, on_waiting) |
EmailMessage | None |
Poll until email arrives or timeout |
get_message_count(criteria, folder, from_sender, subject_contains) |
int |
Count matching messages without fetching |
list_folders() |
list[str] |
List all available IMAP folders |
mark_as_read(criteria, folder) |
int |
Mark matching messages as read |
mark_as_unread(criteria, folder) |
int |
Mark matching messages as unread |
delete_messages(criteria, folder) |
int |
Delete matching messages |
EmailMessage
| Attribute | Type | Description |
|---|---|---|
uid |
str |
Unique message ID on the server |
subject |
str |
Decoded email subject |
sender |
str |
Decoded sender address |
date |
str |
Raw date string from email header |
body_text |
str |
Plain text body |
body_html |
str |
HTML body |
otp_codes |
list[str] |
Extracted 5–6 digit OTP codes |
verification_links |
list[str] |
Extracted verification/confirm links |
Gmail Setup
Gmail requires an App Password, not your regular password.
- Open Google Account → Security
- Enable 2-Step Verification
- Open App Passwords → Generate a new password
- Use that password as
password_email
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file imap_reader-1.0.3.tar.gz.
File metadata
- Download URL: imap_reader-1.0.3.tar.gz
- Upload date:
- Size: 15.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
db3f13c42ef4c009b3668a353e3c051d41f50bac0dea3f993f88dfbbcecc6310
|
|
| MD5 |
518a2a1279a52cae75ac56247fd9fd91
|
|
| BLAKE2b-256 |
b2984c1f406b97da3d51c2f151eac573d6eeb677f22851d49510414bc3a4c7d0
|
File details
Details for the file imap_reader-1.0.3-py3-none-any.whl.
File metadata
- Download URL: imap_reader-1.0.3-py3-none-any.whl
- Upload date:
- Size: 13.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1069ddb6d661d31003f19ef990c6d96299e31b466df83be208acb5a3ff7a6551
|
|
| MD5 |
5897a65a30551a014ce58d80768dcc9d
|
|
| BLAKE2b-256 |
32d61160572ad26ebbebac06ce410ffd4ec27be8d12988c3db021f40944ed759
|