suo
API reference

Search

POST /1/indexes/:index/search — the request and response shape, and how to read explanations.

POST /1/indexes/:index/search

Requires a search key, a scoped token, or a write/admin key.

Request

type SearchRequest = {
	q: string;
	mode?: 'typing' | 'committed';
	filters?: Filter[];
	facets?: string[];
	page?: number;
	hitsPerPage?: number;
	explain?: boolean;
	typoTolerance?: boolean;
	synonyms?: boolean;
	prefix?: boolean;
	minDataVersion?: number;
	clickAnalytics?: boolean;
};
FieldDefaultNotes
q—Required. Up to 512 characters.
mode'typing''typing' caps candidates at 200 for as-you-type speed; 'committed' (on Enter) raises the cap to 500.
filters[]See Filters below.
facets[]Attribute names to return counts for; must be in attributesForFaceting.
page0Zero-indexed.
hitsPerPage20Capped at 100.
explainfalseInclude the full ranking vector on every hit — see The ranking cascade.
typoToleranceindex settingOverride the index's default for this query only.
synonymsindex settingOverride the index's default for this query only.
prefixtrueSet false to require whole-word matches only.
minDataVersion—Wait for the primary to have applied at least this data version — see Read-your-writes.
clickAnalyticsfalseInclude a queryID in the response for use with Click events.

Filters

type Filter =
	| { attribute: string; op: 'eq'; value: string | number | boolean }
	| { attribute: string; op: 'in'; values: (string | number)[] }
	| { attribute: string; op: 'range'; gte?: number; lte?: number };

Filters are always structured parameters, never a string you build yourself — there is no filter syntax to escape or get wrong.

{ "q": "highlights", "filters": [{ "attribute": "lang", "op": "in", "values": ["en", "zh-TW"] }] }

Response

type SearchResponse = {
	query: string;
	correctedQuery: string | null;
	hits: Hit[];
	nbHits: number;
	page: number;
	hitsPerPage: number;
	facets: Record<string, Record<string, number>>;
	processingTimeMs: number;
	dataVersion: number;
	degraded: boolean;
	queryID: string | null;
};

Each hit:

type Hit = {
	objectID: string;
	record: Record<string, unknown>;
	highlight: Record<string, Highlighted>;
	snippet: Record<string, Highlighted>;
	explanation?: Explanation;
};

highlight and snippet mirror your searchable attributes; each is a list of segments marked matched or not, with which method found the match (word, bigram, typo or prefix) — enough to render CJK-safe highlighting without ever calling FTS highlight() yourself.

Example

curl -X POST https://api.usesuo.com/1/indexes/docs/search \
  -H "x-suo-key: suo_search_YOUR_KEY" \
  -H "content-type: application/json" \
  -d '{ "q": "清华", "explain": true }'
{
	"query": "清华",
	"correctedQuery": null,
	"hits": [
		{
			"objectID": "campus-guide-tsinghua",
			"record": { "title": "清华大学校园指南" },
			"highlight": { "title": { "segments": [{ "text": "清华", "match": true, "via": "bigram" }, { "text": "大学校园指南", "match": false }], "fullyMatched": false } },
			"snippet": {},
			"explanation": { "vector": { "pinned": null, "words": 1, "typos": 0, "proximity": 0, "attribute": { "tier": 0, "position": 0, "name": "title" }, "exactness": 1, "custom": [], "bm25": 6.02 }, "decidedAgainstNextBy": "attribute", "matchedTerms": [{ "query": "清华", "indexed": "清华", "via": "bigram" }] }
		}
	],
	"nbHits": 1,
	"page": 0,
	"hitsPerPage": 20,
	"facets": {},
	"processingTimeMs": 6,
	"dataVersion": 481,
	"degraded": false,
	"queryID": null
}

Degraded results

If a dependency (typically the vector leg of a hybrid search past M8, or a replica under load) cannot answer in time, suo returns lexical-only results with degraded: true and the x-suo-degraded header, rather than failing the request. The widget shows a small notice and keeps the results on screen — see Degraded vs error.

On this page