exchangelib and Exchange Online: options after EWS
exchangelib is a Python client for Exchange Web Services, and EWS is the only protocol it speaks. For mailboxes in Exchange Online, EWS ends on April 1, 2027, and the maintainer of exchangelib has no plans to add Microsoft Graph.
What stops
exchangelib describes itself as an interface to “an on-premise Microsoft Exchange 2007-2016 server or Office365 using Exchange Web Services (EWS)”. For Exchange Online, every call it makes is an EWS request, normally to https://outlook.office365.com/EWS/Exchange.asmx.
When Microsoft removes EWS from Exchange Online, those requests fail for every mailbox there. A user asked for Microsoft Graph support in the exchangelib repository. The maintainer answered that it would be “a huge undertaking”, better done “in a different package altogether”, and closed the request with “I have no plans to take on this effort myself”.
Scripts that work with Exchange Server are not affected. EWS stays in Exchange Server, and exchangelib keeps working there.
The dates
- October 10, 2026: a tenant in Microsoft’s worldwide cloud that has
EWSEnabledset to True needs anEWSAllowedAppIDslist. An application that is not on the list loses EWS. - Later, after a 7-day warning in Message Center: Microsoft turns EWS off in tenants that never set
EWSEnabled. - April 1, 2027: Microsoft removes EWS from Exchange Online for every application, listed or not.
The retirement guide has the full schedule.
What a blocked script sees
Exchange Online answers a blocked application with HTTP 403 and no EWS response. exchangelib has no handling of its own for that status. Unless the response carries one of the headers it checks first, the request ends in a MalformedResponseError:
exchangelib.errors.MalformedResponseError: Unknown failure in response. Code: 403 headers: {...} content:The same error appears when the token lacks a permission, so find the cause before you change any code. EWS returns 403 in Exchange Online lists the four settings that answer this way and the commands that show them.
Keep scripts running until the retirement
A script that signs in through OAuth uses an app registration, and Microsoft checks the application (client) ID of that registration against the list. Ask an administrator to keep the ID on it:
Set-OrganizationConfig -EwsEnabled $true -EwsAllowedAppIDs "<app ID 1>,<app ID 2>"The command stores the whole list, so it has to include every ID that should stay. The script then works until April 1, 2027 and no longer. Your EWS app list explains the list.
Rewrite the script for Microsoft Graph
This is the route Microsoft recommends. For Python there are two libraries:
- Microsoft Graph SDK for Python ↗ (
msgraph-sdkon PyPI) is Microsoft’s own client. - python-o365 ↗ (
O365on PyPI) is a community library built on Microsoft Graph.
The calls change, and so do some of the rules behind them: item IDs, paging, search and notifications behave differently. What changes when EWS code moves to Microsoft Graph goes through them. That guide is written for .NET code, and the differences it describes belong to the two APIs, so they apply to Python as well.
Check the features before you start. Microsoft’s retirement page has a roadmap of the EWS capabilities that are still coming to Microsoft Graph, and tells you not to plan on anything that is missing from it. It names three that will not be added: general access to public folders, general access to Microsoft 365 group mailboxes, and access to discovery mailboxes.
Scripts that only read or send mail
A script that only fetches messages or sends them can also move to IMAP, POP or SMTP. Exchange Online supports OAuth for all three, including the client credentials flow for scripts that run without a user. The application gets the IMAP.AccessAsApp, POP.AccessAsApp or SMTP.SendAsApp permission, and an administrator registers its service principal in Exchange and gives it access to the mailbox.
Use OAuth from the start. Exchange Online no longer accepts Basic authentication for IMAP and POP, and Microsoft plans to turn it off by default for SMTP AUTH at the end of December 2026. These protocols carry mail only, so calendars, contacts and tasks need Microsoft Graph.
Other EWS libraries
The retirement applies to every client that speaks EWS, in any language:
ews-javascript-apifor Node.js: its maintainer confirmed in February 2026 that the library is affected, and recommended moving the code to Microsoft Graph.- EWS Java API: Microsoft archived the repository in June 2024. Its readme says the API is in sustaining mode and names Microsoft Graph as the recommended way to reach Exchange Online.
php-ews: the readmes of the two common packages do not mention Microsoft Graph.- Hand-written SOAP requests go to the same endpoint and stop on the same day.
An EWS endpoint we can build for you
One more route would leave the scripts as they are: an EWS endpoint that you host, which accepts the requests of exchangelib and carries them out in Exchange Online through Microsoft Graph. exchangelib lets you give the address of the endpoint yourself, with Configuration(service_endpoint=...), so in principle a script can be pointed at such an endpoint through its configuration.
Sunsetless EWS is a .NET library that replaces the EWS Managed API inside a .NET application, so it cannot help a Python script. The same translation could run as a separate service that you host yourself, behind an HTTP endpoint that serves any EWS client. We have not tested exchangelib against such an endpoint yet.
Tell us about your code
If your code reaches Exchange Online through exchangelib or another EWS client outside .NET, write to us. We build the endpoint for the calls your code makes, and you try it with your own scripts before you rely on it.
The first organizations to get in touch pay a low, fixed price, because their code decides what we build first. We agree everything by email, and a few lines are enough to start:
- The library, and roughly how much code depends on it: one script or a whole application
- What the code does: mail only, or calendars, contacts, tasks and notifications as well
- How it signs in: as an application or as a user
- Whether you plan to rewrite it, and when you have to decide
What the script really calls
The EWS usage report in the Microsoft 365 admin center lists the EWS operations that each application ID calls, and how often. Export it for the ID of your script before EWS is switched off for it. The operations in that list are the scope of a rewrite.
Sources
- exchangelib documentation ↗
- exchangelib, issue 1359: “Microsoft announced EoL for EWS in Exchange Online” ↗
- exchangelib, issue 1365: impact of the EWS retirement ↗
- exchangelib source: how HTTP errors are raised (protocol.py) ↗
- exchangelib, discussion 1322: a 403 caused by token permissions ↗
- Microsoft, “EWS Deprecation Is Here”, October 1, 2026 ↗
- Microsoft, “Introducing EWSAllowedAppIDs”, updated September 22, 2026 ↗
- Microsoft Learn: retirement of EWS in Exchange Online ↗
- Microsoft Learn: EWS usage report ↗
- Microsoft Graph SDK for Python ↗
- python-o365 ↗
- Microsoft Learn: authenticate an IMAP, POP or SMTP connection using OAuth ↗
- Microsoft, SMTP AUTH Basic authentication timeline, January 27, 2026 ↗
- ews-javascript-api, issue 458: “MS EWS deprecation - is this library affected?” ↗
- EWS Java API repository (archived) ↗