office365-rest-python-client
A Python client library for Microsoft 365, the SharePoint REST API and Microsoft Graph.
Python 3.8+ · typed models · fluent queries · deferred execution · 700+ runnable examples
- Two clients, one library —
ClientContextspeaks the SharePoint REST API;GraphClientspeaks Microsoft Graph. - Fluent and deferred — chain
.select(),.filter(),.expand(),.top(),.order_by()and only hit the wire when you callexecute_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
- Installation
- Authentication
- Quick start
- Examples by product
- Common patterns
- Dependencies
- Contributing
- Support
Choose your client
The library ships two clients. Pick the one that matches the API you need.
ClientContext |
GraphClient |
|
|---|---|---|
| Talks to | SharePoint REST API (/_api) |
Microsoft Graph (graph.microsoft.com) |
| Entry point | ClientContext("https://contoso.sharepoint.com/sites/team") |
GraphClient(tenant="contoso.onmicrosoft.com") |
| Best for | SharePoint lists, items, files, folders, search, permissions, site and tenant administration, taxonomy, webhooks | Outlook, OneDrive, Teams, OneNote, Planner, To Do, Entra ID, Intune, Purview, Bookings, reports, security… |
| SharePoint | Full fidelity | Partial — prefer ClientContext for SharePoint-only work |
| Typed models | List, ListItem, File, Folder, Web, Site, User … |
DriveItem, Team, Message, Event, User, Group, PlannerTask … |
| Auth | certificate, interactive, device flow, cookies, NTLM (on-prem) | client secret, certificate, interactive, device flow, ROPC |
| Examples | examples/sharepoint/ |
examples/ by product |
| API reference | SharePoint REST | Microsoft 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;
GraphClientis 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.
ClientContext (SharePoint)
| Flow | Access | Method |
|---|---|---|
| Certificate | App-only | with_client_certificate(tenant, client_id, thumbprint, cert_path=...) |
| Interactive | Delegated (MFA) | with_interactive(tenant, client_id) |
| Device code | Delegated (MFA) | with_device_flow(tenant, client_id) |
| Username / password | Delegated | with_username_and_password(tenant, client_id, username, password) |
| Custom token | Either | with_access_token(token_func) |
| Browser cookies | Delegated | with_cookies(cookie_source) |
| NTLM | On-premises | ClientContext(url, allow_ntlm=True).with_user_credentials(username, password) |
| ACS / SAML | On-premises (legacy) | legacy app-only · legacy SAML |
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)
| Flow | Access | Method |
|---|---|---|
| Client secret | App-only | with_client_secret(client_id, client_secret) |
| Certificate | App-only | with_certificate(client_id, thumbprint, private_key) |
| Interactive | Delegated (MFA) | with_token_interactive(client_id) |
| Device code | Delegated (MFA) | with_device_flow(client_id) |
| Username / password | Delegated | with_username_and_password(client_id, username, password) |
| Custom token | Either | GraphClient(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()
| Area | Examples |
|---|---|
| Lists and items | lists · listitems · views |
| Files and folders | files · folders |
| Sites and webs | sites · webs · hubsites |
| Search | search |
| Permissions and sharing | permissions · sharing |
| Fields and content types | fields · contenttypes |
| Pages and navigation | pages · navigation |
| Taxonomy and profiles | taxonomy · userprofile |
| Tenant administration | tenant · groups · users |
| Webhooks, migration, advanced | webhooks · 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()
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()
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()
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)
Planner
group = client.groups.get_by_name("My Sample Team").get().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)
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")
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 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()
Usage reports
report = client.reports.get_email_activity_counts("D30").execute_query()
print(report.value) # CSV payload
Microsoft Defender
alerts = client.security.alerts_v2.top(20).get().execute_query()
for alert in alerts:
print(alert)
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)
Microsoft Purview
labels = client.security.data_security_and_governance.sensitivity_labels.get().execute_query()
for label in labels:
print(label.display_name, label.id)
Microsoft Bookings
businesses = client.solutions.booking_businesses.get().execute_query()
for business in businesses:
print(business.display_name)
Cloud communications
presence = client.users["alex@contoso.onmicrosoft.com"].presence.get().execute_query()
print(presence.availability, presence.activity)
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"))
Insights
for item in client.me.insights.trending.get().execute_query():
print(item.resource_reference)
Backup Storage
backup = client.solutions.backup_restore
backup.get().execute_query()
print(backup.service_status.status)
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()
)
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()
Dependencies
Install the core package on its own, or add the extras you need:
| Extra | Adds | Use it for |
|---|---|---|
azure |
azure-storage-blob, cryptography |
Azure Blob Storage and certificate helpers |
excel |
openpyxl |
Reading and writing Excel workbooks |
examples |
faker |
Running the sample scripts |
ntlm |
requests-ntlm |
On-premises SharePoint with NTLM |
pandas |
pandas |
to_dataframe() / from_dataframe() |
parquet |
pyarrow |
Parquet import and export |
duckdb |
duckdb |
Querying exported data locally |
sql |
sqlalchemy |
SQL-backed import and export |
notebooks |
jupyter, nbformat, jinja2 |
Running 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
- Source and examples — github.com/vgrem/office365-rest-python-client
- Documentation — vgrem.github.io/office365-rest-python-client
- Changelog — CHANGELOG.md
- Questions — GitHub Discussions or tag
office365-rest-python-clienton Stack Overflow
If this project saves you time, please consider sponsoring its development. ⭐
License
Released under the MIT License.
Release files for office365-rest-python-client 3.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| office365_rest_python_client-3.2.0.tar.gz | 1.2 MB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| office365_rest_python_client-3.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 4.7 MB
Release files / office365_rest_python_client-3.2.0.tar.gz
| Download URL | office365_rest_python_client-3.2.0.tar.gz |
|---|---|
| Size | 1.2 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
c091fb466b12ee58ac1e137a94f50f7b6648e1da11f9fd5cedfc52cfc7c6ba06
|
|
BLAKE2b-256 checksum How to use checksums |
c7f8cdd48506747a41b9c621e714b220ca6970d29c68de607052f3d928435d24
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Release files / office365_rest_python_client-3.2.0-py3-none-any.whl
| Download URL | office365_rest_python_client-3.2.0-py3-none-any.whl |
|---|---|
| Size | 3.5 MB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
7cdf0d9e06e4aa3caff994cbd6f78d5f3fdcdbc46adacb6cff9e037c6faef7dd
|
|
BLAKE2b-256 checksum How to use checksums |
ebf268ff920e42a1b3201b656ea770a1d1829692d348fc31c5efa608ca5bd245
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|