Table of contents

Add hundreds of integrations to your product through Merge’s Unified API
Get a demo

How to integrate with the OneDrive API via Python

Michael Nyamande
@Merge

Microsoft OneDrive offers cloud-based file storage and synchronization, enabling users to securely save, access, and share files from any device.

To help you access and interact with a wide range of file data and functionality in the platform, you can leverage the OneDrive API

The API is accessible through Microsoft Graph and provides developers with programmatic access to file storage, uploads and downloads, folder management, real-time collaboration features, and more.

Regardless of your integration use case for OneDrive, we’ll walk you through the process of integrating with its endpoints via Python.

Prerequisites

Before starting this tutorial, you’ll need the following:

  • Microsoft 365 account to test your integration.
  • Microsoft Entra ID app registration access with administrative privileges to create and configure applications in Microsoft Entra ID (formerly Azure Active Directory).
  • Python 3.7 or higher. OneDrive API integration requires modern Python features and library support.

In addition, consider bookmarking the Microsoft Graph documentation as a reference throughout the development process. This documentation provides comprehensive endpoint details, response schemas, and troubleshooting guidance.

Related: How to integrate with Dropbox's API

Setting up app-based authentication

The first thing you need to do is install the dependencies:

 pip install msal requests python-dotenv

This installs the Microsoft Authentication Library (MSAL) for Python and Requests libraries, which handle Microsoft authentication and send HTTP requests to the OneDrive API. The python-dotenv package is also included to securely load your API credentials from an ENV file, keeping sensitive information out of your source code.

To authenticate with the OneDrive API via OAuth 2.0, you need to create an application on the Azure portal. This lets you securely read and write to your OneDrive. Authenticating using OAuth 2.0 is a complex process, but it's necessary to help keep user credentials private while giving you fine-grained control over application permissions.

To set up an application, navigate to the Azure portal, go to Microsoft Entra ID, and select App registrations:

App registrations screen

Select New registration, enter your app name, and set the redirect URI type to web and the URL to http://localhost:8080/callback. Leave all other fields in their default state and click the Register button to register a new application.

Go to the Application Overview and save your Application(client) ID and Directory (tenant) ID. You'll use these later in the authentication process:

Application Overview screen

Next, generate a client secret by clicking Certificates & secrets. Under the Manage accordion, select New client secret, give the secret a name, and copy the generated secret:

Certificates & secrets screen

Create an ENV file and add the three variables you've collected:

CLIENT_ID=

CLIENT_SECRET=

TENANT_ID=

Lastly, you need to give your application the right permissions to read and write data to OneDrive. Navigate to the API permissions tab and click Add a permission. Select Microsoft Graph > Delegated Permissions. Then go to File permissions and select Files.ReadWrite.All, click Add permissions, and grant admin consent:

API permissions screen

The Files.ReadWrite.All permission grants your application full read and write access to all files that the signed-in user can access in their OneDrive. This means your app can upload, download, edit, and delete files and folders, as well as create new ones.

Authenticating via OAuth 2.0

Now that you have the application set up in Microsoft Azure, use the following code snippet to handle authenticating with the OneDrive API using MSAL. Create a file, name it <code class="blog_inline-code">onedrive_auth.py</code>, and add the following code:

```python
import os
import msal
import json
from dotenv import load_dotenv
import webbrowser
from urllib.parse import urlparse, parse_qs
from http.server import HTTPServer, BaseHTTPRequestHandler
import threading
import requests

# Load environment variables
load_dotenv()

TOKEN_FILE = "onedrive_token.json"

class CallbackHandler(BaseHTTPRequestHandler):
    def do_GET(self):
        query_params = parse_qs(urlparse(self.path).query)
        
        if 'code' in query_params:
            self.server.auth_code = query_params['code'][0]
            self.send_response(200)
            self.send_header('Content-type', 'text/html')
            self.end_headers()
            self.wfile.write(b"

Success! You can close this window.

") else: self.send_response(400) self.send_header('Content-type', 'text/html') self.end_headers() self.wfile.write(b"

Authorization failed.

") def log_message(self, format, *args): return # Suppress logs def save_tokens(access_token, refresh_token): """Save the access token and refresh token to a file.""" token_data = { "access_token": access_token, "refresh_token": refresh_token } with open(TOKEN_FILE, 'w') as token_file: json.dump(token_data, token_file, indent=2) def load_tokens(): """Load the access token and refresh token from a file if it exists.""" if not os.path.exists(TOKEN_FILE): return None, None try: with open(TOKEN_FILE, 'r') as token_file: data = json.load(token_file) return data.get("access_token"), data.get("refresh_token") except (json.JSONDecodeError, KeyError): return None, None def validate_token(access_token): """Test if token is valid by making a simple API call.""" headers = {'Authorization': f'Bearer {access_token}'} response = requests.get('https://graph.microsoft.com/v1.0/me', headers=headers) if response.status_code == 200: return response.json() raise Exception(f"Token validation failed: {response.status_code}") def perform_oauth_flow(app_key, app_secret): """Perform the OAuth2 flow to get new access and refresh tokens.""" scopes = ['https://graph.microsoft.com/Files.ReadWrite.All'] tenant_id = os.getenv('TENANT_ID', 'common') redirect_uri = 'http://localhost:8080/callback' app = msal.ConfidentialClientApplication( client_id=app_key, client_credential=app_secret, authority=f'https://login.microsoftonline.com/{tenant_id}' ) auth_url = app.get_authorization_request_url( scopes=scopes, redirect_uri=redirect_uri ) # Start a local HTTP server to catch the callback server = HTTPServer(('localhost', 8080), CallbackHandler) server.auth_code = None # Start server in a separate thread server_thread = threading.Thread(target=server.serve_forever) server_thread.daemon = True server_thread.start() print("🚀 Opening browser for authentication...") webbrowser.open(auth_url) # Wait for the callback while server.auth_code is None: server.handle_request() server.shutdown() result = app.acquire_token_by_authorization_code( code=server.auth_code, scopes=scopes, redirect_uri=redirect_uri ) if 'access_token' in result: return result['access_token'], result.get('refresh_token') else: raise Exception(f"Authentication failed: {result.get('error_description')}") def authenticate_onedrive(): """ Authenticates with OneDrive using saved token or OAuth2 flow. Returns a OneDrive client instance. """ # Get credentials from environment variables app_key = os.getenv('CLIENT_ID') app_secret = os.getenv('CLIENT_SECRET') if not app_key or not app_secret: print("Error: CLIENT_ID and CLIENT_SECRET environment variables must be set.") return None # Try existing tokens first access_token, refresh_token = load_tokens() if access_token: try: account_info = validate_token(access_token) print(f"✅ Using saved token. Authenticated as: {account_info['displayName']}") return access_token except Exception as error: print(f"⚠️ Saved token expired: {error}") print("🔄 Getting new token...") # Get new tokens via OAuth flow try: access_token, refresh_token = perform_oauth_flow(app_key, app_secret) save_tokens(access_token, refresh_token) account_info = validate_token(access_token) print(f"✅ Successfully authenticated as: {account_info['displayName']}") return access_token except Exception as error: print(f"❌ Error during authentication: {error}") return None if __name__ == '__main__': access_token = authenticate_onedrive() if access_token: print("🎉 Authentication successful! You can now use the access_token object.") ```

This code initiates the OAuth 2.0 authentication process for OneDrive using your application's client ID and secret. It launches a local server that captures the authorization code after you grant consent in your browser.

Once the code is received, it exchanges it for access and refresh tokens. These tokens are securely stored and can be reused in future sessions, making subsequent authentications more efficient and seamless.

The MSAL library doesn't automatically persist tokens or manage refresh and rotation for you; you need to implement it yourself when building for production. Run the code using python <code class="blog_inline-code">onedrive_auth.py</code>. You should see an output that looks like this:

```bash
🚀 Opening browser for authentication...
✅ Successfully authenticated as: Michael Nyamande
🎉 Authentication successful! You can now use the access_token object.
```

Related: How to authenticate with Google Drive's API

Performing file operations with Python

After authenticating, you can interact with OneDrive's file system.

OneDrive organizes content hierarchically, with each user having a default drive accessible through the /me/drive endpoint. Files and folders are represented as DriveItem resources, each containing metadata like size, modification dates, and sharing permissions. Understanding this structure is essential for robust integrations.

The following are some of the key operations you can perform via the API:

Listing files in a OneDrive folder

To list all files and folders in a OneDrive folder, you need to call the <code class="blog_inline-code">/me/drive/root:/{folder_path}:/children</code> endpoint, replacing {folder} with the path of the folder you want to explore. For the root folder, you can use /me/drive/root/children. Since results are returned in batches, you may need to handle pagination if there are several items.

Here's how you can retrieve the full contents of a folder:

```python
import requests
from onedrive_auth import authenticate_onedrive

def list_folder_contents(access_token, folder_path=""):
    """
    Lists the contents of a OneDrive folder, handling pagination.
    An empty folder_path represents the root directory.
    """
    try:
        endpoint = f"https://graph.microsoft.com/v1.0/me/drive/root/children"
        if folder_path:
            endpoint = f"https://graph.microsoft.com/v1.0/me/drive/root:/{folder_path}:/children"
        
        headers = {
            'Authorization': f'Bearer {access_token}',
            'Content-Type': 'application/json'
        }
        
        response = requests.get(endpoint, headers=headers)
        
        if response.status_code == 200:
            data = response.json()
            entries = data.get('value', [])
            
            # Handle pagination
            while '@odata.nextLink' in data:
                response = requests.get(data['@odata.nextLink'], headers=headers)
                if response.status_code == 200:
                    data = response.json()
                    entries.extend(data.get('value', []))
                else:
                    break
            
            for entry in entries:
                if 'folder' in entry:
                    print(f"- Folder: {entry['name']} (ID: {entry['id']}, Last Modified: {entry['lastModifiedDateTime']})")
                else:
                    print(f"- File: {entry['name']} (ID: {entry['id']}, Last Modified: {entry['lastModifiedDateTime']})")
                    
        else:
            print(f"API error listing folder '{folder_path}': {response.status_code} - {response.text}")

    except requests.RequestException as e:
        print(f"Network error listing folder '{folder_path}': {e}")

if __name__ == '__main__':
    access_token = authenticate_onedrive()
    
    if access_token:
        print("\nListing root folder contents:")
        list_folder_contents(access_token, "")  # List root
```

This code securely connects to your OneDrive and lists every file and folder in a given directory. It handles large folders by following the @odata.nextLink property in the API response, iteratively fetching additional pages of results until everything is shown. For each item, it prints the name, unique id (an identifier you can use to fetch and update it from the API), and lastModifiedDateTime (a time string indicating when the item was last updated in OneDrive). The code also reports any errors if something goes wrong.

You can run the code using <code class="blog_inline-code">python list_files.py</code> and then get an output that looks like this:

```
bash
✅ Using saved token. Authenticated as: John Doe

Listing root folder contents:
- Folder: Attachments (ID: 01YQFXKINR5WJNE6KSBFF2PHYV2BD5TAF2, Last Modified: 2025-05-01T12:11:05Z)
- Folder: Microsoft Copilot Chat Files (ID: 01YQFXKIO2Y4NXAIJKY5HKM4H4CN7OZGXV, Last Modified: 2025-04-30T13:53:47Z)
- Folder: reports (ID: 01YQFXKIMXNHB4X2733NBYJ2KKWQMXXEVM, Last Modified: 2025-04-28T16:41:05Z)
- File: Q1-2025.pdf (ID: 01YQFXKIMSADSAYHSG45D3YMF7WQ64PIWA, Last Modified: 2025-05-15T07:54:38Z)
```

Uploading a file to OneDrive

The OneDrive API provides different mechanisms for uploading files based on their size. For files smaller than 4 MB, you can perform the upload in a single request by reading the file's binary content and passing it to the function, along with the desired OneDrive path.

The following is a code snippet showing how to upload small files:

```python
import os
import requests
from onedrive_auth import authenticate_onedrive

def upload_small_file(access_token, local_path, onedrive_path):
    """Uploads a small file (< 4 MB) to OneDrive."""
    try:
        # Check file size
        file_size = os.path.getsize(local_path)
        if file_size > 4 * 1024 * 1024:  # 4MB limit
            print(f"Error: File size ({file_size} bytes) exceeds 4MB simple upload limit.")
            return None
        
        endpoint = f"https://graph.microsoft.com/v1.0/me/drive/root:/{onedrive_path}:/content"
        
        headers = {
            'Authorization': f'Bearer {access_token}',
            'Content-Type': 'application/octet-stream'
        }
        
        with open(local_path, 'rb') as f:
            response = requests.put(endpoint, headers=headers, data=f)
        
        if response.status_code in [200, 201]:
            file_info = response.json()
            print(f"Successfully uploaded '{local_path}' to '{onedrive_path}'")
            return file_info
        else:
            print(f"API error uploading file: {response.status_code} - {response.text}")
            return None
            
    except FileNotFoundError:
        print(f"Error: Local file '{local_path}' not found.")
        return None
    except requests.RequestException as e:
        print(f"Network error uploading file: {e}")
        return None

if __name__ == '__main__':
    access_token = authenticate_onedrive()
    
    if access_token:
        # Example usage (replace with actual file paths)
        upload_small_file(access_token, "report.pdf", "reports/2024_report.pdf")
```

Downloading a file from OneDrive

To retrieve files from OneDrive, you need to obtain the file's metadata, which provides a secure download URL. Once you have this URL, you can fetch the file's actual content and save it locally. This approach ensures you always use the most up-to-date and authorized link for downloading files.

Here's a script to download a file:

```python
import requests
from onedrive_auth import authenticate_onedrive

def download_file(access_token, onedrive_path, local_path):
    """Downloads a file from OneDrive."""
    try:
        # Get file by path
        endpoint = f"https://graph.microsoft.com/v1.0/me/drive/root:/{onedrive_path}"
        
        headers = {
            'Authorization': f'Bearer {access_token}',
            'Content-Type': 'application/json'
        }
        
        # Get file metadata
        response = requests.get(endpoint, headers=headers)
        
        if response.status_code != 200:
            print(f"Error finding file: {response.status_code} - {response.text}")
            return None
        
        file_info = response.json()
        download_url = file_info.get('@microsoft.graph.downloadUrl')
        
        if not download_url:
            print("Error: Download URL not available for this file")
            return None
        
        # Download the file
        download_response = requests.get(download_url)
        
        if download_response.status_code == 200:
            with open(local_path, 'wb') as f:
                f.write(download_response.content)
            print(f"Successfully downloaded '{onedrive_path}' to '{local_path}'")
            print(f"Metadata: Name: {file_info['name']}, Size: {file_info['size']} bytes")
            return file_info
        else:
            print(f"Error downloading file: {download_response.status_code}")
            return None
            
    except requests.RequestException as e:
        print(f"Network error downloading file: {e}")
        return None

if __name__ == '__main__':
    access_token = authenticate_onedrive()
    
    if access_token:
        # Example usage (replace with actual file paths)
        download_file(access_token, "reports/2024_report.pdf", "downloaded_report.pdf")
```

This download_file function retrieves a file from OneDrive by initially requesting its metadata. Then it uses the @microsoft.graph.downloadUrl property, a pre-authenticated URL (typically valid for about an hour), to securely download the file's contents. This approach means you don't need to handle additional authentication for the actual file transfer.

To use this script with your own files, simply replace "reports/2024_report.pdf" and "downloaded_report.pdf" in the example with your file's OneDrive file path and your desired local file name, respectively. Ensure that the OneDrive path matches an existing file you have access to.

Optionally, you can use metadata fields (like lastModifiedDateTime, name, or type) to programmatically decide which file to download (e.g., picking the most recently modified file or filtering by a file name pattern such as *.pdf). This gives developers the flexibility to automate retrieval instead of hard-coding names. For more details on supported filtering parameters and syntax, check out this documentation.

You can find all the code used in this tutorial on GitHub.

Challenges of integrating with OneDrive's API

Microsoft Graph offers powerful file management capabilities, but that doesn't mean it's perfect. You'll likely experience one—if not several—of the following challenges.

Managing OAuth and token refresh complexity

Managing OAuth 2.0 in production is a difficult and ongoing responsibility that extends beyond simply obtaining an initial access token.

Access tokens issued by Microsoft Graph are intentionally short-lived, typically expiring within an hour, so your application needs to request new tokens using refresh tokens. This requires careful implementation of secure token storage, robust refresh logic, and strategies for handling token rotation and invalidation.

In distributed systems, synchronizing token state across multiple services or servers adds another layer of difficulty, and any lapse in security or coordination can expose your application to risk or service interruptions.

The technical challenges are even more complex in enterprise environments. Conditional access policies, multifactor authentication (MFA), and tenant-specific configurations can all introduce unexpected authentication failures or edge cases that are difficult to anticipate. For example, a user may suddenly be required to re-consent to permissions, or a refresh token may be revoked due to a policy change.

Implementing rate limits, throttling, and retries

Microsoft Graph enforces dynamic rate limiting that adapts based on tenant size, resource usage patterns, and system load. Unlike static rate limits with predictable boundaries, Graph's throttling can vary significantly between requests and time periods. Applications that function perfectly during development may encounter unexpected rate limiting in production with higher usage volumes.

Applying upload limits and chunked file handling

While OneDrive's 4 MB simple upload limit is sufficient for everyday documents and images, it quickly becomes a barrier when working with larger files, like videos, data sets, or design assets. Supporting these use cases requires developers to implement chunked uploads, which involves creating upload sessions, dividing files into sequential chunks, managing the upload and sequencing of each part, and handling errors or network interruptions that may occur mid-transfer.

This process also demands careful memory management to avoid loading large files entirely into memory, as well as logic to resume interrupted uploads and track progress throughout.

Integrating inconsistent APIs across storage platforms

Most teams need to integrate with more than one file storage provider. The problem is that each service, whether it's OneDrive, Google Drive, Dropbox, or Box, has its own authentication process, endpoint structure, error handling, and metadata format. This means you end up maintaining separate code for each provider, which increases the complexity of error handling, testing, and documentation.

Every time you add a new provider, you have to learn its unique API and adapt your integration to fit. As providers update their APIs or change requirements, the maintenance burden grows.

Performing debugging and observability gaps

Microsoft Graph provides limited debugging information when requests fail, particularly at scale. Error messages often lack the necessary context to identify root causes, especially for authentication failures, permission issues, or rate-limiting edge cases. Generic error codes without sufficient detail make issue resolution inefficient.

Integrate OneDrive and any other file storage solution with your product via Merge’s Unified API 

Integrating with OneDrive is no small task—and the challenge only multiplies as you scale file storage integrations for more customers.

New edge cases surface. Additional use cases demand support. And each new provider, like Google Drive, brings another custom build.

Merge’s File Storage Unified API eliminates this complexity by letting you add any file storage integration with a File Storage Unified API.

A snapshot of the file storage integrations Merge supports
A snapshot of the file storage integrations Merge supports

The Unified API not only lets you add file storage integrations but also provides:

  • Comprehensive standardization: Files, folders, and permissions are automatically normalized into Merge’s standardized data models—giving you cross-provider access without building custom integrations.
  • Customizability: Through features like Field Mapping and Authenticated Passthrough Request, you can sync any custom file storage data (i.e., it can extend beyond Merge’s Common Models)
  • Enterprise-grade security: Merge respects each provider’s access control lists (ACLs), ensuring users only see the files and folders their permissions allow.

Learn more about Merge’s File Storage Unified API by scheduling a demo with an integration expert.

“It was the same process, go talk to their team, figure out their API. It was taking a lot of time. And then before we knew it, there was a laundry list of HR integrations being requested for our prospects and customers.”

Name
Position
Position
Michael Nyamande
@Merge

Read more

Inside Merge: how we’re building the leading sync engine

Company

A guide to integrating with Paylocity’s API 

Engineering

A guide to integrating with HiBob’s API

Insights

Subscribe to the Merge Blog

Get stories from Merge straight to your inbox

Subscribe

But Merge isn’t just a Unified 
API product. Merge is an integration platform to also manage customer integrations.  gradient text
But Merge isn’t just a Unified 
API product. Merge is an integration platform to also manage customer integrations.  gradient text
But Merge isn’t just a Unified 
API product. Merge is an integration platform to also manage customer integrations.  gradient text
But Merge isn’t just a Unified 
API product. Merge is an integration platform to also manage customer integrations.  gradient text