← Back to Blog

CAML Query for SharePoint Lists: The Developer Guide (2026)

A CAML query is XML that filters and sorts SharePoint list items server-side. Syntax, Where clauses, RowLimit, and running CAML from SPFx — 2026 guide.

CAML Query for SharePoint Lists: The Developer Guide (2026)


What Is a CAML Query?

A CAML query is an XML fragment that tells SharePoint how to filter, sort, and limit the items it returns from a list — evaluated server-side, before the data ever reaches your code. CAML stands for Collaborative Application Markup Language, and its query dialect is documented in the SharePoint query schema. A minimal query that returns active items, newest first, looks like this:

<View>
<Query>
<Where>
<Eq>
<FieldRef Name='Status' />
<Value Type='Text'>Active</Value>
</Eq>
</Where>
<OrderBy>
<FieldRef Name='Created' Ascending='FALSE' />
</OrderBy>
</Query>
<RowLimit>50</RowLimit>
</View>

Three things matter in that snippet, and they handle the vast majority of real queries:

  • holds your filter logic.

  • sorts the result set.

  • caps how many rows come back per page.

If hand-authoring this XML feels brittle — it is — build it interactively with the CAML Query Builder tool and copy the output into your code. The rest of this guide explains the syntax so you can read, debug, and tune what the builder produces.

---

Prerequisites

To run the SPFx examples below you need:

  • An SPFx project on a currently supported version, set up to call SharePoint (the web part context provides the auth token).

  • PnP JS v3 or later — the examples use the modern spfi/SPFx factory API. On PnP JS v2 the import paths and setup differ.

  • At least Read permission on the target list for the executing user; CAML respects list-level permissions like any other query.

  • A column's internal name for every field you filter on (not its display name — more on this below).

If you are not in SPFx, the raw REST approach at the end of this guide works from any client that can authenticate to SharePoint.

---

Why CAML Still Matters in 2026

You can query SharePoint lists several ways — the REST API, Microsoft Graph, and CAML — so why learn a 2000s-era XML dialect?

Because CAML still earns its place in three spots:

  • SPFx web parts using PnP JS. The getItemsByCAMLQuery method runs complex, multi-field filters against a list with one call.

  • Queries OData cannot express. Lookup-column filtering by ID, for "items assigned to me", for calendar recurrence, and across lists are CAML query-schema elements with no OData equivalent (Microsoft Learn: CAML query schema).

  • View definitions. Every SharePoint list view is a CAML under the hood. Reading CAML is reading how views actually work.

For a comparison of when to reach for OData REST filters instead, the patterns in the Microsoft Graph OData cheat sheet carry over to SharePoint REST.

---

The Clause: Comparison Operators

Every filter lives inside , and every comparison follows the same shape: an operator wrapping a (the internal column name) and a (with an explicit Type). The Where element reference lists the full operator set; these are the ones you will actually type:

OperatorMeaningExample use
equalsStatus = 'Active'
not equalStatus != 'Closed'
/ greater than / or equalPriority >= 2
/ less than / or equalCreated < a date
string prefixtitle starts with "Q1"
substringtitle contains "report"
/ empty / not emptyno assignee set
value in a setstatus in (A, B, C)

A example:

<Where>
<Contains>
<FieldRef Name='Title' />
<Value Type='Text'>report</Value>
</Contains>
</Where>

The single most common mistake: using the column's display name instead of its internal name in . A column titled "Due Date" usually has the internal name Due_x0020_Date. Get the internal name wrong and the query silently returns nothing — no error. Check the internal name in list settings (it is in the column's settings URL as Field=) before you debug anything else.

---

Combining Conditions with and

CAML has no flat AND/OR list. Logical operators are binary — each or takes exactly two child elements (Microsoft Learn: And element). To combine three conditions you nest:

<Where>
<And>
<Eq>
<FieldRef Name='Status' />
<Value Type='Text'>Active</Value>
</Eq>
<Or>
<Eq>
<FieldRef Name='Priority' />
<Value Type='Number'>1</Value>
</Eq>
<Eq>
<FieldRef Name='Priority' />
<Value Type='Number'>2</Value>
</Eq>
</Or>
</And>
</Where>

That reads as Status = 'Active' AND (Priority = 1 OR Priority = 2). The nesting is the part everyone gets wrong by hand — a missing wrapper around three conditions is a syntax error. This is exactly the boilerplate the CAML Query Builder eliminates: you add conditions in a flat list and it nests the binary operators correctly.

---

Dates: Use ISO and

Date values must be ISO 8601, and you almost always want IncludeTimeValue='TRUE' for precise comparisons:

<Where>
<Geq>
<FieldRef Name='Created' />
<Value Type='DateTime' IncludeTimeValue='TRUE'>2026-01-01T00:00:00Z</Value>
</Geq>
</Where>

For relative dates, CAML provides with an optional OffsetDays:

<Where>
<Geq>
<FieldRef Name='DueDate' />
<Value Type='DateTime'>
<Today OffsetDays='-7' />
</Value>
</Geq>
</Where>

That returns items due within the last seven days — without your code computing a date string.

---

and the 5,000-Item Threshold

Always set . SharePoint Online enforces a list view threshold of 5,000 items — a query whose filter is not backed by an index and that would scan more than 5,000 rows is blocked with SPQueryThrottledException (Microsoft Learn: items exceed the list view threshold). The fix is to filter on an indexed column and cap rows with . Pair it with Paged='TRUE' to page through big lists:

<View>
<Query>
<OrderBy><FieldRef Name='ID' Ascending='TRUE' /></OrderBy>
</Query>
<RowLimit Paged='TRUE'>100</RowLimit>
</View>

The response includes a ListItemCollectionPositionNext token; feed it back on the next request to get the following page (Microsoft Learn: RowLimit element). PnP JS handles this token for you.

---

Running CAML from SPFx with PnP JS

In an SPFx web part, run CAML with PnP JS getItemsByCAMLQuery:

// src/webparts/tasks/services/TaskService.ts
import { spfi, SPFx } from "@pnp/sp";
import "@pnp/sp/webs";
import "@pnp/sp/lists";
import "@pnp/sp/items";

const sp = spfi().using(SPFx(this.context));

const camlViewXml =
<View>
<Query>
<Where>
<Eq>
<FieldRef Name='Status' />
<Value Type='Text'>Active</Value>
</Eq>
</Where>
</Query>
<RowLimit>50</RowLimit>
</View>
;

const items = await sp.web.lists
.getByTitle("Tasks")
.getItemsByCAMLQuery({ ViewXml: camlViewXml });

For the full PnP JS setup — spfi, the SPFx behavior, CRUD, and error handling — see PnP JS in SPFx: Read and Write SharePoint Data.

---

Running CAML over Raw REST

If you are not using PnP JS, SharePoint exposes REST endpoints for working with list data (Microsoft Learn: SharePoint REST endpoints). Build the URL with the REST API Builder, then POST the CAML to the list's GetItems endpoint:

POST /_api/web/lists/getbytitle('Tasks')/GetItems
Content-Type: application/json;odata=verbose
{
"query": {
"__metadata": { "type": "SP.CamlQuery" },
"ViewXml": "<View><Query><Where><Eq><FieldRef Name='Status' /><Value Type='Text'>Active</Value></Eq></Where></Query><RowLimit>50</RowLimit></View>"
}
}

The query needs read access to the list — if you get a 403, check the user's permissions against the SharePoint Online permissions guide.

---

Common Gotchas

The query returns 0 items, but you can see matching items in the list.

Cause: wrong internal field name in , or a that does not match the column type. Fix: confirm the internal name (not the display name) and that Type matches (Text, Number, DateTime, Lookup, User).

Microsoft.SharePoint.SPQueryThrottledException

Cause: the query scanned more items than the 5,000-item list view threshold allows without an indexed filter column. Fix: filter on an indexed column first, add , and consider Paged='TRUE'.

0 items returned despite matching data — lookup or person column filter

Cause: lookup and person columns filter by ID, not display text. Fix: use / with the integer ID, or set LookupId='TRUE' on the .

SPQueryThrottledException on a sorted query

Cause: on an unindexed column on a large list also throttles. Fix: index the sort column too, not just the filter column.

The server cannot complete your request — malformed XML

Cause: double quotes inside ViewXml. CAML uses single quotes for attribute values; when you embed ViewXml in JSON, keep the single quotes. Fix: use single quotes throughout the CAML, and escape only what JSON itself requires.

---

What's Next

Use and an indexed filter column on anything beyond a few hundred items — that single habit prevents most CAML query failures in production. From there:




Free Developer Tool

CAML Query Builder

Build complex CAML queries visually — no more hand-writing XML. Add nested AND/OR conditions, lookup filters, and get clean output instantly.

Try It Free →

We use cookies for analytics (and ads if/when AdSense is enabled). By accepting, you allow these uses. See our Privacy Policy and Cookie Policy.