exchangelib: a Django-style client for Exchange Web Services
Python client for Microsoft Exchange Web Services (EWS)
At a glance
- What is it?
- exchangelib is a Python client library for Microsoft Exchange Web Services that presents a mailbox as an ORM, with autodiscover and support for searching, creating, updating, deleting, exporting and uploading mail, calendar, task, contact and distribution list items. It suits teams automating an Exchange or Office 365 mailbox, and it only works where EWS is still exposed.
- Who is it for?
- Adopt exchangelib if you need to read or write Exchange or Office 365 mailbox data from Python and the server still exposes Exchange Web Services, because the library gives you item objects, a queryset style API and autodiscover instead of raw SOAP envelopes. Do not adopt it for a tenant where only the newer Microsoft cloud API is available, since this client speaks EWS and nothing else.
- Can I use it commercially?
- Yes. BSD-2-Clause is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 122 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
An ORM for a mailbox, not a SOAP wrapper
exchangelib describes itself as an ORM for your Exchange mailbox, providing Django-style access to all your data. The Django comparison is the useful part of that description, because it tells you the shape of the API before you read a line of the documentation.
The library is a platform independent client for communicating with an on-premise Microsoft Exchange 2007-2016 server or Office 365 using Exchange Web Services. Among other things, it implements autodiscover, and it has functions for searching, creating, updating, deleting, exporting and uploading calendar, mailbox, task, contact and distribution list items.
So the model is that a mailbox is a set of typed collections, that a query returns objects, and that saving an object is a method call rather than an XML envelope. The Django analogy is doing real work here, because Exchange is one of those systems where the raw protocol is punishing and the interesting engineering is the mapping layer.
The project is BSD-2-Clause licensed, requires Python 3.10 or later, and is classified as Production/Stable in the package metadata. Documentation is published at ecederstrand.github.io/exchangelib, with the source-level API documentation alongside it. The most recent release listed is v5.6.0 from 2025-10-10, and the last push to the repository was on 2026-06-23.
Reading a mailbox in five lines
The teaser example is the fastest way to understand the API, because it shows the whole path from credentials to a list of items.
from exchangelib import Credentials, Account
credentials = Credentials("[email protected]", "topsecret")
account = Account("[email protected]", credentials=credentials, autodiscover=True)
for item in account.inbox.all().order_by("-datetime_received")[:100]:
print(item.subject, item.sender, item.datetime_received)Three things are happening in those lines. The Credentials object takes a primary address and a password, which is the simplest authentication mode the library supports. The Account object takes a primary address again, the credentials, and autodiscover set to true, which asks the library to find the correct endpoint for the account rather than having you supply a URL.
Then the query. account.inbox is a folder, .all() returns everything in it, and .order_by with a field name and a direction gives an ordered queryset, which is then sliced to the first hundred items. The field names are the model's fields, and the negative sign means descending.
That is a queryset API in the Django sense, and it is why the library is called an ORM. The work of turning that call into an EWS FindItem request, applying the server side paging and deserializing the XML into objects happens underneath, and the objects you get back expose their fields as attributes, which is what the print statement uses.
Autodiscover, and why the endpoint matters
Autodiscover is the feature that makes the library usable against a mixed estate, and the README names it explicitly as one of the things the library implements.
The problem it solves is that a single Exchange deployment can expose several endpoints, and the address a user logs into is not always the address the web interface is served from. Autodiscover asks the server where to send the requests for that particular account, which means the same code works for a user on an on-premise server and for a user on Office 365 without a configuration table.
The alternative is to configure the endpoint yourself, which the library also allows since autodiscover is a parameter rather than a hard-wired behaviour. Turning it off is a legitimate choice when you are talking to a server where autodiscover is disabled or slow, or when you already know the endpoint and want to avoid the extra round trip at startup.
The dependency list backs the mechanism up. dnspython at 2.2.0 or later is in the runtime dependencies, and that is the library you need to resolve the autodiscover records, since autodiscover for Exchange is a DNS and XML process rather than a simple HTTP call.
For a service that connects once at startup and then holds the connection, the autodiscover cost is paid once. For a script that connects repeatedly, it is worth caching the account object rather than rebuilding it.
The item types, and what the operations cover
The range of item types is what distinguishes this from a narrow mail library, and it is listed in the README as searching, creating, updating, deleting, exporting and uploading across several categories.
Mailbox items are the obvious case, covering messages and the folders they live in. Calendar items are the second, which means appointments and meetings with their recurrence, attendees and free busy information. Task items cover to do entries. Contact items cover people, and distribution lists cover the groups of recipients that a server exposes as their own object type.
Exporting and uploading deserve a note, because they are not the same as reading and writing items. Exporting means producing a file from the server side representation of an item, which for calendar and mail items is the Exchange item format, and uploading is the reverse, which is how you take a file and push it into a folder. Both are the operations you need when moving data between mailboxes or between systems, and both are the operations that are awkward with a simple message API.
The README also refers to the source code documentation, which is generated from the code, and that is where the full catalogue of item fields lives. The class per item type carries a large number of typed fields, and the field names are the ones you use in queries and in serialisation.
Authentication: the base dependencies and the optional extras
The authentication story is spread across the dependencies, and it is worth untangling because the extras are easy to miss.
The runtime dependencies include oauthlib, requests_oauthlib and requests_ntlm, and the optional dependency group lists kerberos with requests_gssapi and an msal entry. So the library supports the Microsoft OAuth flow, NTLM authentication, Kerberos through the GSSAPI library, and MSAL as an option.
The basic case is the one in the teaser, a Credentials object with an address and a password, which is what you would use against an on-premise server where the account authenticates with a domain password.
Against Office 365 the sensible path is OAuth, since the client libraries for it are in the base dependency set rather than hidden behind an extra. Kerberos and MSAL are the cases where you have to say so, which is the normal way to keep optional native dependencies out of an install that does not need them.
The rest of the dependency list explains the rest of the library. defusedxml at 0.6.0 or later and lxml above 3.0 are the XML parsers, and the defusedxml presence is a deliberate hardening choice for a client that parses documents from a server. isodate handles the date and time formats that Exchange uses, tzdata and tzlocal handle time zones, pygments is for coloured output, and requests with a minimum of 2.31.0 is the HTTP layer.
The alternative, and the condition that decides it
There is a real alternative for mailbox automation, and the decision comes down to a single property of the server rather than a preference between libraries.
The alternative is the newer Microsoft cloud API, the REST based one that Microsoft built after Exchange Web Services, and the difference in approach is fundamental. EWS is a SOAP interface with XML envelopes, which is why a library like this spends so much of its effort on serialisation, on autodiscover, and on turning server responses into typed objects. The newer API is a JSON and HTTP interface with its own identity model, and it does not speak SOAP at all.
The practical consequence is that this library works where EWS is available, and does not work where it is not. An on-premise Exchange 2007 to 2016 server exposes EWS, and the library targets exactly that range. A tenant that has been configured with only the newer API available is a different story, and a library that speaks SOAP cannot help there.
So the check to run before adopting is a single question about the environment: does the server or tenant you need to talk to expose Exchange Web Services, and for which account types? If yes, this library saves a great deal of work. If the answer is no, the choice was never between libraries.
The second reason to check is scope. If what you need is a contact list or a calendar feed, there are simpler protocols for that, and this library is a large instrument for a small job.
Maintenance, documentation and the release cadence
A few operational notes for anyone planning to depend on it.
The release history is steady rather than fast, with v5.6.0 on 2025-10-10, v5.5.1 on 2025-03-14 and v5.5.0 on 2024-10-28, and the last push to the repository was on 2026-06-23. The project is not archived. A library that tracks a server product across years is not going to release weekly, and the pace is consistent with the rate at which Exchange itself changes.
The documentation is generated rather than written, and the release procedure in the package metadata shows it: pdoc3 is run over the exchangelib package to produce the docs directory, and the generated output is committed. That means the API reference is always in step with the code, at the cost of the prose being thin, since the interesting explanations live in the docstrings and in the separate documentation site.
The repository also carries a CHANGELOG.md, a code of conduct, a pre-commit configuration, a flake8 configuration, a DeepSource configuration and a Codacy configuration, plus separate development and test requirement files. The test requirements are notable, because testing a client for a server product normally means a real server to talk to, and the presence of a dedicated test requirements file and a scripts directory suggests the suite is run against a live instance.
The version is dynamic and read from the package source, and the release process bumps it in the package init file, in the changelog, and in the documentation build, which is a small example of a release checklist written down in the place where it is executed.
Editorial conclusion
Adopt exchangelib if you need to read or write Exchange or Office 365 mailbox data from Python and the server still exposes Exchange Web Services, because the library gives you item objects, a queryset style API and autodiscover instead of raw SOAP envelopes. Do not adopt it for a tenant where only the newer Microsoft cloud API is available, since this client speaks EWS and nothing else. Before you build on it, check the authentication extras you need, because the default dependencies cover basic and NTLM style auth while Kerberos and MSAL are optional extras you have to add.
Frequently asked questions
How do I read the inbox with exchangelib?
Create Credentials with the primary address and password, build an Account with those credentials and autodiscover=True, then query account.inbox.all().order_by("-datetime_received")[:100] and read the item attributes such as subject, sender and datetime_received.
What kinds of items can exchangelib read and write?
The README lists searching, creating, updating, deleting, exporting and uploading for calendar, mailbox, task, contact and distribution list items, and the library implements autodiscover for locating the endpoint.
Which authentication methods does exchangelib support?
The runtime dependencies include oauthlib, requests_oauthlib and requests_ntlm, and the optional dependencies add kerberos through requests_gssapi and an msal entry. The simplest form is a Credentials object with an address and a password.
Which Python versions and servers does exchangelib support?
The package metadata requires Python 3.10 or later, and the README describes support for on-premise Microsoft Exchange 2007-2016 servers and for Office 365 through Exchange Web Services. The licence is BSD-2-Clause.
Official sources
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.
[](https://hysenlabs.com/projects/ecederstrand-exchangelib)Was this useful?