SharePoint Online Search REST API (Search Service) — An Exhaustive Guide with Practical Examples
SharePoint Online exposes a Search REST service that lets you run keyword queries (KQL), request selected managed properties, paginate results, apply refiners, and retrieve suggestions — all through standard REST calls. You pass query parameters either in the URL (GET) or in the request body (POST). (Microsoft Learn)
This article focuses on the SharePoint Search REST API endpoints:
GET https://contoso.sharepoint.com/sites/ExampleSite/_api/search/query?...POST https://contoso.sharepoint.com/sites/ExampleSite/_api/search/postqueryGET https://contoso.sharepoint.com/sites/ExampleSite/_api/search/suggest?...(Microsoft Learn)
1) When to use Search REST vs “List APIs” vs Microsoft Graph
Use Search REST when you need
- Relevance-ranked results (best matches first)
- Full-text search inside documents and pages
- Cross-library / cross-list discovery (within what the caller can access)
- Refiners (facets like fileExtension, author, content type, custom managed properties)
- “Google-like” experiences (query suggestions, query rules, ranking)
Prefer list/library REST (or Microsoft Graph list APIs) when you need
- Exact item retrieval from a known list/library
- CRUD operations (Search is primarily for discovery; not a CRUD API)
2) Core concepts you must know (KQL, managed properties, refiners, FQL)
KQL (Keyword Query Language)
Your main query goes into querytext='...' and is typically KQL (e.g., FileExtension:pdf AND budget). Search REST builds the query using these parameters. (Microsoft Learn)
Managed properties
Search results return managed properties (e.g., Title, Path, Author, FileExtension). If you need custom metadata, it must be mapped to a managed property in the search schema (tenant/site level).
Refiners vs refinement filters
- Refiners: “tell me which facets to calculate” (like
fileExtension,author, etc.) - RefinementFilters: “apply a filter using FQL syntax”
Microsoft documentation calls out that RefinementFilters uses FQL (different from KQL), and shows examples like filtering by file extension. (Microsoft Learn)
3) Endpoints overview
A) Query (GET)
Best for quick tests and simple queries.
GET https://contoso.sharepoint.com/sites/ExampleSite/_api/search/query?querytext='sharepoint'Accept: application/json;odata=nometadata
You specify query parameters in the URL for GET requests. (Microsoft Learn)
B) PostQuery (POST)
Best for:
- long query parameters
- cleaner payload
- arrays (like multiple refinement filters)
Search REST supports POST requests where parameters are passed as JSON. (Microsoft Learn)
C) Suggest
For query suggestions (pre-query / post-query suggestions), use the suggest endpoint. (Microsoft Learn)
4) The response shape (how to parse results)
Search REST returns a structure like:
PrimaryQueryResultRelevantResultsTableRows[]Cells[](each cell hasKeyandValue)
So you usually parse: Rows → Cells → (Key, Value) to build your result objects.
5) The “minimum useful query” (Title + Path + pagination)
Example: PDFs containing “handover”, return 10 results, skip first 20
GET https://contoso.sharepoint.com/sites/ExampleSite/_api/search/query ?querytext='handover AND FileExtension:pdf' &selectproperties='Title,Path,FileExtension,Size,Author,LastModifiedTime' &rowlimit=10 &startrow=20 &trimduplicates=trueAccept: application/json;odata=nometadata
Notes
selectpropertieskeeps payload small and predictable.rowlimitandstartrowgive you paging.trimduplicates=trueremoves near-duplicate items (often useful in enterprise content).
6) Common query patterns (copy/paste ready)
A) Search inside a specific site (Path scoping)
querytext='(security OR compliance) AND Path:https://contoso.sharepoint.com/sites/ExampleSite/'
B) Only documents (exclude folders)
A common approach is to use:
querytext='IsDocument:true AND -ContentType:Folder'
C) Only a library / folder subtree
querytext='Path:https://contoso.sharepoint.com/sites/ExampleSite/Shared%20Documents/Policies/'
D) Exact filename or partial title
querytext='Title:"Q4 Report" OR Filename:Q4*'
E) Sort by last modified
Use sortlist (format is ManagedProperty:direction):
...&sortlist='LastModifiedTime:descending'
7) Refiners (facets) + RefinementFilters (FQL) — real-world example
Goal
Search for “invoice”, but show refiners for file extension and author, and filter to PDF/DOCX.
Step 1 — Ask Search to calculate refiners
GET https://contoso.sharepoint.com/sites/ExampleSite/_api/search/query ?querytext='invoice' &selectproperties='Title,Path,FileExtension,Author,LastModifiedTime' &refiners='fileExtension,Author' &rowlimit=20Accept: application/json;odata=nometadata
Step 2 — Apply refinement filters (FQL)
Microsoft’s docs show refinement filters like:refinementfilters='fileExtension:equals("docx")' (Microsoft Learn)
Example filtering to PDF or DOCX:
GET https://contoso.sharepoint.com/sites/ExampleSite/_api/search/query ?querytext='invoice' &selectproperties='Title,Path,FileExtension,Author,LastModifiedTime' &refinementfilters='or(fileExtension:equals("pdf"),fileExtension:equals("docx"))' &rowlimit=20Accept: application/json;odata=nometadata
Key point: refinementfilters is FQL, not plain KQL. (Microsoft Learn)
8) POST example (recommended for complex queries)
POST https://contoso.sharepoint.com/sites/ExampleSite/_api/search/postqueryAccept: application/json;odata=nometadataContent-Type: application/json;odata=nometadata{ "request": { "Querytext": "invoice AND Path:https://contoso.sharepoint.com/sites/ExampleSite/", "RowLimit": 25, "StartRow": 0, "TrimDuplicates": true, "SelectProperties": { "results": ["Title","Path","FileExtension","Author","LastModifiedTime"] }, "Refiners": "fileExtension,Author", "RefinementFilters": { "results": ["or(fileExtension:equals(\"pdf\"),fileExtension:equals(\"docx\"))"] } }}
POST lets you pass parameters in JSON instead of URL query strings. (Microsoft Learn)
9) Query Suggestions (typeahead)
Example: Get suggestions for what the user typed
GET https://contoso.sharepoint.com/sites/ExampleSite/_api/search/suggest ?querytext='inv' &inumberofquerysuggestions=5 &inumberofresultsuggestions=5Accept: application/json;odata=nometadata
Microsoft documents how to retrieve query suggestions using the Search REST service. (Microsoft Learn)
10) SPFx example (using SPHttpClient, as you prefer)
Below is a minimal, production-style example that:
- takes a user query string
- calls Search REST
- parses rows/cells into objects
A) Helper: build the URL safely
private buildSearchUrl(siteUrl: string, userQuery: string, startRow: number, rowLimit: number): string { const querytext = encodeURIComponent(`'${userQuery}'`); const select = encodeURIComponent(`'Title,Path,FileExtension,LastModifiedTime,Author'`); const trim = `true`; return `${siteUrl}/_api/search/query` + `?querytext=${querytext}` + `&selectproperties=${select}` + `&rowlimit=${rowLimit}` + `&startrow=${startRow}` + `&trimduplicates=${trim}`;}
B) Call Search REST with SPHttpClient
import { SPHttpClient, SPHttpClientResponse } from '@microsoft/sp-http';public async searchDocuments(userQuery: string, startRow: number = 0, rowLimit: number = 20): Promise<any[]> { const siteUrl = this.context.pageContext.web.absoluteUrl; const url = this.buildSearchUrl(siteUrl, userQuery, startRow, rowLimit); const res: SPHttpClientResponse = await this.context.spHttpClient.get( url, SPHttpClient.configurations.v1, { headers: { 'Accept': 'application/json;odata=nometadata' } } ); if (!res.ok) { const text = await res.text(); throw new Error(`Search failed (${res.status}): ${text}`); } const json = await res.json(); // Navigate to Rows/Cells const rows = json?.PrimaryQueryResult?.RelevantResults?.Table?.Rows || []; // Convert each row's Cells[] into a flat object: { Title: ..., Path: ... } return rows.map((r: any) => { const obj: any = {}; (r.Cells || []).forEach((c: any) => { obj[c.Key] = c.Value; }); return obj; });}
C) Simple paging approach
- Page 1:
startRow=0 - Page 2:
startRow=rowLimit - Page 3:
startRow=rowLimit*2
That’s all Search needs for pagination.
11) C# HttpClient example (app-only or delegated token)
This is the “raw REST” way (works in console apps, services, etc.). You just need a valid SharePoint access token.
using System;using System.Collections.Generic;using System.Net.Http;using System.Net.Http.Headers;using System.Text.Encodings.Web;using System.Text.Json;using System.Threading.Tasks;public static class SharePointSearchClient{ public static async Task<List<Dictionary<string, string>>> QueryAsync( string siteUrl, string accessToken, string kql, int startRow = 0, int rowLimit = 20) { var querytext = Uri.EscapeDataString($"'{kql}'"); var select = Uri.EscapeDataString("'Title,Path,FileExtension,LastModifiedTime,Author'"); var url = $"{siteUrl}/_api/search/query" + $"?querytext={querytext}" + $"&selectproperties={select}" + $"&rowlimit={rowLimit}" + $"&startrow={startRow}" + $"&trimduplicates=true"; using (var http = new HttpClient()) { http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken); http.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); http.DefaultRequestHeaders.Add("Accept", "application/json;odata=nometadata"); var resp = await http.GetAsync(url); var body = await resp.Content.ReadAsStringAsync(); if (!resp.IsSuccessStatusCode) throw new Exception($"Search failed ({(int)resp.StatusCode}): {body}"); using (var doc = JsonDocument.Parse(body)) { var root = doc.RootElement; var rows = root .GetProperty("PrimaryQueryResult") .GetProperty("RelevantResults") .GetProperty("Table") .GetProperty("Rows"); var results = new List<Dictionary<string, string>>(); foreach (var row in rows.EnumerateArray()) { var item = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); foreach (var cell in row.GetProperty("Cells").EnumerateArray()) { var key = cell.GetProperty("Key").GetString() ?? ""; var val = cell.GetProperty("Value").GetString() ?? ""; item[key] = val; } results.Add(item); } return results; } } }}
12) Throttling and reliability (HTTP 429)
SharePoint Online can throttle search requests and return HTTP 429. Microsoft’s guidance is to pause requests and back off, because continuing to send requests extends throttling time. (Microsoft Learn)
One notable detail from Microsoft guidance: some app-only search traffic is throttled at 25 requests/second and will return 429 when exceeded. (Microsoft Learn)
Practical backoff rule
- If you get 429:
- respect
Retry-Afterif present - otherwise exponential backoff: 2s, 4s, 8s, 16s…
- respect
13) Common “gotchas” (the stuff that breaks projects)
- URL encoding
- Always encode KQL properly (spaces, quotes, parentheses).
- For GET, your querytext is wrapped in
'...', so encode after you build it.
- Managed property not retrievable
- Some properties won’t come back unless they’re marked retrievable in the search schema.
- People search vs content search
- Content search uses managed properties; user profile search has its own properties and patterns.
- Security trimming
- Search only returns what the caller is allowed to see.
Summary Tables
A) Implementation Steps (practical checklist)
| Step | What you do | Outcome |
|---|---|---|
| 1 | Decide scope (site vs tenant vs library path) | Avoid noisy results |
| 2 | Write KQL for querytext | Accurate matching |
| 3 | Pick selectproperties | Small + predictable payload |
| 4 | Add paging (rowlimit, startrow) | Stable pagination |
| 5 | Add refiners (refiners) | Faceted navigation |
| 6 | Apply refinementfilters (FQL) | Real filtering (PDF/DOCX/etc.) |
| 7 | Implement 429 backoff | Reliable at scale |
B) Technical Cheat Sheet (most-used parameters)
| Parameter | Purpose | Example |
|---|---|---|
querytext | Main KQL query | querytext='budget AND FileExtension:pdf' |
selectproperties | Which managed props return | 'Title,Path,Author,LastModifiedTime' |
rowlimit | Page size | rowlimit=20 |
startrow | Offset | startrow=40 |
trimduplicates | Remove near-duplicates | trimduplicates=true |
sortlist | Sort results | 'LastModifiedTime:descending' |
refiners | Which facets to compute | refiners='fileExtension,Author' |
refinementfilters | Apply filters (FQL) | fileExtension:equals("pdf") (Microsoft Learn) |
/_api/search/suggest | Suggestions endpoint | Typeahead UX (Microsoft Learn) |
