Library / SDK
mjs/imapclient avatar
mjs/imapclient

IMAPClient: a Pythonic IMAP library that wraps imaplib properly

An easy-to-use, Pythonic and complete IMAP client library

564 stars100 forksPythonNOASSERTION

At a glance

What is it?
IMAPClient is a Python library that sits on top of the standard library's imaplib and fixes the two things imaplib gets wrong for real use, unparsed server responses and call-by-return-value error handling. It handles UIDs, internationalised mailbox names and time zones transparently, and it ships an interactive console and a live test harness for checking a server implementation.
Who is it for?
Adopt IMAPClient if you need to talk to a real IMAP server from Python, because the two problems it solves, parsing server responses and detecting errors, are exactly the ones that make hand-written imaplib code fragile. Do not adopt it if you are writing a one-off script that only fetches a single message, because the setup cost exceeds the benefit.
Can I use it commercially?
Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
Is it still maintained?
Yes. The repository last received commits 13 days ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The two problems with imaplib

The readme poses the question directly: why create another IMAP client library for Python, when the standard library already has imaplib?

The answer it gives is specific and worth reading in full, because it explains why the library exists rather than asserting that it is better.

The first problem is that imaplib is very low-level. It expects string values where lists or tuples would be more appropriate, and it returns server responses almost unparsed. As IMAP server responses can be quite complex, this means everyone using imaplib ends up writing their own fragile parsing routines. That is the real cost. The protocol is not the hard part of IMAP, the response format is, and every project that touches it ends up reimplementing the same fragile parser.

The second problem is that imaplib does not make good use of exceptions, which means you need to check the return value of each call to see if what you just did was successful. For a protocol where almost every command can fail, that turns each call into a two line pattern of call and check, and it makes it easy to forget the check.

IMAPClient actually uses imaplib internally, and the readme is honest that this may change at some point in the future. So it is not a from scratch protocol implementation. It is a layer that takes the low-level interface and turns it into the shape Python programmers expect.

That framing matters for the dependency picture. There is no third-party dependency to speak of, so installing it is cheap and the supply chain surface is small.

What the library hides for you

The features list is seven items, and three of them are about things that are genuinely easy to get subtly wrong.

Arguments and return values are natural Python types. IMAP server responses are fully parsed and readily usable. Those two are the imaplib problems above.

The third is that IMAP unique message IDs, the UIDs, are handled transparently, with no need to call different methods to use UIDs. UIDs are the server's own stable identifiers for messages, and they are what you need for anything that has to survive a client reconnect, because message sequence numbers get renumbered the moment something is deleted. A library that makes the UID variant of every method a separate call doubles the API surface and doubles the chance of using the wrong one. IMAPClient removes that choice.

The fourth is that escaping for internationalised mailbox names is transparently handled. Unicode mailbox names may be passed as input wherever a folder name is accepted. That is a small feature that removes an entire category of bug, because IMAP mailbox names are modified UTF-7 encoded on the wire, and the encoding rules have enough corner cases that a hand-rolled version is a common source of mangled folder names.

The fifth is that time zones are transparently handled, including when the server and the client are in different zones. IMAP stores dates in a server-local form, and a client in another timezone has to interpret them against the server's idea of local time, which is where naive implementations quietly produce messages that are off by hours.

The last two are conveniences and correctness: convenience methods are provided for commonly used functionality, and exceptions are raised when errors occur.

The example in the readme shows the shape of the result:

python
    from imapclient import IMAPClient

    # context manager ensures the session is cleaned up
    with IMAPClient(host="imap.host.org") as client:
        client.login('someone', 'secret')
        client.select_folder('INBOX')

        # search criteria are passed in a straightforward way
        # (nesting is supported)
        messages = client.search(['NOT', 'DELETED'])

        # fetch selectors are passed as a simple list of strings.
        response = client.fetch(messages, ['FLAGS', 'RFC822.SIZE'])

        # `response` is keyed by message id and contains parsed,
        # converted response items.
        for message_id, data in response.items():
            print('{id}: {size} bytes, flags={flags}'.format(
                id=message_id,
                size=data[b'RFC822.SIZE'],
                flags=data[b'FLAGS']))

The context manager guarantees the session is cleaned up, the search criteria are passed as nested lists rather than a string you have to quote correctly, and the fetch response comes back keyed by message id with parsed and converted values.

The example files show what people actually need

The examples directory at the root of the project is the most practical part of the repository for anyone deciding whether this handles their server, and the file names say more than a feature list would.

There is example.py, which is the basic usage pattern. There is email_parsing.py, which is about turning fetched messages into something your application can use. And there are two idle examples, idle_example.py and idle_selector_example.py, which is a hint about a feature that matters for push-style email.

IDLE is the IMAP extension where a connection stays open and the server notifies you when something changes instead of you polling. It is how a client gets near-real-time delivery, and it is notoriously fiddly to implement because it holds a connection open for long periods and needs careful handling of the server's untagged responses. Two example files devoted to it suggests it is a supported path rather than an afterthought.

Then there are three that are about connections rather than messages. oauth2_example.py is the one that matters most in practice, because password authentication against a major provider is usually impossible and token based authentication is the only option. tls_cacert.py and tls_no_checks.py are the two ends of the certificate verification story. One uses a custom certificate authority, and the other disables the checks, which is the setting you need against a server with a self-signed certificate and the setting you should never enable against anything else.

Having both of those as first class examples is a good sign about the project's attitude. A library for IMAP is also a library for TLS configuration, because the connection is where the security decisions are made, and shipping examples for both the correct and the insecure configuration is a way of making the choice visible.

The OAuth example is the one to read first if you are connecting to a hosted mail provider, because getting the token flow right is most of the work in that case.

Testing against real servers, and the interactive console

Two developer tools in the package are worth knowing about, and both solve real problems.

The first is the interactive console. This script connects an IMAPClient instance using the command line args given and starts an interactive session, and the readme says it is useful for exploring the API and testing things out, avoiding the steps required to set up an IMAPClient instance. The IPython shell is used if it is installed, otherwise the code.interact() function from the standard library is used. It is installed as the imapclient-interact script alongside the library, or it can be invoked as a module.

That is a real productivity tool. IMAPClient has a large method surface, and the fastest way to learn what a particular server returns for a particular command is to run it and look, rather than to write a script, run it, read a traceback and edit it again.

The second is the live test suite, and it is more ambitious. IMAPClient includes a series of live, functional tests which exercise it against a live IMAP account, and the readme describes them as useful for ensuring compatibility with a given IMAP server implementation. It is run with the imapclient-livetest script, taking a configuration file and optional unittest arguments. A sample configuration file, livetest-sample.ini, sits at the top level of the repository.

The warning attached to it is the important part. The operations used by livetest are destructive and could cause unintended loss of data. That said, as of version 0.9, livetest limits its activity to a folder it creates and subfolders of that folder. It should be safe to use with any IMAP account but please do not run livetest against a truly important IMAP account.

So the confinement to a self-created folder is a mitigation rather than a guarantee, and the readme asks for the output to be included with an issue if it fails against a particular server, and welcomes reports of successful runs, including the type and version of the server if known.

That combination, a destructive test suite that runs against real accounts, plus the request for server-specific reports, is the mechanism by which a client library claims broad compatibility. The readme lists where the library is heavily tested: Dovecot, Gmail, Fastmail.fm which uses a modified Cyrus implementation, Office365 and Yahoo. Access to accounts on other IMAP servers for testing would be greatly appreciated.

Packaging, versions and the maintenance surface

The project metadata is plain and tells you most of what you need.

The package name is IMAPClient and the version is 4.1.0, with the description matching the readme tagline. The author is Menno Finlay-Smits and the maintainers are a group address rather than a person, which is a deliberate choice for a project that has outlived its original author's day to day involvement. The keywords are imap, client, email and mail. Requires Python 3.8 or newer, and the readme's summary table puts the supported range at 3.8 to 3.14.

The classifier for Development Status is 5, Production/Stable, and the readme says you should feel confident using IMAPClient for production purposes. The build backend is setuptools with a requirement of 61.0 or newer, and the only package is imapclient. There are no runtime dependencies listed in the dependency groups, which is the dev group of sphinx, black, flake8, isort, mypy and pylint.

The tool configuration is worth noting. isort uses the black profile with multi_line_output 3 and order_by_type false, which is the standard way to keep the two formatters from fighting. The mypy configuration sets files to the whole project and excludes doc, examples and tests, and the comment in the file spells out what strict mode would mean, listing the flags individually. mypy is configured over the whole tree rather than over annotated functions only, so type errors in unannotated code are still caught.

On versioning, the readme makes a commitment: to clearly communicate version compatibility, IMAPClient will strictly adhere to the Semantic Versioning scheme from version 1.0 onwards. The recent releases show that being taken seriously, with 4.0.0 and 4.0.1 both landing on 2026-09-11 and 4.1.0 on 2026-09-18, and the last push on 2026-10-10.

That 4.0 release is recent enough that it is worth reading the release notes before you upgrade. The readme points at the release history in the documentation for exactly that.

The rest of the repository is conventional and well organised. There is a doc/ directory, an AUTHORS.rst, a COPYING file holding the New BSD licence, a NEWS.rst for the changelog, a .readthedocs.yaml for the documentation build, a .flake8 configuration, and a uv.lock, which tells you the project uses uv as its package manager and pins its own tooling with it.

Editorial conclusion

Adopt IMAPClient if you need to talk to a real IMAP server from Python, because the two problems it solves, parsing server responses and detecting errors, are exactly the ones that make hand-written imaplib code fragile. Do not adopt it if you are writing a one-off script that only fetches a single message, because the setup cost exceeds the benefit. Before you commit, run imapclient-livetest against a disposable account, since the live tests are destructive by design, and read the release notes for the 4.0 series, which is a recent major version.

Frequently asked questions

What does IMAPClient do that imaplib does not?

It fixes the two things the readme identifies as imaplib's weaknesses: imaplib is very low-level, expecting string values where lists would be appropriate and returning server responses almost unparsed, and it does not make good use of exceptions, so every call's return value has to be checked. IMAPClient uses imaplib internally but returns natural Python types, fully parsed responses and real exceptions.

How does IMAPClient handle UIDs and internationalised mailbox names?

IMAP unique message IDs are handled transparently, with no need to call different methods to use UIDs. Escaping for internationalised mailbox names is handled transparently too, so Unicode mailbox names may be passed as input wherever a folder name is accepted. Time zones are also handled transparently, including when the server and client are in different zones.

How do I install IMAPClient?

It is listed on PyPI and can be installed with uv add imapclient. More installation methods are described in the documentation, which lives at imapclient.readthedocs.io, and the package name on PyPI is IMAPClient.

What do the imapclient-interact and imapclient-livetest scripts do?

The interactive console connects an IMAPClient instance using the given command line args and starts an interactive session, using IPython if it is installed and code.interact() otherwise. The livetest script runs a series of live functional tests against a real IMAP account using a configuration file, and the readme warns that the operations are destructive and should not be run against a truly important account.

Which IMAP servers is IMAPClient tested against?

The readme says it is heavily tested against Dovecot, Gmail, Fastmail.fm which uses a modified Cyrus implementation, Office365 and Yahoo, and that access to accounts on other IMAP servers for testing would be greatly appreciated. The examples directory also includes oauth2_example.py, tls_cacert.py and tls_no_checks.py for the connection-level differences between providers.

Official sources

  1. Issues
  2. mjs/imapclient on GitHub
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/mjs-imapclient.svg)](https://hysenlabs.com/projects/mjs-imapclient)

Was this useful?