vgrem/office365-rest-python-client

Microsoft 365 & Microsoft Graph Library for Python

Python

1,726

2,469 commits

updated Oct 13, 2026

See the code

README

office365-rest-python-client

A Python client library for Microsoft 365, the SharePoint REST API and Microsoft Graph.

Downloads PyPI PyPI pyversions License: MIT Sponsor

Python 3.8+ · typed models · fluent queries · deferred execution · 700+ runnable examples

  • Two clients, one library — ClientContext speaks the SharePoint REST API; GraphClient speaks Microsoft Graph.
  • Fluent and deferred — chain .select(), .filter(), .expand(), .top(), .order_by() and only hit the wire when you call execute_query().
  • Typed models — every entity is a real Python class (List, File, Team, Message, …), not a dictionary.
  • Copy-paste examples — a runnable script for every service and scenario under examples/.

New here? Pick a client in Choose your client, then jump straight to a runnable snippet in Examples by product.

Table of contents

Choose your client

The library ships two clients. Pick the one that matches the API you need.

ClientContextGraphClient
Talks toSharePoint REST API v1Microsoft Graph (graph.microsoft.com)
Entry pointClientContext("https://contoso.sharepoint.com/sites/team")GraphClient(tenant="contoso.onmicrosoft.com")
Best forSharePoint lists, items, files, folders, search, permissions, site and tenant administration, taxonomy, webhooksOutlook, OneDrive, Teams, OneNote, Planner, To Do, Entra ID, Intune, Purview, Bookings, reports, security…
SharePointFull fidelityPartial — prefer ClientContext for SharePoint-only work
Typed modelsList, ListItem, File, Folder, Web, Site, User …DriveItem, Team, Message, Event, User, Group, PlannerTask …
Authcertificate, interactive, device flow, cookies, NTLM (on-prem)client secret, certificate, interactive, device flow, ROPC
Examplesexamples/sharepoint/examples/ by product
API referenceSharePoint RESTMicrosoft Graph

Rule of thumb

  • SharePoint lists, files, site or tenant administration → ClientContext
  • Teams, Outlook, OneDrive, Entra ID, Intune, Purview, reports → GraphClient
  • OneDrive files work with either client; GraphClient is the modern, cross-service path.

Installation

pip install office365-rest-python-client

With uv:

uv add office365-rest-python-client

Directly from source:

pip install git+https://github.com/vgrem/office365-rest-python-client.git

Authentication

Modern flows authenticate through Microsoft Entra ID using MSAL. Both clients support delegated (user) and app-only (application) access.

[!IMPORTANT] SharePoint ACS app-only access was retired in April 2026 and SAML-based authentication in May 2026. Use the certificate, interactive or device-code flows below instead — see examples/sharepoint/auth/modern/ and the migration script migrate_from_saml_to_cert.py.

ClientContext (SharePoint)

FlowAccessMethod
CertificateApp-onlywith_client_certificate(tenant, client_id, thumbprint, cert_path=...)
InteractiveDelegated (MFA)with_interactive(tenant, client_id)
Device codeDelegated (MFA)with_device_flow(tenant, client_id)
Username / passwordDelegatedwith_username_and_password(tenant, client_id, username, password)
Custom tokenEitherwith_access_token(token_func)
Browser cookiesDelegatedwith_cookies(cookie_source)
NTLMOn-premisesClientContext(url, allow_ntlm=True).with_user_credentials(username, password)
ACS / SAMLOn-premises (legacy)legacy app-only · legacy SAML

[!NOTE] SharePoint app-only access requires a certificate. A client secret is not accepted by SharePoint REST API v1 — use GraphClient for secret-based app-only. See why a client secret doesn't work for SharePoint.

from office365.sharepoint.client_context import ClientContext

ctx = ClientContext("https://contoso.sharepoint.com/sites/team").with_client_certificate(
    "contoso.onmicrosoft.com",
    client_id="00000000-0000-0000-0000-000000000000",
    thumbprint="AA11BB22CC33DD44EE55FF66AA77BB88CC99DD00",
    cert_path="./private.pem",
)

web = ctx.web.get().execute_query()
print(web.title)

Ready-made scripts: certificate · certificate (private key) · custom scopes · interactive · device flow · username/password · access token · cookies · NTLM

GraphClient (Microsoft Graph)

FlowAccessMethod
Client secretApp-onlywith_client_secret(client_id, client_secret)
CertificateApp-onlywith_certificate(client_id, thumbprint, private_key)
InteractiveDelegated (MFA)with_token_interactive(client_id)
Device codeDelegated (MFA)with_device_flow(client_id)
Username / passwordDelegatedwith_username_and_password(client_id, username, password)
Custom tokenEitherGraphClient(token_callback, tenant=...)
from office365.graph_client import GraphClient

client = GraphClient(tenant="contoso.onmicrosoft.com").with_client_secret(
    client_id="00000000-0000-0000-0000-000000000000",
    client_secret="your-client-secret",
)

users = client.users.get().execute_query()
print(len(users), "users")

Ready-made scripts: client secret · certificate · interactive · device flow · username/password · custom token callback

Quick start

SharePoint list — read and write items

from office365.sharepoint.client_context import ClientContext

ctx = ClientContext("https://contoso.sharepoint.com/sites/team").with_client_certificate(
    "contoso.onmicrosoft.com", client_id, thumbprint, cert_path="./private.pem"
)

tasks = ctx.web.lists.get_by_title("Tasks")
item = tasks.add_item({"Title": "Write the release notes"}).execute_query()
print("Created item:", item.id)

for task in tasks.items.get_all().execute_query():
    print(task.properties["Title"])

Microsoft Graph — send an email

from office365.graph_client import GraphClient

client = GraphClient(tenant="contoso.onmicrosoft.com").with_client_secret(client_id, client_secret)

client.me.send_mail(
    subject="Hello from Graph API",
    body="This email was sent using the Microsoft Graph API.",
    to_recipients=["alex@contoso.onmicrosoft.com"],
).execute_query()

Examples by product

Every snippet below is lifted from a runnable script. Follow the More → link for the full example and its required permissions.

SharePoint (ClientContext)

# Lists and items
tasks = ctx.web.lists.get_by_title("Tasks")
item = tasks.add_item({"Title": "Ship it", "Status": "Active"}).execute_query()
tasks.items.get_all().execute_query()  # or .filter("Status eq 'Active'").get_all()

# Files and folders
folder = ctx.web.get_folder_by_server_relative_url("/sites/team/Shared Documents")
with open("report.pdf", "rb") as f:
    file = folder.files.upload(f).execute_query()

with open("report.pdf", "wb") as f:
    ctx.web.get_file_by_server_relative_path("/sites/team/Shared Documents/report.pdf").download(f).execute_query()

# Large file (chunked upload session)
with open("video.mp4", "rb") as f:
    folder.files.create_upload_session(f, 10 * 1024 * 1024).execute_query()
AreaExamples
Lists and itemslists · listitems · views
Files and foldersfiles · folders
Sites and webssites · webs · hubsites
Searchsearch
Permissions and sharingpermissions · sharing
Fields and content typesfields · contenttypes
Pages and navigationpages · navigation
Taxonomy and profilestaxonomy · userprofile
Tenant administrationtenant · groups · users
Webhooks, migration, advancedwebhooks · migration · advanced

OneDrive

uploaded = client.me.drive.root.upload_file("report.xlsx").execute_query()

with open("report.xlsx", "wb") as f:
    client.me.drive.root.get_by_path("report.xlsx").download(f).execute_query()

More →

Teams

team = client.teams.create_and_wait("Contoso Project", "All project collaboration").execute_query()

channel = team.channels.add("Project Chat").execute_query()
channel.messages.add("Hello team!").execute_query()

More →

Outlook (mail and calendar)

from datetime import datetime, timedelta, timezone

# Send mail
client.me.send_mail(
    subject="Hello from Graph API",
    body="This email was sent using the Microsoft Graph API.",
    to_recipients=["alex@contoso.onmicrosoft.com"],
).execute_query()

# Create a calendar event
when = datetime.now(timezone.utc) + timedelta(days=1)
client.me.calendar.events.add(
    subject="Team Lunch",
    body="Let's grab lunch together.",
    start=when,
    end=when + timedelta(hours=1),
    attendees=["alex@contoso.onmicrosoft.com"],
).execute_query()

More →

Entra ID

from office365.directory.users.password_profile import PasswordProfile
from office365.directory.users.profile import UserProfile

profile = UserProfile(
    displayName="Alex Wilber",
    userPrincipalName="alex@contoso.onmicrosoft.com",
    mailNickname="alex",
    accountEnabled=True,
    passwordProfile=PasswordProfile(password="P@ssw0rd!", forceChangePasswordNextSignIn=True),
)
user = client.users.add(profile).execute_query()
print(user.display_name)

More →

Planner

group = client.groups.find_by_name("My Sample Team", required=True).execute_query()
plans = group.planner.plans.get().execute_query()
task = client.planner.tasks.add("Update client list", plans[0].id).execute_query()
print(task.title)

More →

To Do

for task_list in client.me.todo.lists.get().execute_query():
    print(task_list.display_name, len(task_list.tasks.get().execute_query()), "tasks")

More →

OneNote

with open("Sample.html", "rb") as page_html:
    page = client.me.onenote.pages.add(presentation_file=page_html).execute_query()
print(page.links.oneNoteWebUrl)

More →

More services — Admin, Reports, Defender, Intune, Purview, Bookings, Communications, Security, Insights, Backup Storage

Microsoft 365 admin

announcement = client.admin.service_announcement
health = announcement.health_overviews.get().execute_query()
issues = announcement.issues.get().execute_query()
messages = announcement.messages.get().execute_query()

More →

Usage reports

report = client.reports.get_email_activity_counts("D30").execute_query()
print(report.value)  # CSV payload

More →

Microsoft Defender

alerts = client.security.alerts_v2.top(20).get().execute_query()
for alert in alerts:
    print(alert)

More →

Microsoft Intune

devices = client.device_management.managed_devices.get().execute_query()
for device in devices:
    print(device.device_name, device.operating_system, device.compliance_state)

More →

Microsoft Purview

labels = client.security.data_security_and_governance.sensitivity_labels.get().execute_query()
for label in labels:
    print(label.display_name, label.id)

More →

Microsoft Bookings

businesses = client.solutions.booking_businesses.get().execute_query()
for business in businesses:
    print(business.display_name)

More →

Cloud communications

presence = client.users["alex@contoso.onmicrosoft.com"].presence.get().execute_query()
print(presence.availability, presence.activity)

More →

Security (threat intelligence)

host = client.security.threat_intelligence.hosts["contoso.com"].get().execute_query()
reputation = host.reputation.get().execute_query()
print(reputation.properties.get("score"))

More →

Insights

for item in client.me.insights.trending.get().execute_query():
    print(item.resource_reference)

More →

Backup Storage

backup = client.solutions.backup_restore
backup.get().execute_query()
print(backup.service_status.status)

More →

Common patterns

Deferred execution. Requests are queued and sent only when you call execute_query(). Chain the fluent methods first, then execute once.

items = (
    ctx.web.lists.get_by_title("Tasks")
    .items.select(["Title", "Status"])
    .filter("Status eq 'Active'")
    .order_by("Title")
    .top(100)
    .get()
    .execute_query()
)

Addressing resources. Entities are addressed by the key you already know, so you rarely need an opaque id: user principal name, site path or URL, file path, list title. get_by_url/get_by_path on client.sites fetch when you call execute_query(); keyed/UPN forms and driveItem.get_by_path are lazy, so chain .get() first.

user = client.users["ada@contoso.com"].get().execute_query()  # a user, by UPN
site = client.sites.get_by_url("https://contoso.sharepoint.com/sites/marketing").execute_query()
file = client.me.drive.root.get_by_path("Reports/Q1.xlsx").get().execute_query()
items = ctx.web.lists.get_by_title("Tasks").items.get().execute_query()

Reading large collections. Use get_all() to follow paging automatically, and page_size to stay under server limits.

items = ctx.web.lists.get_by_title("Orders").items.get_all(page_size=2000).execute_query()

files = (
    ctx.web.get_folder_by_server_relative_url("/sites/team/Shared Documents")
    .get_files(recursive=True, page_size=2000)
    .execute_query()
)

Batching. Dispatch many queued operations in one round trip. Raise concurrency to run batches in parallel; throttled sub-requests are retried individually, honoring Retry-After.

for row in rows:
    ctx.web.lists.get_by_title("Contacts").add_item(row)

ctx.execute_batch()  # sequential
ctx.execute_batch(concurrency=5)  # up to 5 batches in flight

# Graph
for user in new_users:
    client.users.add(user)
client.execute_batch(concurrency=5)

Large lists (> 5,000 items). SharePoint refuses filtered/sorted queries on non-indexed columns. The library can tell you what to index and pre-flight the query.

from office365.sharepoint.listitems.caml import Caml, CamlQuery

query = CamlQuery.builder().where(Caml.text("Status").eq("Active")).order_by("ID").row_limit(2000, paged=True).build()

lst.check_query(query)  # raises with a clear message if an index is missing
print(lst.index_candidates(query))  # columns worth indexing
lst.ensure_indexed("Status").execute_query()  # create the index (builds in the background)

Import and export DataFrames. Optional helpers turn list items into pandas / Polars frames and back.

lst = ctx.web.lists.ensure_list("Orders").execute_query()

df = lst.to_dataframe().execute_query().value
lst.from_dataframe(df, chunksize=100).execute_query()

Async / await

Every call that reaches the network has an await twin. Builders stay synchronous — only the terminal call changes. No extra dependency is required: by default the blocking HTTP call is handed to a worker thread, so the event loop stays free.

import asyncio


async def main() -> None:
    web = await ctx.web.get().execute_query_async()
    print(web.title)


asyncio.run(main())

execute_query_async(), execute_batch_async(), get_all_async(), execute_query_parallel_async(), streaming downloads/exports and long-running-operation polling all have async twins, and collections support async for. See the async guide.

Dependencies

Install the core package on its own, or add the extras you need:

ExtraAddsUse it for
azureazure-storage-blob, cryptographyAzure Blob Storage and certificate helpers
excelopenpyxlReading and writing Excel workbooks
examplesfakerRunning the sample scripts
ntlmrequests-ntlmOn-premises SharePoint with NTLM
pandaspandasto_dataframe() / from_dataframe()
parquetpyarrowParquet import and export
duckdbduckdbQuerying exported data locally
sqlsqlalchemySQL-backed import and export
notebooksjupyter, nbformat, jinja2Running the notebook samples
pip install "office365-rest-python-client[pandas,excel]"

Contributing

Issues and pull requests are welcome — please use the issue tracker for bugs and ideas.

git clone https://github.com/vgrem/office365-rest-python-client.git
cd office365-rest-python-client
uv sync --all-extras
pytest --offline -q

See CONTRIBUTING.md for the full development setup. Run the gate before submitting: ruff check ., ruff format --check ., pyright and pytest --offline -q.

Support

If this project saves you time, please consider sponsoring its development. ⭐

License

Released under the MIT License.

entra-id
hacktoberfest
intune
microsoft365
microsoft-graph
microsoft-purview
microsoft-teams
office365
onedrive
onenote
outlook
planner
python
sdk-python
sharepoint
sharepoint-rest-api

vgrem/office365-rest-python-client

Microsoft 365 & Microsoft Graph Library for Python

Python

1,726

2,469 commits

updated Oct 13, 2026

See the code

README

office365-rest-python-client

A Python client library for Microsoft 365, the SharePoint REST API and Microsoft Graph.

Downloads PyPI PyPI pyversions License: MIT Sponsor

Python 3.8+ · typed models · fluent queries · deferred execution · 700+ runnable examples

  • Two clients, one library — ClientContext speaks the SharePoint REST API; GraphClient speaks Microsoft Graph.
  • Fluent and deferred — chain .select(), .filter(), .expand(), .top(), .order_by() and only hit the wire when you call execute_query().
  • Typed models — every entity is a real Python class (List, File, Team, Message, …), not a dictionary.
  • Copy-paste examples — a runnable script for every service and scenario under examples/.

New here? Pick a client in Choose your client, then jump straight to a runnable snippet in Examples by product.

Table of contents

Choose your client

The library ships two clients. Pick the one that matches the API you need.

ClientContextGraphClient
Talks toSharePoint REST API v1Microsoft Graph (graph.microsoft.com)
Entry pointClientContext("https://contoso.sharepoint.com/sites/team")GraphClient(tenant="contoso.onmicrosoft.com")
Best forSharePoint lists, items, files, folders, search, permissions, site and tenant administration, taxonomy, webhooksOutlook, OneDrive, Teams, OneNote, Planner, To Do, Entra ID, Intune, Purview, Bookings, reports, security…
SharePointFull fidelityPartial — prefer ClientContext for SharePoint-only work
Typed modelsList, ListItem, File, Folder, Web, Site, User …DriveItem, Team, Message, Event, User, Group, PlannerTask …
Authcertificate, interactive, device flow, cookies, NTLM (on-prem)client secret, certificate, interactive, device flow, ROPC
Examplesexamples/sharepoint/examples/ by product
API referenceSharePoint RESTMicrosoft Graph

Rule of thumb

  • SharePoint lists, files, site or tenant administration → ClientContext
  • Teams, Outlook, OneDrive, Entra ID, Intune, Purview, reports → GraphClient
  • OneDrive files work with either client; GraphClient is the modern, cross-service path.

Installation

pip install office365-rest-python-client

With uv:

uv add office365-rest-python-client

Directly from source:

pip install git+https://github.com/vgrem/office365-rest-python-client.git

Authentication

Modern flows authenticate through Microsoft Entra ID using MSAL. Both clients support delegated (user) and app-only (application) access.

[!IMPORTANT] SharePoint ACS app-only access was retired in April 2026 and SAML-based authentication in May 2026. Use the certificate, interactive or device-code flows below instead — see examples/sharepoint/auth/modern/ and the migration script migrate_from_saml_to_cert.py.

ClientContext (SharePoint)

FlowAccessMethod
CertificateApp-onlywith_client_certificate(tenant, client_id, thumbprint, cert_path=...)
InteractiveDelegated (MFA)with_interactive(tenant, client_id)
Device codeDelegated (MFA)with_device_flow(tenant, client_id)
Username / passwordDelegatedwith_username_and_password(tenant, client_id, username, password)
Custom tokenEitherwith_access_token(token_func)
Browser cookiesDelegatedwith_cookies(cookie_source)
NTLMOn-premisesClientContext(url, allow_ntlm=True).with_user_credentials(username, password)
ACS / SAMLOn-premises (legacy)legacy app-only · legacy SAML

[!NOTE] SharePoint app-only access requires a certificate. A client secret is not accepted by SharePoint REST API v1 — use GraphClient for secret-based app-only. See why a client secret doesn't work for SharePoint.

from office365.sharepoint.client_context import ClientContext

ctx = ClientContext("https://contoso.sharepoint.com/sites/team").with_client_certificate(
    "contoso.onmicrosoft.com",
    client_id="00000000-0000-0000-0000-000000000000",
    thumbprint="AA11BB22CC33DD44EE55FF66AA77BB88CC99DD00",
    cert_path="./private.pem",
)

web = ctx.web.get().execute_query()
print(web.title)

Ready-made scripts: certificate · certificate (private key) · custom scopes · interactive · device flow · username/password · access token · cookies · NTLM

GraphClient (Microsoft Graph)

FlowAccessMethod
Client secretApp-onlywith_client_secret(client_id, client_secret)
CertificateApp-onlywith_certificate(client_id, thumbprint, private_key)
InteractiveDelegated (MFA)with_token_interactive(client_id)
Device codeDelegated (MFA)with_device_flow(client_id)
Username / passwordDelegatedwith_username_and_password(client_id, username, password)
Custom tokenEitherGraphClient(token_callback, tenant=...)
from office365.graph_client import GraphClient

client = GraphClient(tenant="contoso.onmicrosoft.com").with_client_secret(
    client_id="00000000-0000-0000-0000-000000000000",
    client_secret="your-client-secret",
)

users = client.users.get().execute_query()
print(len(users), "users")

Ready-made scripts: client secret · certificate · interactive · device flow · username/password · custom token callback

Quick start

SharePoint list — read and write items

from office365.sharepoint.client_context import ClientContext

ctx = ClientContext("https://contoso.sharepoint.com/sites/team").with_client_certificate(
    "contoso.onmicrosoft.com", client_id, thumbprint, cert_path="./private.pem"
)

tasks = ctx.web.lists.get_by_title("Tasks")
item = tasks.add_item({"Title": "Write the release notes"}).execute_query()
print("Created item:", item.id)

for task in tasks.items.get_all().execute_query():
    print(task.properties["Title"])

Microsoft Graph — send an email

from office365.graph_client import GraphClient

client = GraphClient(tenant="contoso.onmicrosoft.com").with_client_secret(client_id, client_secret)

client.me.send_mail(
    subject="Hello from Graph API",
    body="This email was sent using the Microsoft Graph API.",
    to_recipients=["alex@contoso.onmicrosoft.com"],
).execute_query()

Examples by product

Every snippet below is lifted from a runnable script. Follow the More → link for the full example and its required permissions.

SharePoint (ClientContext)

# Lists and items
tasks = ctx.web.lists.get_by_title("Tasks")
item = tasks.add_item({"Title": "Ship it", "Status": "Active"}).execute_query()
tasks.items.get_all().execute_query()  # or .filter("Status eq 'Active'").get_all()

# Files and folders
folder = ctx.web.get_folder_by_server_relative_url("/sites/team/Shared Documents")
with open("report.pdf", "rb") as f:
    file = folder.files.upload(f).execute_query()

with open("report.pdf", "wb") as f:
    ctx.web.get_file_by_server_relative_path("/sites/team/Shared Documents/report.pdf").download(f).execute_query()

# Large file (chunked upload session)
with open("video.mp4", "rb") as f:
    folder.files.create_upload_session(f, 10 * 1024 * 1024).execute_query()
AreaExamples
Lists and itemslists · listitems · views
Files and foldersfiles · folders
Sites and webssites · webs · hubsites
Searchsearch
Permissions and sharingpermissions · sharing
Fields and content typesfields · contenttypes
Pages and navigationpages · navigation
Taxonomy and profilestaxonomy · userprofile
Tenant administrationtenant · groups · users
Webhooks, migration, advancedwebhooks · migration · advanced

OneDrive

uploaded = client.me.drive.root.upload_file("report.xlsx").execute_query()

with open("report.xlsx", "wb") as f:
    client.me.drive.root.get_by_path("report.xlsx").download(f).execute_query()

More →

Teams

team = client.teams.create_and_wait("Contoso Project", "All project collaboration").execute_query()

channel = team.channels.add("Project Chat").execute_query()
channel.messages.add("Hello team!").execute_query()

More →

Outlook (mail and calendar)

from datetime import datetime, timedelta, timezone

# Send mail
client.me.send_mail(
    subject="Hello from Graph API",
    body="This email was sent using the Microsoft Graph API.",
    to_recipients=["alex@contoso.onmicrosoft.com"],
).execute_query()

# Create a calendar event
when = datetime.now(timezone.utc) + timedelta(days=1)
client.me.calendar.events.add(
    subject="Team Lunch",
    body="Let's grab lunch together.",
    start=when,
    end=when + timedelta(hours=1),
    attendees=["alex@contoso.onmicrosoft.com"],
).execute_query()

More →

Entra ID

from office365.directory.users.password_profile import PasswordProfile
from office365.directory.users.profile import UserProfile

profile = UserProfile(
    displayName="Alex Wilber",
    userPrincipalName="alex@contoso.onmicrosoft.com",
    mailNickname="alex",
    accountEnabled=True,
    passwordProfile=PasswordProfile(password="P@ssw0rd!", forceChangePasswordNextSignIn=True),
)
user = client.users.add(profile).execute_query()
print(user.display_name)

More →

Planner

group = client.groups.find_by_name("My Sample Team", required=True).execute_query()
plans = group.planner.plans.get().execute_query()
task = client.planner.tasks.add("Update client list", plans[0].id).execute_query()
print(task.title)

More →

To Do

for task_list in client.me.todo.lists.get().execute_query():
    print(task_list.display_name, len(task_list.tasks.get().execute_query()), "tasks")

More →

OneNote

with open("Sample.html", "rb") as page_html:
    page = client.me.onenote.pages.add(presentation_file=page_html).execute_query()
print(page.links.oneNoteWebUrl)

More →

More services — Admin, Reports, Defender, Intune, Purview, Bookings, Communications, Security, Insights, Backup Storage

Microsoft 365 admin

announcement = client.admin.service_announcement
health = announcement.health_overviews.get().execute_query()
issues = announcement.issues.get().execute_query()
messages = announcement.messages.get().execute_query()

More →

Usage reports

report = client.reports.get_email_activity_counts("D30").execute_query()
print(report.value)  # CSV payload

More →

Microsoft Defender

alerts = client.security.alerts_v2.top(20).get().execute_query()
for alert in alerts:
    print(alert)

More →

Microsoft Intune

devices = client.device_management.managed_devices.get().execute_query()
for device in devices:
    print(device.device_name, device.operating_system, device.compliance_state)

More →

Microsoft Purview

labels = client.security.data_security_and_governance.sensitivity_labels.get().execute_query()
for label in labels:
    print(label.display_name, label.id)

More →

Microsoft Bookings

businesses = client.solutions.booking_businesses.get().execute_query()
for business in businesses:
    print(business.display_name)

More →

Cloud communications

presence = client.users["alex@contoso.onmicrosoft.com"].presence.get().execute_query()
print(presence.availability, presence.activity)

More →

Security (threat intelligence)

host = client.security.threat_intelligence.hosts["contoso.com"].get().execute_query()
reputation = host.reputation.get().execute_query()
print(reputation.properties.get("score"))

More →

Insights

for item in client.me.insights.trending.get().execute_query():
    print(item.resource_reference)

More →

Backup Storage

backup = client.solutions.backup_restore
backup.get().execute_query()
print(backup.service_status.status)

More →

Common patterns

Deferred execution. Requests are queued and sent only when you call execute_query(). Chain the fluent methods first, then execute once.

items = (
    ctx.web.lists.get_by_title("Tasks")
    .items.select(["Title", "Status"])
    .filter("Status eq 'Active'")
    .order_by("Title")
    .top(100)
    .get()
    .execute_query()
)

Addressing resources. Entities are addressed by the key you already know, so you rarely need an opaque id: user principal name, site path or URL, file path, list title. get_by_url/get_by_path on client.sites fetch when you call execute_query(); keyed/UPN forms and driveItem.get_by_path are lazy, so chain .get() first.

user = client.users["ada@contoso.com"].get().execute_query()  # a user, by UPN
site = client.sites.get_by_url("https://contoso.sharepoint.com/sites/marketing").execute_query()
file = client.me.drive.root.get_by_path("Reports/Q1.xlsx").get().execute_query()
items = ctx.web.lists.get_by_title("Tasks").items.get().execute_query()

Reading large collections. Use get_all() to follow paging automatically, and page_size to stay under server limits.

items = ctx.web.lists.get_by_title("Orders").items.get_all(page_size=2000).execute_query()

files = (
    ctx.web.get_folder_by_server_relative_url("/sites/team/Shared Documents")
    .get_files(recursive=True, page_size=2000)
    .execute_query()
)

Batching. Dispatch many queued operations in one round trip. Raise concurrency to run batches in parallel; throttled sub-requests are retried individually, honoring Retry-After.

for row in rows:
    ctx.web.lists.get_by_title("Contacts").add_item(row)

ctx.execute_batch()  # sequential
ctx.execute_batch(concurrency=5)  # up to 5 batches in flight

# Graph
for user in new_users:
    client.users.add(user)
client.execute_batch(concurrency=5)

Large lists (> 5,000 items). SharePoint refuses filtered/sorted queries on non-indexed columns. The library can tell you what to index and pre-flight the query.

from office365.sharepoint.listitems.caml import Caml, CamlQuery

query = CamlQuery.builder().where(Caml.text("Status").eq("Active")).order_by("ID").row_limit(2000, paged=True).build()

lst.check_query(query)  # raises with a clear message if an index is missing
print(lst.index_candidates(query))  # columns worth indexing
lst.ensure_indexed("Status").execute_query()  # create the index (builds in the background)

Import and export DataFrames. Optional helpers turn list items into pandas / Polars frames and back.

lst = ctx.web.lists.ensure_list("Orders").execute_query()

df = lst.to_dataframe().execute_query().value
lst.from_dataframe(df, chunksize=100).execute_query()

Async / await

Every call that reaches the network has an await twin. Builders stay synchronous — only the terminal call changes. No extra dependency is required: by default the blocking HTTP call is handed to a worker thread, so the event loop stays free.

import asyncio


async def main() -> None:
    web = await ctx.web.get().execute_query_async()
    print(web.title)


asyncio.run(main())

execute_query_async(), execute_batch_async(), get_all_async(), execute_query_parallel_async(), streaming downloads/exports and long-running-operation polling all have async twins, and collections support async for. See the async guide.

Dependencies

Install the core package on its own, or add the extras you need:

ExtraAddsUse it for
azureazure-storage-blob, cryptographyAzure Blob Storage and certificate helpers
excelopenpyxlReading and writing Excel workbooks
examplesfakerRunning the sample scripts
ntlmrequests-ntlmOn-premises SharePoint with NTLM
pandaspandasto_dataframe() / from_dataframe()
parquetpyarrowParquet import and export
duckdbduckdbQuerying exported data locally
sqlsqlalchemySQL-backed import and export
notebooksjupyter, nbformat, jinja2Running the notebook samples
pip install "office365-rest-python-client[pandas,excel]"

Contributing

Issues and pull requests are welcome — please use the issue tracker for bugs and ideas.

git clone https://github.com/vgrem/office365-rest-python-client.git
cd office365-rest-python-client
uv sync --all-extras
pytest --offline -q

See CONTRIBUTING.md for the full development setup. Run the gate before submitting: ruff check ., ruff format --check ., pyright and pytest --offline -q.

Support

If this project saves you time, please consider sponsoring its development. ⭐

License

Released under the MIT License.

entra-id
hacktoberfest
intune
microsoft365
microsoft-graph
microsoft-purview
microsoft-teams
office365
onedrive
onenote
outlook
planner
python
sdk-python
sharepoint
sharepoint-rest-api

Project health

Latest release

3.2.0 · Sep 27, 2026

Releases this year

3

Significant stargazers

Thomas Schmelzer

361 followers · starred Jun 2022

Jun Komoda

17 followers · starred Dec 2022

Pradyumna Singh Rathore

23 followers · starred Oct 2025

Aamir

32 followers · starred Mar 2023