Skip to main content
Version: v0.25.0

Search stored messages

GET /api/search searches stored message text across sessions. It requires an operator or admin key and is enabled by default with the built-in database full-text provider. A viewer key gets 403. Set SEARCH_ENABLED=false to remove the route, or SEARCH_PROVIDER=builtin-fts to keep the built-in provider active when search plugins are installed.

curl --get http://localhost:2785/api/search \
-H "X-API-Key: YOUR_API_KEY" \
--data-urlencode "q=invoice" \
--data-urlencode "sessionId=8f3c2b1a-9d4e-4c7a-8b2f-1e6d5a4c3b2a" \
--data-urlencode "chatId=628123456789@c.us" \
--data-urlencode "direction=incoming" \
--data-urlencode "limit=20"

Filters and pagination​

ParameterMeaning
qRequired, non-blank text. At most 1000 characters and 32 search words. Punctuation separates words; a standalone symbol or emoji counts as a word. Repeated whitespace-separated words count once.
sessionId, chatIdNarrow the search to one session or chat. These filters cannot expand the key's permissions.
directionincoming or outgoing.
typeStored message type, for example text, image or poll.
fromSender filter.
dateFrom, dateToInclusive bounds in Unix epoch milliseconds.
limit, offsetPage size (default 50) and offset from 0 to 100000. limit is capped by SEARCH_LIMIT_MAX (default 100).

The response has hits, total, tookMs and provider. A hit includes its stored messageId, WhatsApp waMessageId, sessionId, chatId, body, snippet, timestamp, type, direction, from and optional score. Use offset to request subsequent pages.

This endpoint searches indexed text. To filter message rows by message time, type, direction or message reference without a full-text query, use the stored message list.

API-key restrictions​

The gateway applies allowedSessions and, since v0.25.0, allowedChats before the built-in provider computes hits, pagination and totals. A chat-scoped key can omit chatId to search its allowed chats; an explicit disallowed chat gets 403. Search through a plugin provider gets 403 for a chat-scoped key, so select SEARCH_PROVIDER=builtin-fts for this use case.

On SQLite, hits returned to a session-scoped or chat-scoped key omit score, because the full-index score would reflect messages outside its scope. They remain in relevance order. See Authentication for creating restricted keys.

Query syntax and snippets​

PostgreSQL uses websearch_to_tsquery, supporting quoted phrases, OR and exclusion. SQLite treats each whitespace-separated token literally and combines tokens with AND; its FTS query operators are not exposed.

Snippets use literal <mark> and </mark> markers around matches. The rest of the body is not HTML-escaped. Split on those markers and render the segments as text; never assign a raw snippet to innerHTML.

StatusCause
400Blank or oversized q, more than 32 words, or an invalid numeric parameter.
401Missing, unknown, revoked or expired API key.
403Insufficient role, IP/session/chat scope denial, or chat-scoped search through a plugin.
501No provider is available, including a custom SQLite build without FTS5.
502A plugin returned a malformed result.
503The active search plugin failed or timed out.

See the API reference for the full response schema and Configuration for search settings.