How to Access a SharePoint Document Library with Microsoft Graph API
Use Microsoft Graph to find a SharePoint site, select its document library, list files and download content with least-privileged permissions.
Microsoft Graph represents a SharePoint document library as a drive. The default library is available at /sites/{siteId}/drive; use /sites/{siteId}/drives to discover every library on a site. Files and folders inside a library are driveItem resources.
The usual sequence is:
- Obtain a bearer token with the least-privileged permissions required.
- Resolve the SharePoint site to a site ID.
- Get the default library or enumerate all libraries.
- Address a folder or file by ID or path.
- List children and follow pagination links.
- Download file bytes from the item’s
/contentendpoint.
1. Choose the Graph permissions
Use delegated permissions when the request runs for a signed-in user. Use application permissions when a background service runs without a user. Grant tenant consent and confirm that the identity can access the target site.
| Operation | Delegated work or school | Application |
|---|---|---|
| Resolve a site by hostname and path | Sites.Read.All |
Sites.Read.All |
| Read drive or driveItem metadata | Files.Read |
Files.Read.All |
| List folder children | Files.Read |
Files.Read.All |
| Download file content | Files.Read |
Files.Read.All |
These are least-privileged permissions for the documented read operations. Your tenant may require administrator consent or additional site access. Do not treat a successful site lookup as proof that every library item is readable.
See Microsoft’s site lookup, drive, driveItem, children and content references for the current permission matrix.
2. Resolve the SharePoint site
If you know the tenant host and the server-relative site path, call:
GET https://graph.microsoft.com/v1.0/sites/contoso.sharepoint.com:/sites/Engineering
Authorization: Bearer YOUR_ACCESS_TOKEN
The response contains an id, displayName, name and webUrl. Save the id; later requests use it as {siteId}.
curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
"https://graph.microsoft.com/v1.0/sites/contoso.sharepoint.com:/sites/Engineering"
The path is relative to the SharePoint hostname. Encode reserved characters when constructing a URL programmatically.
3. Select the document library
Use the site’s default library
GET https://graph.microsoft.com/v1.0/sites/{siteId}/drive
Authorization: Bearer YOUR_ACCESS_TOKEN
A Graph drive is the top-level container for a SharePoint document library. This is the shortest route when the default library is the intended target.
Discover every library
GET https://graph.microsoft.com/v1.0/sites/{siteId}/drives
Authorization: Bearer YOUR_ACCESS_TOKEN
Inspect each returned drive’s id, name, driveType and webUrl. Select the library explicitly when the site has multiple libraries or the target is not the default.
curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
"https://graph.microsoft.com/v1.0/sites/SITE_ID/drives"
4. Address a folder or file
You can use a drive item ID, or resolve an item by a path relative to the library root.
By item ID
GET https://graph.microsoft.com/v1.0/drives/{driveId}/items/{itemId}
Authorization: Bearer YOUR_ACCESS_TOKEN
By path
GET https://graph.microsoft.com/v1.0/sites/{siteId}/drive/root:/Reports/2026/summary.xlsx
Authorization: Bearer YOUR_ACCESS_TOKEN
For a non-default library, use the selected drive in the equivalent drive route:
GET https://graph.microsoft.com/v1.0/drives/{driveId}/root:/Reports/2026/summary.xlsx
Paths are case-insensitive in many SharePoint scenarios, but preserve the exact name returned by Graph and URL-encode spaces, #, % and other reserved characters. IDs are safer for long-lived references because a rename does not change the item ID.
5. List folders and follow pagination
Folders expose a children relationship. Start with the folder item ID:
GET https://graph.microsoft.com/v1.0/drives/{driveId}/items/{folderItemId}/children
Authorization: Bearer YOUR_ACCESS_TOKEN
Each response contains a value array. A collection can span multiple pages; when @odata.nextLink is present, request that URL until it is absent. Treat the next link as opaque and do not rebuild it manually.
curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
"https://graph.microsoft.com/v1.0/drives/DRIVE_ID/items/FOLDER_ID/children"
Use the returned item’s file property to identify files and its folder property to identify folders. Request only the fields needed by adding $select=id,name,size,file,folder,lastModifiedDateTime,webUrl.
6. Download file content
Once you have the file item ID, request its primary content stream:
GET https://graph.microsoft.com/v1.0/sites/{siteId}/drive/items/{itemId}/content
Authorization: Bearer YOUR_ACCESS_TOKEN
curl -L -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
"https://graph.microsoft.com/v1.0/sites/SITE_ID/drive/items/ITEM_ID/content" \
-o downloaded-file
The -L option follows the download response redirect. Keep metadata and content requests separate: metadata tells you what the item is, while /content transfers its bytes.
7. Complete Python example
This example assumes that ACCESS_TOKEN was acquired through your chosen Microsoft identity flow.
import os
from urllib.parse import quote
import requests
GRAPH = "https://graph.microsoft.com/v1.0"
TOKEN = os.environ["ACCESS_TOKEN"]
HOST = "contoso.sharepoint.com"
SITE_PATH = "/sites/Engineering"
headers = {"Authorization": f"Bearer {TOKEN}"}
# 1. Resolve the site.
site_url = f"{GRAPH}/sites/{HOST}:{SITE_PATH}"
site = requests.get(site_url, headers=headers, timeout=30)
site.raise_for_status()
site_id = site.json()["id"]
# 2. Enumerate libraries and choose one by name.
drives = requests.get(
f"{GRAPH}/sites/{site_id}/drives",
headers=headers,
timeout=30,
)
drives.raise_for_status()
libraries = drives.json()["value"]
drive = next(d for d in libraries if d["name"] == "Documents")
drive_id = drive["id"]
# 3. Resolve a file by path.
item_path = "Reports/2026/summary.xlsx"
item_url = f"{GRAPH}/drives/{drive_id}/root:/{quote(item_path, safe='/')}"
item = requests.get(item_url, headers=headers, timeout=30)
item.raise_for_status()
item_id = item.json()["id"]
# 4. Download its bytes.
content = requests.get(
f"{GRAPH}/drives/{drive_id}/items/{item_id}/content",
headers=headers,
timeout=120,
)
content.raise_for_status()
with open("summary.xlsx", "wb") as output:
output.write(content.content)
print(f"Downloaded {item.json()['name']} ({len(content.content)} bytes)")
8. Complete Node.js example
const GRAPH = 'https://graph.microsoft.com/v1.0';
const token = process.env.ACCESS_TOKEN;
const headers = { Authorization: `Bearer ${token}` };
const host = 'contoso.sharepoint.com';
const sitePath = '/sites/Engineering';
const siteRes = await fetch(`${GRAPH}/sites/${host}:${sitePath}`, { headers });
if (!siteRes.ok) throw new Error(`Site lookup failed: ${siteRes.status}`);
const site = await siteRes.json();
const drivesRes = await fetch(`${GRAPH}/sites/${site.id}/drives`, { headers });
if (!drivesRes.ok) throw new Error(`Drive listing failed: ${drivesRes.status}`);
const drives = await drivesRes.json();
const drive = drives.value.find((d) => d.name === 'Documents');
if (!drive) throw new Error('The Documents library was not found');
const itemPath = 'Reports/2026/summary.xlsx'
.split('/')
.map(encodeURIComponent)
.join('/');
const itemRes = await fetch(
`${GRAPH}/drives/${drive.id}/root:/${itemPath}`,
{ headers }
);
if (!itemRes.ok) throw new Error(`Item lookup failed: ${itemRes.status}`);
const item = await itemRes.json();
const contentRes = await fetch(
`${GRAPH}/drives/${drive.id}/items/${item.id}/content`,
{ headers, redirect: 'follow' }
);
if (!contentRes.ok) throw new Error(`Download failed: ${contentRes.status}`);
const bytes = Buffer.from(await contentRes.arrayBuffer());
await import('node:fs/promises').then((fs) => fs.writeFile('summary.xlsx', bytes));
console.log(`Downloaded ${item.name} (${bytes.length} bytes)`);
9. Common errors and fixes
| Error | Likely cause | Fix |
|---|---|---|
| 401 Unauthorized | Missing, expired or malformed bearer token. | Acquire a fresh token for Microsoft Graph and send Authorization: Bearer .... |
| 403 Forbidden | The app lacks the endpoint’s permission, admin consent or site access. | Check delegated versus application permissions, grant consent and verify the identity can read the site. |
| 404 Not Found on site lookup | Wrong hostname, server-relative path or site ID. | Copy the host and path from the SharePoint URL; do not include a full https:// URL in the path form. |
404 for /drive |
The request assumes a default library that is unavailable or the site ID is wrong. | Verify the site ID, then enumerate /drives and select the intended library. |
| Item not found by path | Incorrect folder names, unescaped characters or a path relative to the wrong drive. | URL-encode each path segment, start at the selected drive root and list children to confirm names. |
| Only some files appear | The folder response is paginated. | Keep requesting @odata.nextLink until it is absent. |
| Download returns JSON instead of a file | The request failed before reaching the content stream. | Check the status code and response body, then retry with a valid item ID and permission. |
| Throttling (429) | Too many requests in a short period. | Honor Retry-After, use exponential backoff with jitter and avoid repeatedly resolving the same site or path. |
10. Reliability, performance and cost considerations
- Cache stable site IDs and drive IDs, but revalidate when administrators move or recreate libraries.
- Prefer item IDs after discovery; paths are convenient but can break when folders are renamed.
- Use
$selectto reduce metadata payloads and request only the fields required by the job. - Process paged children incrementally instead of loading a very large library into memory.
- Retry transient 429 and 5xx responses with bounded exponential backoff. Never retry authentication failures blindly.
- Stream large downloads to disk rather than buffering the entire response in memory.
- Microsoft Graph usage is governed by your Microsoft 365 licensing, tenant limits and throttling policies; the endpoint itself does not establish a separate fixed per-request price.
11. Or skip the browser setup
If your goal is to capture a rendered SharePoint page or document view rather than retrieve file bytes, ScreenshotNeo provides a single screenshot request. Its capture flow accepts consent banners before the shot and removes more than 60 known consent platforms, newsletter popups and chat widgets. Bot checks, blank pages, timeouts, failed loads and cache hits are not billed, and response headers identify the page verdict and billing status.
See the ScreenshotNeo API documentation for all options.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests; r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90); open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' }); const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
You can also capture PDFs, select an element, load lazy images, set a device or viewport, run custom JavaScript, hide selectors, block requests, provide cookies or headers, wait for network idle and submit bulk jobs. Its MCP server exposes take_screenshot, get_page_info and capture_pdf for AI clients such as Claude and Cursor. The free plan includes 1,000 screenshots each month with no card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account.
12. FAQ
Is a SharePoint library the same thing as a Graph drive?
For Graph file operations, yes: a SharePoint document library is exposed as a drive, and its files and folders are driveItem resources.
How do I access a library that is not the default?
Call /sites/{siteId}/drives, find the library by its returned metadata and use that drive ID for item and content requests.
Should I store paths or IDs?
Use paths during discovery and IDs for durable references. A renamed path can stop resolving, while an item’s ID normally remains stable.
Can application permissions read every SharePoint site?
Only when the tenant and resource permissions allow it. A valid application token still needs the required Graph permission and access to the target data.
Does listing a folder download its files?
No. Listing returns metadata. Call the item’s /content endpoint to download bytes.


