Live training: next batches coming soon. Join the waitlist:Waitlists open:Developer Training →Developer →Copilot Studio Training →Copilot Studio →

SharePoint REST API: Endpoints, Examples and CRUD (2026 Guide)

The SharePoint REST API lets you read and change SharePoint data with plain HTTP calls to the /_api/ address of any site. This GET request returns five orders from a list:

GET https://spguides.sharepoint.com/sites/SPGuides/_api/web/lists/getbytitle('SPGDemo Orders')/items?$select=ID,Title,OrderTotal&$top=5
Accept: application/json;odata=nometadata

Send it signed in to the site and you get the rows back as JSON, with no SDK or library involved.

In this tutorial, we’ll run every common sharepoint rest api call on a real SharePoint Online site: the endpoint cheat sheet, headers, OData filters, list and item CRUD, folders and files, groups, and the errors you’ll hit.

What is the SharePoint REST API?

It’s a web service built into SharePoint (client.svc) that exposes sites, lists, items, files, users and groups as URLs. You call it with GET, POST and the other HTTP methods, and it answers in JSON or XML using the OData standard.

It covers the same objects as the CSOM client object model and JSOM in SharePoint, so anything that speaks HTTP can use it: browser JavaScript, SPFx, Power Automate, PowerShell, C# or Python.

It works in SharePoint Online and SharePoint Server. More developer guides sit on the SharePoint tutorials hub.

Every URL starts with the site address plus /_api/. From there you walk the object model: _api/site is the site collection and _api/web is the current site.

SharePoint REST API endpoints cheat sheet

Here are the endpoints I use most. Add them after https://spguides.sharepoint.com/sites/SPGuides/_api/; I tested each one on our site for this post:

What you wantMethodEndpoint
Site collectionGETsite
Current site (title, language, URL)GETweb?$select=Title,Language
All lists and librariesGETweb/lists
One list by title or GUIDGETweb/lists/getbytitle('Orders') or web/lists(guid'...')
List columnsGETweb/lists/getbytitle('Orders')/fields
List itemsGETweb/lists/getbytitle('Orders')/items
One item by IDGETweb/lists/getbytitle('Orders')/items(2)
Create a listPOSTweb/lists
Create / update / delete an itemPOST / MERGE / DELETE.../items, .../items(3)
Send an item to the recycle binPOST.../items(3)/recycle()
Create a folderPOSTweb/folders
Upload a filePOSTweb/GetFolderByServerRelativeUrl('...')/Files/add(url='a.txt',overwrite=true)
Download file contentGETweb/GetFileByServerRelativeUrl('...')/$value
Current userGETweb/currentuser
Site users / groupsGETweb/siteusers, web/sitegroups
Members of a groupGETweb/sitegroups(30)/users
Form digest for writesPOSTcontextinfo

HTTP methods and headers the REST API expects

GET reads and POST creates. Updates and deletes are also sent as POST, with an X-HTTP-Method header that says what you really mean:

HeaderWhenValue
AcceptEvery callapplication/json;odata=nometadata (lean) or odata=verbose (old d.results shape)
Content-TypeCalls with a bodySame family as Accept
X-RequestDigestWrites from a browser sessionFormDigestValue from /_api/contextinfo
X-HTTP-MethodUpdate or deleteMERGE, PUT or DELETE
IF-MATCHUpdate or deleteThe item’s ETag, or * to overwrite any version

MERGE changes only the fields you send. PUT replaces the whole object, so optional fields you leave out go back to their defaults. I use MERGE for list items almost every time.

A missing or stale digest is the classic 403. The full fix is in the security validation error guide. Calls signed with an OAuth token, like PnP PowerShell, skip the digest.

How do I test a SharePoint REST API call in the browser?

Sign in to the site, then paste a GET URL in the address bar:

https://spguides.sharepoint.com/sites/SPGuides/_api/web/lists/getbytitle('SPGDemo Orders')/items?$select=ID,Title,CustomerName,OrderTotal&$top=3

The browser doesn’t send a JSON Accept header, so SharePoint answers in Atom XML. Each item comes back as an entry with its fields under m:properties, which is handy for a quick check but hard to read.

For JSON, call it from code. This browser-side fetch asks for nometadata JSON:

const site = "https://spguides.sharepoint.com/sites/SPGuides";
const res = await fetch(site + "/_api/web/lists/getbytitle('SPGDemo Orders')/items?$select=ID,Title,OrderTotal&$top=3", {
  headers: { Accept: "application/json;odata=nometadata" }
});
const data = await res.json();
console.table(data.value);   // nometadata puts the rows in "value"; verbose puts them in d.results

From PowerShell, Invoke-PnPSPRestMethod reuses your PnP sign-in, so a relative URL is enough. If you haven’t set it up, start with installing PnP PowerShell. This script reads the site, filters the list collection and gets the current user:

$web = Invoke-PnPSPRestMethod -Url "/_api/web?`$select=Title,Language,ServerRelativeUrl"
$web | Select-Object Title, Language, ServerRelativeUrl | Format-List

$filter = "startswith(Title,'SPGDemo O') or startswith(Title,'SPGDemo P')"
$lists = Invoke-PnPSPRestMethod -Url "/_api/web/lists?`$select=Title,ItemCount,BaseTemplate&`$filter=$filter"
$lists.value | Select-Object Title, ItemCount, BaseTemplate | Format-Table -AutoSize

$me = Invoke-PnPSPRestMethod -Url "/_api/web/currentuser?`$select=Id,Title,IsSiteAdmin"
$me | Select-Object Id, Title, IsSiteAdmin | Format-Table -AutoSize
Invoke-PnPSPRestMethod GET calls returning the SharePoint site title, language 1033 and SPGDemo lists

Language 1033 is English (United States). The same web endpoint with $select=Language answers the “get site language” question.

Filter, sort and expand list items with OData query options

Query options go after the endpoint: $select picks columns, $filter picks rows, $orderby sorts, $top limits and $expand pulls lookup and person fields. Here’s one call with all five:

$url = "/_api/web/lists/getbytitle('SPGDemo Orders')/items" +
       "?`$select=ID,Title,CustomerName,OrderTotal,SalesRep/Title,Product/Title" +
       "&`$expand=SalesRep,Product" +
       "&`$filter=OrderStatus eq 'Shipped'" +
       "&`$orderby=OrderTotal desc&`$top=5"
$orders = Invoke-PnPSPRestMethod -Url $url
$orders.value | Select-Object ID, Title, CustomerName, OrderTotal,
    @{n='SalesRep';e={$_.SalesRep.Title}}, @{n='Product';e={$_.Product.Title}} | Format-Table -AutoSize
SharePoint REST API filter orderby top and expand on SPGDemo Orders returning shipped orders with sales rep

Only three orders are shipped, so $top=5 returned three, highest total first. SalesRep and Product came back as names because both are in $expand.

Three things that caught me in testing:

  • Without $top, the items endpoint returns only the first 100 rows plus an odata.nextLink. Follow that link to get the next page.
  • $skip is ignored for list items. $skip=10&$top=2 still gave me IDs 1 and 2, so page with the $skiptoken inside nextLink.
  • Use internal names. $select=Customer Name fails; CustomerName works.

This is the nextLink our 210-row list returned:

.../items?%24skiptoken=Paged%3dTRUE%26p_ID%3d100&%24select=ID

Supported filter functions include eq, ne, gt, lt, startswith() and substringof(). endswith and tolower aren’t. For huge lists, a CAML query gives you more control, and Power Automate users can follow REST API pagination in Power Automate.

Create a list and add items with POST

Creating a list is a POST to the lists collection with a title and a template (100 is a custom list, 101 a document library). Then each item is a POST to its items endpoint:

$body = @{ Title = "SPGDemo Vendors"; BaseTemplate = 100; Description = "Vendors for the REST API demo" }
$list = Invoke-PnPSPRestMethod -Method Post -Url "/_api/web/lists" -Content $body
"Created list: $($list.Title) (template $($list.BaseTemplate))"

foreach ($v in "Northwind Traders", "Fabrikam Inc.", "Litware Supplies") {
    $item = Invoke-PnPSPRestMethod -Method Post -Url "/_api/web/lists/getbytitle('SPGDemo Vendors')/items" -Content @{ Title = $v }
    "Added item $($item.Id): $($item.Title)"
}
SharePoint REST API POST creating the SPGDemo Vendors list and adding three items

The new list showed up in Site contents with the three vendors:

SPGDemo Vendors list in SharePoint Online with items created by the REST API

In browser JavaScript, get a digest first and send it with the POST:

const digest = (await (await fetch(site + "/_api/contextinfo", {
  method: "POST", headers: { Accept: "application/json;odata=nometadata" }
})).json()).FormDigestValue;

const res = await fetch(site + "/_api/web/lists/getbytitle('SPGDemo Vendors')/items", {
  method: "POST",
  headers: {
    "Accept": "application/json;odata=nometadata",
    "Content-Type": "application/json;odata=nometadata",
    "X-RequestDigest": digest
  },
  body: JSON.stringify({ Title: "Northwind Traders" })
});
console.log(res.status);   // 201 Created

With odata=verbose, the body also needs the list’s ListItemEntityTypeFullName as the type. Spaces in the list name become _x0020_:

{
  "__metadata": { "type": "SP.Data.SPGDemo_x0020_VendorsListItem" },
  "Title": "Northwind Traders"
}

For bulk loads, adding list items with PnP PowerShell is simpler than a REST loop.

Update and delete list items (MERGE, DELETE and recycle)

To update, send MERGE to the item’s URL. To remove an item but keep it recoverable, call recycle(). Here are both:

$items = "/_api/web/lists/getbytitle('SPGDemo Vendors')/items"
Invoke-PnPSPRestMethod -Method Merge -Url "$items(3)" -Content @{ Title = "Litware Supplies LLC" }

$extra = Invoke-PnPSPRestMethod -Method Post -Url $items -Content @{ Title = "Old Vendor Entry" }
Invoke-PnPSPRestMethod -Method Post -Url "$items($($extra.Id))/recycle()" -Content @{} | Out-Null

$bin = Invoke-PnPSPRestMethod -Url "/_api/web/recyclebin?`$filter=Title eq 'Old Vendor Entry'"
$bin.value | Select-Object Title, ItemType, DeletedDate | Format-Table -AutoSize
SharePoint REST API MERGE updating an item title and recycle() moving an item to the recycle bin

Item 3 now reads Litware Supplies LLC, and Old Vendor Entry sits in the site recycle bin.

Watch out with DELETE. When I sent a plain DELETE to a test item, it never showed up in the recycle bin. If someone might want it back, use recycle(). Restores work as described in the SharePoint recycle bin guide.

The same calls from browser JavaScript use POST plus the method header:

await fetch(site + "/_api/web/lists/getbytitle('SPGDemo Vendors')/items(3)", {
  method: "POST",
  headers: {
    "Accept": "application/json;odata=nometadata",
    "Content-Type": "application/json;odata=nometadata",
    "X-RequestDigest": digest,
    "X-HTTP-Method": "MERGE",
    "IF-MATCH": "*"
  },
  body: JSON.stringify({ Title: "Litware Supplies LLC" })
});   // 204 No Content on success

// delete: same URL, "X-HTTP-Method": "DELETE", no body
// recycle instead: POST .../items(3)/recycle()

IF-MATCH: * overwrites any version. Send the item’s real ETag instead and SharePoint refuses changes made on top of someone else’s edit. A wrong ETag returned this 412:

412 {"odata.error":{"code":"-1, Microsoft.SharePoint.Client.ClientServiceException","message":{"lang":"en-US",
"value":"The request ETag value '\"99\"' does not match the object's ETag value '\"1211e783-...-634ed16f8d4f,5\"'."}}}

Deleting items also leaves gaps in the IDs. If that bothers you, see how to reset the list item ID.

Folders and files with the REST API

Files live in libraries, so I created one first, with template 101:

$lib = Invoke-PnPSPRestMethod -Method Post -Url "/_api/web/lists" -Content @{ Title = "SPGDemo Vendor Files"; BaseTemplate = 101 }
"Library: $($lib.Title)"   # Library: SPGDemo Vendor Files

Then a folder, a small text file, the folder’s file list and the file’s content:

$lib = "/sites/SPGuides/SPGDemo Vendor Files"
$folder = Invoke-PnPSPRestMethod -Method Post -Url "/_api/web/folders" -Content @{ ServerRelativeUrl = "$lib/Insurance" }

$text = "Certificate of insurance received 10/24/2026"
$file = Invoke-PnPSPRestMethod -Method Post -ContentType "text/plain" -Content $text `
    -Url "/_api/web/GetFolderByServerRelativeUrl('$lib/Insurance')/Files/add(url='coi-northwind.txt',overwrite=true)"

$files = Invoke-PnPSPRestMethod -Url "/_api/web/GetFolderByServerRelativeUrl('$lib/Insurance')/Files?`$select=Name,TimeCreated"
$files.value | Format-Table Name, TimeCreated -AutoSize
Invoke-PnPSPRestMethod -Raw -Url "/_api/web/GetFileByServerRelativeUrl('$lib/Insurance/coi-northwind.txt')/`$value"
SharePoint REST API creating a folder, uploading a file with Files/add and reading it back with $value

The library now holds Insurance plus Contracts, a folder I made the same way in an earlier test:

SPGDemo Vendor Files library showing the Contracts and Insurance folders created with the SharePoint REST API

Files/add takes the raw file bytes as the body, and $value returns the content. Microsoft caps a file created this way at 2 GB. Names with % or # need the ResourcePath versions of these endpoints.

To remove a file but keep it recoverable, recycle it:

POST https://spguides.sharepoint.com/sites/SPGuides/_api/web/GetFileByServerRelativeUrl('/sites/SPGuides/SPGDemo Vendor Files/Insurance/coi-northwind.txt')/recycle()
X-RequestDigest: <digest>

For real uploads, uploading files with PnP PowerShell and uploading files with metadata in SPFx wrap these same endpoints.

SharePoint groups and users with the REST API

siteusers lists the users the site knows (its User Information List), and sitegroups lists its groups. Creating a group is a POST with a title:

$group = Invoke-PnPSPRestMethod -Method Post -Url "/_api/web/sitegroups" `
    -Content @{ Title = "SPGDemo Vendor Reviewers"; Description = "REST API demo group" }
"Group $($group.Id): $($group.Title)"   # Group 30: SPGDemo Vendor Reviewers

Adding a member is a POST of the user’s login name to the group’s users collection. Removing one is removebyid() or removeByLoginName():

$group = Invoke-PnPSPRestMethod -Url "/_api/web/sitegroups/getbyname('SPGDemo Vendor Reviewers')"
$login = (Invoke-PnPSPRestMethod -Url "/_api/web/getuserbyid(26)").LoginName

Invoke-PnPSPRestMethod -Method Post -Url "/_api/web/sitegroups($($group.Id))/users" -Content @{ LoginName = $login } | Out-Null
(Invoke-PnPSPRestMethod -Url "/_api/web/sitegroups($($group.Id))/users?`$select=Id,Title").value |
    Format-Table Id, Title -AutoSize

Invoke-PnPSPRestMethod -Method Post -Url "/_api/web/sitegroups($($group.Id))/users/removebyid(26)" -Content @{} | Out-Null
SharePoint REST API adding a user to a SharePoint group, listing members and removing the user

Miller showed up as member 26, and the group was empty again after removebyid(26). New groups get no permissions until you assign a role, so this test changed no access.

One PnP gotcha: a POST with no body, like removebyid(), failed with “Cannot handle the data at position 0” until I added -Content @{}.

To get the current user’s groups, call:

GET https://spguides.sharepoint.com/sites/SPGuides/_api/web/currentuser/groups?$select=Id,Title

It returns only groups the user belongs to directly. I own the site through its Microsoft 365 group, so SPGuides Owners didn’t appear for me. Showing the user’s name instead? See displaying the current user in SPFx.

Call the REST API from SPFx, Power Automate and PowerShell

In an SPFx web part, use SPHttpClient. It handles the sign-in and adds the digest to writes:

const url = `${this.context.pageContext.web.absoluteUrl}/_api/web/lists/getbytitle('SPGDemo Orders')/items?$select=ID,Title&$top=5`;
const response = await this.context.spHttpClient.get(url, SPHttpClient.configurations.v1);
const orders = (await response.json()).value;

I use this pattern in retrieving list items in SPFx. For mail, Teams or OneDrive data, Microsoft Graph in SPFx is the better API.

In Power Automate, the Send an HTTP request to SharePoint action takes the same endpoint as a relative URI:

Site Address: https://spguides.sharepoint.com/sites/SPGuides
Method:       GET
Uri:          _api/web/lists/getbytitle('SPGDemo Orders')/items?$select=ID,Title&$top=5
Headers:      Accept = application/json;odata=nometadata

More flow examples are in REST API calls in Power Automate. In PowerShell, use Invoke-PnPSPRestMethod as shown throughout this post.

SPServices and SendEmail: what’s retired

SPServices was a jQuery library that wrapped SharePoint’s old SOAP web services (the .asmx files) for classic pages. It isn’t maintained, and its Script Editor approach doesn’t run on modern pages.

Every SPServices task has a REST call: GetListItems becomes /items, UpdateListItems becomes POST or MERGE, and GetUserProfileByName becomes /currentuser or the user profile endpoints. Move that code into SPFx with SPHttpClient.

The SP.Utilities.Utility.SendEmail endpoint is also gone. Microsoft retired it on October 31, 2025. Send mail with Microsoft Graph’s sendMail, or the Outlook connector’s Send an email action in Power Automate.

Common SharePoint REST API errors

These are the real error messages our site returned, and what each one means:

  • 403 “The security validation for this page is invalid”: a browser write without a valid X-RequestDigest.
  • “List ‘SPGDemo Vendor’ does not exist at site with URL …”: a typo in getbytitle. Titles must match the display name exactly.
  • “The expression “Customer Name” is not valid.”: you used a display name in $select or $filter. Use the internal name.
  • “The query to field ‘SalesRep’ is not valid … the $expand query string must contains SalesRep.”: a person or lookup field in $select without $expand.
  • 412 ETag mismatch: someone changed the item after you read it. Re-read it, or send IF-MATCH: *.
  • 429 Too Many Requests: SharePoint throttled you. Wait for the Retry-After time, then batch or slow down.

If you’re building on SharePoint data, these guides pick up where this one ends:

Frequently Asked Questions

What is the REST API in SharePoint?

It’s SharePoint’s built-in web service at /_api/. You read and change sites, lists, items, files, users and groups with HTTP requests and get JSON or XML back, from any language that can send HTTP.

Where is the SharePoint REST API documentation?

Microsoft keeps it on Learn, starting with Get to know the SharePoint REST service. It links to the list, file, OData query and batch pages.

How do I get more than 100 or 5,000 items with the SharePoint REST API?

Follow the odata.nextLink in each response. It carries a $skiptoken for the next page. $skip doesn’t work on list items, and Microsoft’s OData query operations page lists the supported options.

Should I use the SharePoint REST API or Microsoft Graph?

Use the SharePoint REST API for SharePoint-only features like site groups, list settings and recycle(). Use Microsoft Graph when you also need mail, Teams or OneDrive, or one API across Microsoft 365.

Do SharePoint REST API calls need X-RequestDigest?

Only writes (POST, MERGE, PUT, DELETE) from a browser session. Calls with an OAuth token, like PnP PowerShell, SPHttpClient or Power Automate, don’t need you to add it.

2 thoughts on “SharePoint REST API: Endpoints, Examples and CRUD (2026 Guide)”

Leave a Comment