Python client library for Microsoft 365 and Microsoft Graph APIs.
Covers SharePoint REST API v1, Microsoft Graph (Outlook, OneDrive, Teams, OneNote, Planner, and more), and supports all modern Azure AD authentication flows.
Python 3.8+ required.
- Installation
- Which client do I need?
- Authentication
- SharePoint — ClientContext
- Microsoft Graph — GraphClient
- Dependencies
pip install office365-rest-python-clientWith uv:
uv pip install office365-rest-python-clientLatest from GitHub (includes unreleased changes):
pip install git+https://github.com/vgrem/office365-rest-python-client.gitClientContext |
GraphClient |
|
|---|---|---|
| Target API | SharePoint REST API v1 | Microsoft Graph API |
| Use for | SharePoint lists, files, folders, search, site admin, permissions | Outlook, OneDrive, Teams, OneNote, Planner, Users, Groups |
| SharePoint via Graph? | — | Partial — use ClientContext for full SharePoint fidelity |
| Docs | SharePoint REST API | Microsoft Graph |
⚠️ ACS Retirement: Azure Access Control Service (ACS) app-only auth is fully retired as of April 2026.UserCredential(SAML/IDCRL) is retired as of May 1, 2026. Both have been removed from the library. Migrate to Azure AD auth. Learn more
No user interaction. Ideal for scripts, pipelines, and services.
from office365.sharepoint.client_context import ClientContext
ctx = ClientContext("{site_url}").with_client_certificate(
tenant="{tenant}", # e.g. contoso.onmicrosoft.com
client_id="{client_id}",
thumbprint="{thumbprint}",
cert_path="/path/to/cert.pem"
)ctx = ClientContext("{site_url}").with_client_secret(
tenant="{tenant}",
client_id="{client_id}",
client_secret="{client_secret}"
)Non-interactive user auth. Requires no MFA on the account.
ctx = ClientContext("{site_url}").with_username_and_password(
tenant="{tenant}",
client_id="{client_id}",
username="{username}",
password="{password}"
)Opens a browser. Works with MFA and Conditional Access.
ctx = ClientContext("{site_url}").with_interactive(
tenant="{tenant}",
client_id="{client_id}"
)Prerequisite: add
http://localhostas a Redirect URI in your Azure app registration.
Authenticate on another device via a displayed code. Useful for headless environments.
ctx = ClientContext("{site_url}").with_device_flow(
tenant="{tenant}",
client_id="{client_id}"
)For SharePoint Server (2016, 2019, Subscription Edition). Requires pip install office365-rest-python-client[ntlm].
from office365.sharepoint.client_context import ClientContext
ctx = ClientContext("http://sharepoint.company.com/sites/project", allow_ntlm=True).with_user_credentials(
username="DOMAIN\\username",
password="password"
)from office365.graph_client import GraphClient
client = GraphClient(tenant="{tenant}").with_client_secret(
client_id="{client_id}",
client_secret="{client_secret}"
)client = GraphClient(tenant="{tenant}").with_client_certificate(
client_id="{client_id}",
thumbprint="{thumbprint}",
private_key="{private_key}"
)client = GraphClient(tenant="{tenant}").with_token_interactive(
client_id="{client_id}"
)client = GraphClient(tenant="{tenant}").with_username_and_password(
client_id="{client_id}",
username="{username}",
password="{password}"
)Bring your own MSAL or any OAuth 2.0-compliant token provider:
import msal
def acquire_token():
app = msal.ConfidentialClientApplication(
client_id, authority=f"https://login.microsoftonline.com/{tenant}",
client_credential=client_secret
)
return app.acquire_token_for_client(["https://graph.microsoft.com/.default"])
client = GraphClient(acquire_token)For national and sovereign clouds, pass the environment parameter:
from office365.azure_env import AzureEnvironment
from office365.sharepoint.client_context import ClientContext
ctx = ClientContext("{site_url}", environment=AzureEnvironment.USGovernmentHigh)\
.with_client_certificate(...)| Environment | Constant |
|---|---|
| Global (default) | AzureEnvironment.Global |
| US Government GCC | AzureEnvironment.USGovernment |
| US Government GCC High | AzureEnvironment.USGovernmentHigh |
| US Government DoD | AzureEnvironment.USGovernmentDoD |
| China (21Vianet) | AzureEnvironment.China |
| Germany (legacy) | AzureEnvironment.Germany |
from office365.sharepoint.client_context import ClientContext
ctx = ClientContext("{site_url}").with_client_certificate(
tenant="{tenant}", client_id="{client_id}",
thumbprint="{thumbprint}", cert_path="./cert.pem"
)
web = ctx.web.get().execute_query()
print(f"Site title: {web.title}")# Get all items (handles 5000+ row threshold automatically)
items = ctx.web.lists.get_by_title("Orders").items.get_all().execute_query()
for item in items:
print(item.properties["Title"])
# Create item
target_list = ctx.web.lists.get_by_title("Tasks")
item = target_list.add_item({"Title": "New task", "Status": "Active"}).execute_query()
# Bulk create — auto-batches in chunks of 100
for row in data:
target_list.add_item({"Title": row["name"]})
ctx.execute_batch()
# Filter, select, expand
items = ctx.web.lists.get_by_title("Projects")\
.items\
.filter("Status eq 'Active'")\
.select(["Title", "Author/Title"])\
.expand(["Author"])\
.get_all().execute_query()# Upload file
with open("report.pdf", "rb") as f:
folder = ctx.web.get_folder_by_server_relative_url("/sites/mysite/Shared Documents")
file = folder.upload_file("report.pdf", f).execute_query()
# Download file
with open("report.pdf", "wb") as f:
ctx.web.get_file_by_server_relative_path("/sites/mysite/Shared Documents/report.pdf")\
.download(f).execute_query()
# Large file upload (chunked)
folder.files.create_upload_session(
local_path, chunk_size=10*1024*1024,
chunk_uploaded=lambda offset: print(f"{offset} bytes uploaded")
).execute_query()
# Download folder as zip
with open("archive.zip", "wb") as f:
folder.download_folder(f).execute_query()
# Create nested folders
base = ctx.web.get_folder_by_server_relative_url("/sites/mysite/Shared Documents")
sub = base.add("Projects").execute_query()
sub.add("2025").execute_query()All file examples | All folder examples
from office365.sharepoint.search.request import SearchRequest
from office365.sharepoint.search.service import SearchService
search = SearchService(ctx)
request = SearchRequest("IsDocument:1", RowLimit=50, StartRow=0)
result = search.post_query(request).execute_query()
rows = result.value.PrimaryQueryResult.RelevantResults.Table.Rows
for row in rows:
print(row)from office365.sharepoint.roles.type import RoleType
# Break inheritance and grant access to a user
role_def = ctx.web.role_definitions.get_by_type(RoleType.Reader)
user = ctx.web.ensure_user("i:0#.f|membership|user@company.com")
item = ctx.web.lists.get_by_title("Confidential").items.get_by_id(1)
item.break_role_inheritance(copy_role_assignments=False)
item.add_role_assignment(user, role_def)
ctx.execute_query()from office365.sharepoint.tenant.administration.tenant import Tenant
tenant = Tenant(ctx)
# List all sites
sites = tenant.get_site_properties_from_sharepoint_by_filters().execute_query()
for site in sites:
print(site.url)
# Create a site
tenant.create_site({"Url": "https://tenant.sharepoint.com/sites/newsite", "Title": "New Site"}).execute_query()from office365.graph_client import GraphClient
client = GraphClient(tenant="{tenant}").with_client_secret(
client_id="{client_id}", client_secret="{client_secret}"
)
me = client.me.get().execute_query()
print(f"Signed in as: {me.user_principal_name}")# Send email
client.me.send_mail(
subject="Hello",
body="Message body",
to_recipients=["user@company.com"]
).execute_query()
# List messages
messages = client.me.messages.top(10).get().execute_query()
for msg in messages:
print(msg.subject)
# Create calendar event
from office365.outlook.calendar.events.event import Event
event = client.me.calendar.events.add(
subject="Team standup",
start="2025-06-01T09:00:00",
end="2025-06-01T09:30:00"
).execute_query()# List drives
drives = client.drives.get().execute_query()
# Upload file to OneDrive
with open("report.xlsx", "rb") as f:
client.me.drive.root.upload("report.xlsx", f).execute_query()
# Download file
with open("report.xlsx", "wb") as local_file:
client.me.drive.root.get_by_path("Documents/report.xlsx")\
.download(local_file).execute_query()# List all teams
teams = client.groups.get().execute_query()
# Send channel message
client.teams["{team_id}"].channels["{channel_id}"]\
.messages.add(body="Hello team!").execute_query()
# Create a team
new_team = client.groups["{group_id}"].add_team().execute_query()# Create a page
with open("MyPage.html", "rb") as f:
page = client.me.onenote.pages.add(presentation_file=f).execute_query()# Create a task
task = client.planner.tasks.add(
title="Review PR",
planId="{plan_id}"
).execute_query()# List users
users = client.users.top(100).get().execute_query()
# Create group
group = client.groups.add(
display_name="Engineering",
mail_nickname="engineering",
mail_enabled=False,
security_enabled=True
).execute_query()Installed automatically:
| Package | Purpose |
|---|---|
| requests | HTTP transport |
| msal | Azure AD authentication |
Optional:
pip install office365-rest-python-client[pandas] # to_dataframe() on collectionsPRs welcome. See issues for things to work on.