Skip to main content
GET /search searches individual messages across all your conversations and returns snippet-level hits. Every result carries a conversation_id + message_index anchor, so you can jump straight to the surrounding turns with GET /conversations/:id/context.

Query parameters

string
required
The search query, 1–500 characters. Leading and trailing whitespace is trimmed; a whitespace-only query returns 400.
string
default:"hybrid"
keyword, semantic, or hybrid. Keyword matches literal substrings; semantic matches by meaning; hybrid fuses both. See Search modes.
string
Restrict results to conversations from one end user. Maximum 200 characters.
string
default:"all"
One of all, 1h, 24h, 3d, 7d, 30d, 90d.
integer
default:"20"
Maximum results to return, 1–100. There is no offset — pagination is limit-only.
string
default:"false"
true or false. When true, matches inside tool call inputs and tool results are included in the result list.

Search modes and degradation

The response always reports which mode actually ran (search_mode) and why, if it differs from what you requested (degrade_reason). A semantic or hybrid request degrades transparently to keyword rather than failing:
Scores use a different scale per mode and are not comparable across modes: in keyword mode the score is an integer count of substring matches; in semantic mode it is a similarity value; in hybrid mode it is a small rank-fusion fraction. Branch on search_mode before applying any threshold, and use scores only to order results within a single response. Scores are rounded to 4 decimals.

Response fields

string
The query as searched (trimmed).
string
The mode that actually ran: keyword, semantic, or hybrid. May differ from the requested mode.
string
none, semantic_unavailable, or below_relevance_floor.
array
Ranked hits. Each hit has conversation_id, message_index, role (user or assistant), timestamp, snippet (the matched text with surrounding context), score, cluster_id and cluster_name (the conversation’s use-case cluster, or null), and conversation_summary (a one-line summary of the conversation, or null). Hits that resolve to a specific content block additionally carry unit_id, content_block_index, block_type, tool_name, and chunk_index.
object
Exact counts of distinct conversations containing a literal (case-insensitive) match for the query — independent of the ranked page. matched_conversations covers the requested window; prior_window_matched_conversations covers the equal-length window immediately before it (null when time_range=all). Either can be null if the count query timed out — a missing number, never a fabricated zero.
object
limit (requested page size), returned (hits in this response), and has_more (true when the ranked candidate pool held more hits than the page). There is no offset; to see different results, narrow q, time_range, or user_id.

Example

To read the turns around the first hit:

Next steps

  • Conversations — fetch windowed context around any search hit.
  • Signals — if you are hunting failures, the frustration and tool-failure endpoints already embed context.
  • CLI referencemoda search wraps this endpoint with the same modes.