Say you’re building an order dashboard, and the sales manager wants only the shipped orders over $400 from a SharePoint list of 210 orders, newest first. The SharePoint REST API can get list items like that in one GET request.
You call /_api/web/lists/getbytitle('List')/items and add $select, $filter, $orderby and $top to the URL. Then you follow the next-page link until every item is in.
In this tutorial, we’ll run the sharepoint rest api get list items requests one by one on a real list: all items, one item by ID, chosen columns, filters on text, numbers and dates, sorting, lookup and person columns with $expand, and paging past the first 100 items.
The Sample List We’ll Use
Every request below runs against one list, SPGDemo Orders, on https://spguides.sharepoint.com/sites/SPGuides. It has 210 orders: ten detailed ones (SO-1001 to SO-1108) and 200 bulk rows. If you’re new to the endpoint itself, start with my SharePoint REST API tutorial.

| Column (display name) | Internal name | Type |
|---|---|---|
| Title | Title | Single line of text (order number) |
| Customer Name | CustomerName | Single line of text |
| Order Total | OrderTotal | Currency |
| Order Date | OrderDate | Date |
| Order Status | OrderStatus | Choice (New, Processing, Shipped, Delivered) |
| Paid | Paid | Yes/No |
| Product | Product | Lookup to SPGDemo Products |
| Sales Rep | SalesRep | Person |
More list walkthroughs live in the SharePoint tutorials section. Now notice the second column. REST URLs always use the internal name, so OrderStatus, not Order Status. You’ll see what happens when you mix them up near the end.
How to Run These Requests
You can paste a GET URL into the browser’s address bar while you’re signed in to SharePoint. That works, but SharePoint answers with XML (Atom), which is hard to read.
For JSON, send an Accept header. I open the site, press F12, and run fetch() in the browser console. The browser sends your sign-in cookie, so no token is needed:
const url = "https://spguides.sharepoint.com/sites/SPGuides/_api/web/lists/getbytitle('SPGDemo Orders')/items?$select=ID,Title&$top=5";
const res = await fetch(url, {
headers: { Accept: "application/json;odata=nometadata" }
});
const data = await res.json();
console.table(data.value);
Here’s what each Accept value gives you:
| Accept header | Where the items are | Next-page link |
|---|---|---|
| application/json;odata=nometadata | value | odata.nextLink |
| application/json;odata=verbose | d.results (with __metadata per item) | d.__next |
I use nometadata in this post because the responses are short. In a web part, the same URLs work with SPHttpClient or PnPjs, as in displaying SharePoint list items in an SPFx web part.
Method 1: Get All List Items
Use this when you want to see what a list returns before you narrow it down. Here’s the request:
GET https://spguides.sharepoint.com/sites/SPGuides/_api/web/lists/getbytitle('SPGDemo Orders')/items
Accept: application/json;odata=nometadata
It returned 100 of the 210 orders, plus every column of each item:

{
"odata.nextLink": "https://spguides.sharepoint.com/sites/SPGuides/_api/web/lists/getbytitle(%27SPGDemo%20Orders%27)/items?%24skiptoken=Paged%3dTRUE%26p_ID%3d100",
"value": [
{
"FileSystemObjectType": 0,
"Id": 1,
"ContentTypeId": "0x01006B9212FFDA6F6144AB32A6B15DB16677",
"Title": "SO-1001",
"CustomerName": "Northwind Traders",
"OrderTotal": null,
"OrderStatus": null,
"SalesRepId": null,
"ProductId": null,
"ID": 1,
"Modified": "2026-10-23T17:32:16Z",
"AuthorId": 11
},
... 99 more items
]
}
How does this work?
The /items endpoint returns the first 100 items by default. That’s a page size, not an error, so it’s easy to miss when your test list is small.
The odata.nextLink at the top is the URL of the next page. Its $skiptoken=Paged=TRUE&p_ID=100 means “continue after item ID 100”. Method 7 follows it to get all 210.
You also get system fields you rarely need, such as ContentTypeId and FileSystemObjectType. Lookup and person columns only show their ID (ProductId, SalesRepId).
Method 2: Get a Single List Item by ID
When you already know the item, put its ID in parentheses after items. I asked for order 4 with a few columns:
GET https://spguides.sharepoint.com/sites/SPGuides/_api/web/lists/getbytitle('SPGDemo Orders')/items(4)?$select=ID,Title,CustomerName,Quantity,OrderTotal,OrderStatus,OrderDate,Paid

{
"Id": 4,
"Title": "SO-1102",
"CustomerName": "Adventure Works",
"Quantity": 10,
"OrderTotal": 1890,
"OrderDate": "2026-01-08T00:00:00Z",
"OrderStatus": "Processing",
"Paid": true,
"ID": 4
}
How does this work?
A single item comes back as one object, not inside value. GetItemById(4) does the same thing; I tested both and got the same fields.
Now look at OrderDate. The list shows 1/7/2026 for this order, but REST returned 2026-01-08T00:00:00Z. REST sends dates in UTC, and the list view converts them to the site’s time zone.
So here’s the catch: if you display REST dates without converting them, some orders will look one day off.
Method 3: Return Only the Columns You Need With $select and $top
Use $select to name the columns and $top to limit the item count. This request asks for four columns and five items:
GET https://spguides.sharepoint.com/sites/SPGuides/_api/web/lists/getbytitle('SPGDemo Orders')/items?$select=ID,Title,CustomerName,OrderTotal&$top=5

{
"odata.nextLink": "https://spguides.sharepoint.com/sites/SPGuides/_api/web/lists/getbytitle(%27SPGDemo%20Orders%27)/items?%24skiptoken=Paged%3dTRUE%26p_ID%3d5&%24select=ID%2cTitle%2cCustomerName%2cOrderTotal&%24top=5",
"value": [
{ "Id": 1, "Title": "SO-1001", "CustomerName": "Northwind Traders", "OrderTotal": null, "ID": 1 },
{ "Id": 2, "Title": "SO-1002", "CustomerName": "Fabrikam Inc.", "OrderTotal": 1499.88, "ID": 2 },
{ "Id": 3, "Title": "SO-1101", "CustomerName": "Woodgrove Bank", "OrderTotal": 239.96, "ID": 3 },
{ "Id": 4, "Title": "SO-1102", "CustomerName": "Adventure Works", "OrderTotal": 1890, "ID": 4 },
{ "Id": 5, "Title": "SO-1103", "CustomerName": "Tailspin Toys", "OrderTotal": 358, "ID": 5 }
]
}
How does this work?
Each item now holds only the four columns, plus Id, which SharePoint always adds. Five items came back, and the next link continues after ID 5 with the same $select and $top.
Empty columns still come back as null, like SO-1001’s order total. Your code should expect that.
Pro Tip: Always use $select in real code. On my list it cut each item from more than 20 properties to 5, and it stops the response from growing every time someone adds a column.
Method 4: Filter List Items With $filter
$filter takes a condition built from internal names, operators and values. Text and choice values go in single quotes; numbers don’t. Here are the shipped orders over $400:
GET https://spguides.sharepoint.com/sites/SPGuides/_api/web/lists/getbytitle('SPGDemo Orders')/items?$select=ID,Title,CustomerName,OrderStatus,OrderTotal&$filter=OrderStatus eq 'Shipped' and OrderTotal gt 400

{
"value": [
{ "Id": 2, "Title": "SO-1002", "CustomerName": "Fabrikam Inc.", "OrderTotal": 1499.88, "OrderStatus": "Shipped", "ID": 2 },
{ "Id": 9, "Title": "SO-1107", "CustomerName": "Litware Inc.", "OrderTotal": 537, "OrderStatus": "Shipped", "ID": 9 }
]
}
How does this work?
Three orders are Shipped, but Tailspin Toys (SO-1103) is only $358, so gt 400 drops it. Fabrikam and Litware are left.
You can join conditions with and and or. These are the operators SharePoint supports:
| Use | Syntax | Example on SPGDemo Orders |
|---|---|---|
| Equal / not equal | eq, ne | Title eq 'SO-1107' (returned ID 9) |
| Compare numbers or dates | lt, le, gt, ge | OrderTotal gt 400 |
| Yes/No column | eq 1 / eq 0 | Paid eq 1 and OrderStatus eq 'Shipped' (IDs 2, 5, 9) |
| Empty value | eq null | OrderTotal eq null |
| Starts with | startswith(Column,’text’) | startswith(CustomerName,'W') (IDs 3, 6) |
| Contains | substringof(‘text’,Column) | substringof('Inc',CustomerName) |
Filtering on Title is also how you get an item by its title or any other field value. The same OData syntax works in Power Automate’s Get items action; see the Power Automate OData filter query examples.
Filter by text (contains) and by date
For “contains”, the argument order is reversed: the text comes first, then the column. This finds every customer name containing “Inc”:
GET https://spguides.sharepoint.com/sites/SPGuides/_api/web/lists/getbytitle('SPGDemo Orders')/items?$select=ID,Title,CustomerName&$filter=substringof('Inc',CustomerName)

{
"value": [
{ "Id": 2, "Title": "SO-1002", "CustomerName": "Fabrikam Inc.", "ID": 2 },
{ "Id": 9, "Title": "SO-1107", "CustomerName": "Litware Inc.", "ID": 9 },
{ "Id": 10, "Title": "SO-1108", "CustomerName": "Proseware Inc.", "ID": 10 }
]
}
Don’t reach for endswith, tolower or concat. I tried endswith(CustomerName,'Inc.') and got a 400 error: “The function operator ‘endswith’ is not supported or its usage is invalid.”
For dates, wrap an ISO 8601 UTC value in datetime'...'. These are the orders from January 20, 2026 on, oldest first:
GET https://spguides.sharepoint.com/sites/SPGuides/_api/web/lists/getbytitle('SPGDemo Orders')/items?$select=ID,Title,OrderDate&$filter=OrderDate ge datetime'2026-01-20T00:00:00Z'&$orderby=OrderDate

{
"value": [
{ "Id": 8, "Title": "SO-1106", "OrderDate": "2026-01-22T00:00:00Z", "ID": 8 },
{ "Id": 9, "Title": "SO-1107", "OrderDate": "2026-01-26T00:00:00Z", "ID": 9 },
{ "Id": 2, "Title": "SO-1002", "OrderDate": "2026-01-28T00:00:00Z", "ID": 2 },
{ "Id": 10, "Title": "SO-1108", "OrderDate": "2026-01-28T00:00:00Z", "ID": 10 }
]
}
Four orders matched. Because the stored values are UTC, build the boundaries in UTC too. For a from-to range in a flow, see getting list items by created date range in Power Automate.
Method 5: Sort List Items With $orderby
Add $orderby=Column desc for descending order, or asc (the default) for ascending. Separate two columns with a comma. This returns the four largest orders:
GET https://spguides.sharepoint.com/sites/SPGuides/_api/web/lists/getbytitle('SPGDemo Orders')/items?$select=ID,Title,CustomerName,OrderTotal&$orderby=OrderTotal desc,Title asc&$top=4

{
"odata.nextLink": "https://spguides.sharepoint.com/sites/SPGuides/_api/web/lists/getbytitle(%27SPGDemo%20Orders%27)/items?%24skiptoken=Paged%3dTRUE%26p_OrderTotal%3d479%252e920000000000%26p_Title%3dSO%252d1106%26p_ID%3d8&%24select=ID%2cTitle%2cCustomerName%2cOrderTotal&%24orderby=OrderTotal+desc%2cTitle+asc&%24top=4",
"value": [
{ "Id": 4, "Title": "SO-1102", "CustomerName": "Adventure Works", "OrderTotal": 1890, "ID": 4 },
{ "Id": 2, "Title": "SO-1002", "CustomerName": "Fabrikam Inc.", "OrderTotal": 1499.88, "ID": 2 },
{ "Id": 9, "Title": "SO-1107", "CustomerName": "Litware Inc.", "OrderTotal": 537, "ID": 9 },
{ "Id": 8, "Title": "SO-1106", "CustomerName": "Coho Winery", "OrderTotal": 479.92, "ID": 8 }
]
}
How does this work?
The results run from $1,890 down to $479.92. Title asc only matters when two orders share a total.
Look at the next link: its $skiptoken now carries p_OrderTotal, p_Title and p_ID. SharePoint remembers the sort values of the last row, which is why you should follow the link as-is rather than build the token yourself.
Sorting by Modified desc works the same way and is the quickest way to get the most recently changed items.
Method 6: Get Lookup and Person Columns With $expand
Lookup and person columns return only an ID until you expand them (the Product column here is a lookup like the one in creating a SharePoint lookup column).
Name the sub-fields in $select and the column in $expand. Here I get the product and sales rep for Monitor Arm orders:
GET https://spguides.sharepoint.com/sites/SPGuides/_api/web/lists/getbytitle('SPGDemo Orders')/items?$select=ID,Title,Product/Title,SalesRep/Title,SalesRep/Id&$expand=Product,SalesRep&$filter=Product/Title eq 'Monitor Arm' and ID le 10

{
"value": [
{
"SalesRep": { "Title": "Miller", "Id": 26 },
"Product": { "Title": "Monitor Arm" },
"Id": 5,
"Title": "SO-1103",
"ID": 5
},
{
"SalesRep": { "Title": "Miller", "Id": 26 },
"Product": { "Title": "Monitor Arm" },
"Id": 9,
"Title": "SO-1107",
"ID": 9
}
]
}
How does this work?
Each item now has a nested Product and SalesRep object. Product/Title is the Title of the item in the SPGDemo Products list, and SalesRep/Title is the person’s display name.
You can also ask for SalesRep/EMail or other user fields the same way. Keep the column in both places: the sub-field in $select, the column name in $expand.
To filter by the lookup’s ID, you don’t need $expand at all: $filter=ProductId eq 3 returned the USB-C Dock orders. People are filtered by display name, for example SalesRep/Title eq 'Miller' with $expand=SalesRep.
Two limits from Microsoft: multi-value lookup and person columns can’t be filtered, and there’s no “current user” operator in REST filters. For the current user, read their ID first, as in getting the current user in SPFx.
Method 7: Get All Items With Paging (__next and $skiptoken)
This is the part the old jQuery samples skip. A list of 210 items needs three requests at 100 items per page. With odata=verbose, the next-page URL is in d.__next:
GET https://spguides.sharepoint.com/sites/SPGuides/_api/web/lists/getbytitle('SPGDemo Orders')/items?$select=ID,Title&$top=100
Accept: application/json;odata=verbose

{
"d": {
"results": [
{
"__metadata": {
"id": "ba72cd73-a730-42ba-b38e-9381e39ea2b9",
"uri": "https://spguides.sharepoint.com/sites/SPGuides/_api/Web/Lists(guid'01cea0a6-d7b5-419f-a0cc-99c3e10df216')/Items(1)",
"etag": "\"1\"",
"type": "SP.Data.SPGDemo_x0020_OrdersListItem"
},
"Id": 1,
"Title": "SO-1001",
"ID": 1
},
... 99 more items
],
"__next": "https://spguides.sharepoint.com/sites/SPGuides/_api/web/lists/getbytitle(%27SPGDemo%20Orders%27)/items?%24skiptoken=Paged%3dTRUE%26p_ID%3d100&%24select=ID%2cTitle&%24top=100"
}
}
With nometadata, the same URL is in odata.nextLink. This function keeps calling it until there’s no next page. I ran it on the list:
async function getAllItems(siteUrl, listTitle, query) {
let url = `${siteUrl}/_api/web/lists/getbytitle('${listTitle}')/items?${query}`;
const items = [];
let page = 0;
while (url) {
const res = await fetch(url, { headers: { Accept: "application/json;odata=nometadata" } });
if (!res.ok) throw new Error(`HTTP ${res.status}: ${await res.text()}`);
const data = await res.json();
items.push(...data.value);
page++;
console.log(`Page ${page}: ${data.value.length} items`);
url = data["odata.nextLink"]; // undefined on the last page
}
return items;
}
const all = await getAllItems("https://spguides.sharepoint.com/sites/SPGuides", "SPGDemo Orders", "$select=ID,Title,OrderStatus&$top=100");
console.log(`Total: ${all.length} items`);
Console output:
Page 1: 100 items
Page 2: 100 items
Page 3: 10 items
Total: 210 items
How does this work?
Each response holds one page in value. The last page has no odata.nextLink, so url becomes undefined and the loop ends. On my list, the three calls took about half a second.
$top sets the page size. Don’t try $skip for paging: $top=3&$skip=50 still gave me items 1, 2 and 3, because SharePoint ignores $skip on list items.
Paging is by position, so items deleted between two calls can shift the next page. If you page in a flow instead, here’s REST API pagination in Power Automate.
What About Lists Over 5,000 Items?
SharePoint Online’s list view threshold is 5,000 items. Paging by ID like Method 7 keeps working on big lists, because each call reads one small page in ID order.
Filters and sorts are different. On a list over 5,000 items, filter and sort on indexed columns and make the first condition narrow the result below 5,000. Otherwise you’ll hit the list view threshold error.
In my test, $top=6000 on the 210-item list simply returned all 210 with no next link. On large lists, I keep pages at 5,000 or fewer and follow the next link. To load test data, I used PnP PowerShell to add more than 5,000 items.
Pro Tip: When a view or filter needs more than plain OData can do (complex lookups, managed metadata), Microsoft’s RenderListDataAsStream POST endpoint returns list data the way list views see it.
Which Method Should You Use?
| You need | Use |
|---|---|
| Quick look at a list | /items (first 100) |
| One known item | /items(ID) or GetItemById(ID) |
| Smaller, faster responses | $select + $top |
| Items matching a condition | $filter (eq, gt, startswith, substringof, datetime) |
| Newest, largest, A-Z | $orderby with asc/desc |
| Lookup or person values | $select=Col/Title + $expand=Col |
| Every item in the list | Follow odata.nextLink or __next |
Things to Keep in Mind
Most failed requests I see come down to a column name. A wrong internal name returns a 500 error like this:
GET https://spguides.sharepoint.com/sites/SPGuides/_api/web/lists/getbytitle('SPGDemo Orders')/items?$select=ID,Title&$filter=Status eq 'New'

{
"odata.error": {
"code": "-2146232832, Microsoft.SharePoint.SPException",
"message": {
"lang": "en-US",
"value": "Column 'Status' does not exist. It may have been deleted by another user."
}
}
}
- Use internal names.
Statusdoesn’t exist (the column isOrderStatus), and the display nameOrder Status eq 'New'fails with 400 “The expression … is not valid.” Find internal names in the column settings URL (Field=). - Expect 100 items. No
$topmeans one page of 100. Follow the next link for the rest. - Dates are UTC. Convert before you display them and filter with UTC boundaries.
- Classic script won’t run on modern pages. The old Script Editor + jQuery samples need custom script, which is blocked by default. Use SPFx or call the API from a flow or app.
For full create, update and delete examples on the same endpoint, my SPFx CRUD operations guide picks up where this post stops.
If you’re working with SharePoint lists from code, these are worth a look next:
Frequently Asked Questions
How do I get all items from a SharePoint list with the REST API?
Call /_api/web/lists/getbytitle(‘List’)/items. It returns the first 100 items and an odata.nextLink (or d.__next in verbose mode). Keep requesting that link until it’s missing. On my 210-item list, that took three calls.
How do I get a SharePoint list item by ID with REST?
Use /_api/web/lists/getbytitle(‘List’)/items(4) or /GetItemById(4), and add $select for the columns you need. A single item comes back as one object instead of a value array.
Why does the SharePoint REST API return only 100 items?
100 is the default page size for list items. Set $top for a different page size and follow the next-page link. $skip doesn’t work for list items; use the $skiptoken in the next link instead.
How do I filter by a lookup or person column in SharePoint REST?
Put the sub-field in $select and the column in $expand, then filter on it, for example $filter=Product/Title eq ‘Monitor Arm’. People are matched by display name. Microsoft lists the supported operators in its OData query operations guide.
Can I use endswith or tolower in a SharePoint REST filter?
No. SharePoint supports eq, ne, lt, le, gt, ge, startswith and substringof, plus date parts like year() and month(). endswith returned a 400 error in my test.

Hey! I’m Bijay Kumar, founder of SPGuides.com and a Microsoft Business Applications MVP (Power Automate, Power Apps). I launched this site in 2020 because I truly enjoy working with SharePoint, Power Platform, and SharePoint Framework (SPFx), and wanted to share that passion through step-by-step tutorials, guides, and training videos. My mission is to help you learn these technologies so you can utilize SharePoint, enhance productivity, and potentially build business solutions along the way.