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/postquery
  • GET 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:

  • PrimaryQueryResult
    • RelevantResults
      • Table
        • Rows[]
          • Cells[] (each cell has Key and Value)

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=true
Accept: application/json;odata=nometadata

Notes

  • selectproperties keeps payload small and predictable.
  • rowlimit and startrow give you paging.
  • trimduplicates=true removes 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=20
Accept: 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=20
Accept: 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/postquery
Accept: application/json;odata=nometadata
Content-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=5
Accept: 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-After if present
    • otherwise exponential backoff: 2s, 4s, 8s, 16s…

13) Common “gotchas” (the stuff that breaks projects)

  1. URL encoding
  • Always encode KQL properly (spaces, quotes, parentheses).
  • For GET, your querytext is wrapped in '...', so encode after you build it.
  1. Managed property not retrievable
  • Some properties won’t come back unless they’re marked retrievable in the search schema.
  1. People search vs content search
  • Content search uses managed properties; user profile search has its own properties and patterns.
  1. Security trimming
  • Search only returns what the caller is allowed to see.

Summary Tables

A) Implementation Steps (practical checklist)

StepWhat you doOutcome
1Decide scope (site vs tenant vs library path)Avoid noisy results
2Write KQL for querytextAccurate matching
3Pick selectpropertiesSmall + predictable payload
4Add paging (rowlimit, startrow)Stable pagination
5Add refiners (refiners)Faceted navigation
6Apply refinementfilters (FQL)Real filtering (PDF/DOCX/etc.)
7Implement 429 backoffReliable at scale

B) Technical Cheat Sheet (most-used parameters)

ParameterPurposeExample
querytextMain KQL queryquerytext='budget AND FileExtension:pdf'
selectpropertiesWhich managed props return'Title,Path,Author,LastModifiedTime'
rowlimitPage sizerowlimit=20
startrowOffsetstartrow=40
trimduplicatesRemove near-duplicatestrimduplicates=true
sortlistSort results'LastModifiedTime:descending'
refinersWhich facets to computerefiners='fileExtension,Author'
refinementfiltersApply filters (FQL)fileExtension:equals("pdf") (Microsoft Learn)
/_api/search/suggestSuggestions endpointTypeahead UX (Microsoft Learn)

Edvaldo Guimrães Filho Avatar

Published by