Skip to main content

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 with statement 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.

  1. Open Google AccountSecurity
  2. Enable 2-Step Verification
  3. Open App Passwords → Generate a new password
  4. Use that password as password_email

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

imap_reader-1.0.3.tar.gz (15.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

imap_reader-1.0.3-py3-none-any.whl (13.5 kB view details)

Uploaded Python 3

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

Hashes for imap_reader-1.0.3.tar.gz
Algorithm Hash digest
SHA256 db3f13c42ef4c009b3668a353e3c051d41f50bac0dea3f993f88dfbbcecc6310
MD5 518a2a1279a52cae75ac56247fd9fd91
BLAKE2b-256 b2984c1f406b97da3d51c2f151eac573d6eeb677f22851d49510414bc3a4c7d0

See more details on using hashes here.

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

Hashes for imap_reader-1.0.3-py3-none-any.whl
Algorithm Hash digest
SHA256 1069ddb6d661d31003f19ef990c6d96299e31b466df83be208acb5a3ff7a6551
MD5 5897a65a30551a014ce58d80768dcc9d
BLAKE2b-256 32d61160572ad26ebbebac06ce410ffd4ec27be8d12988c3db021f40944ed759

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page