← 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)


Key Takeaways

  • A CAML query is XML that filters, sorts, and limits SharePoint list items server-side, before the data reaches your code.

  • CAML is not deprecated. It still covers three things OData REST filters cannot: ("items assigned to me"), for calendar recurrence, and multi-list .

  • Every comparison in needs the column's internal name, not its display name — the single most common cause of a query that silently returns zero rows.

  • SharePoint Online throttles any unindexed query past the 5,000-item list view threshold with SPQueryThrottledException — filter on an indexed column and set to avoid it.

  • Skip hand-authoring the nested / XML — the CAML Query Builder generates it from a flat condition list.

What Is a CAML Query?

A CAML query is an XML fragment — , , inside a — that tells SharePoint how to filter, sort, and cap the items it returns from a list. It is evaluated server-side, before any data reaches your code. CAML stands for Collaborative Application Markup Language; its full element and attribute reference lives in Microsoft's query schema documentation. 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.

  • 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 (Microsoft Learn: items exceed the list view threshold). A query whose filter is not backed by an index and would scan more than 5,000 rows gets blocked with SPQueryThrottledException. 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. If you hit SPQueryThrottledException mid-migration or in a scheduled job, cross-check the exact error text against the error decoder before assuming it's a permissions issue.

---

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.

---

FAQ

What does CAML stand for?

CAML stands for Collaborative Application Markup Language — an XML dialect SharePoint uses to define list views and ad hoc queries. It predates the REST API and Microsoft Graph but is still the schema every list view compiles down to under the hood.

Is CAML query deprecated?

No. Microsoft has not deprecated CAML, and it remains the only way to express certain filters — , , and multi-list — that OData REST filters can't reproduce. PnP JS's getItemsByCAMLQuery is actively maintained specifically to run it from modern SPFx code.

CAML query vs REST filters — which should I use?

Use REST's $filter/$select/$expand OData syntax for straightforward single-field filters — it's simpler to write and debug. Reach for CAML when you need lookup-ID filtering, calendar date-range overlap, membership checks, or cross-list joins that OData can't express.

Can I run a CAML query from Power Automate?

Not directly — Power Automate's SharePoint connector uses OData $filter syntax, not CAML. To run CAML from an automated flow, call the SharePoint REST GetItems endpoint (the raw-REST pattern shown above) from an HTTP action instead of the built-in "Get items" action.

Why does my CAML query return 0 items with no error?

The overwhelmingly common cause is a that uses the column's display name instead of its internal name — SharePoint does not error on this, it just matches nothing. Verify the internal name in the column's list-settings URL (Field=...) before checking anything else.

---

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.