Plain fetch() or XMLHttpRequest work fine for hitting SharePoint’s REST API — the part that’s actually SharePoint-specific isn’t the HTTP call itself, it’s the headers: an Accept header SharePoint’s OData implementation expects, and a request digest token required on anything that isn’t a GET.
In this post: A GET request: fetching list items · A POST request, and the request digest · When would you actually use this? · Related reading
A GET request: fetching list items
function getListItems() {
const siteUrl = _spPageContextInfo.webAbsoluteUrl;
const endpoint = `${siteUrl}/_api/web/lists/getbytitle('Tasks')/items`;
fetch(endpoint, {
method: "GET",
headers: {
"Accept": "application/json;odata=nometadata"
}
})
.then(response => {
if (!response.ok) { throw new Error(`Request failed: ${response.status}`); }
return response.json();
})
.then(data => console.log(data.value))
.catch(error => console.error(error));
}
The Accept: application/json;odata=nometadata header is what makes SharePoint return plain JSON instead of its older, verbose OData format wrapping every field in metadata. GET requests are otherwise unremarkable — no digest, no special auth handling beyond whatever cookie/token already authenticates the page.
A POST request, and the request digest
This is the part that trips people up coming from a generic REST API background: any request that changes data (POST, MERGE, DELETE) needs an X-RequestDigest header, or SharePoint rejects it outright with a 403. The digest is a short-lived security token already embedded in every SharePoint page — you don’t request it separately, you just read it off the page:
function createListItem() {
const siteUrl = _spPageContextInfo.webAbsoluteUrl;
const endpoint = `${siteUrl}/_api/web/lists/getbytitle('Tasks')/items`;
const digest = document.getElementById("__REQUESTDIGEST").value;
fetch(endpoint, {
method: "POST",
headers: {
"Accept": "application/json;odata=nometadata",
"Content-Type": "application/json;odata=verbose",
"X-RequestDigest": digest
},
body: JSON.stringify({
"__metadata": { "type": "SP.Data.TasksListItem" },
"Title": "New task from a POST request"
})
})
.then(response => {
if (!response.ok) { throw new Error(`Request failed: ${response.status}`); }
return response.json();
})
.then(data => console.log("Created:", data))
.catch(error => console.error(error));
}
Two things worth flagging: the __metadata.type value has to match the list’s actual entity type name (SP.Data.<ListName>ListItem, with some naming quirks for lists that have spaces or start with a number) — get this wrong and the request fails with a metadata error, not an obviously-labeled one. And the digest embedded in the page has a limited lifetime (typically 30 minutes) — for a long-running SPA that doesn’t reload the page, re-fetch a fresh digest via a call to /_api/contextinfo rather than assuming the original one is still valid.
Any request that changes data needs an X-RequestDigest header, or SharePoint rejects it with a 403 — this is the part a generic REST tutorial won’t tell you.
When would you actually use this?
- You’re writing plain JavaScript inside a SharePoint page (a script editor web part, a classic page) and don’t want to pull in PnPjs for a single request.
- You’re debugging why a POST/PATCH request keeps failing with a 403 — a missing or expired
X-RequestDigestis the most common cause. - You need to understand what a library like PnPjs is actually doing under the hood, since it wraps exactly this pattern.
Related reading
- SharePoint REST API File Uploads Using JavaScript and PnPjs — the same digest-and-headers pattern applied to file uploads specifically, including chunked uploads for large files.
- Exploring SharePoint REST API Endpoints — a reference list of the endpoints you’ll actually call once you’re past the basic GET/POST pattern here.
- CSOM, JSOM and REST API in SharePoint: Quick Overview — if you’re deciding whether REST is even the right API for a given project versus CSOM or JSOM.
That covers the core pattern. Let me know in the comments if your setup needs something different.
App Catalog Authentication Automation Backup Compliance Content Type CSS Flows Google Javascript Limitations List Metadata MFA Microsoft Node NodeJs O365 OneDrive Permissions PnP PnPJS Policy PowerApps Power Automate PowerAutomate PowerPlatform PowerShell React ReactJs Rest API Rest Endpoint Security Send an HTTP Request to SharePoint SharePoint SharePoint List SharePoint Modern SharePoint Online SPFX SPO Sync Tags Teams Termstore Versioning


