---
title: "Search mail by meaning and by words"
method: GET
path: "/api/emails/search"
tags: ["Email"]
---

# Search mail by meaning and by words

`GET /api/emails/search`

## Request

- Base URL: `https://domani.run`
- URL: `https://domani.run/api/emails/search`
- Auth: HTTP bearer

Searches the mailboxes you may read, or one mailbox with `mailbox`. Hybrid by default: the message embedding (pgvector, cosine) and the weighted words index are fused by reciprocal rank with a small recency boost, in one statement; `why` tells which branch matched. Gmail operators in `q`: `from:` `to:` `cc:` `subject:` `label:` `filename:` `mailbox:` `is:unread|read|starred|unstarred` `has:attachment` `in:inbox|archive|sent|spam|trash|anywhere` `after:2026/08/01` `before:` `newer_than:7d` `older_than:2w` (h/d/w/m/y), `"a phrase"`, `-word` or `-from:` to exclude, `a OR b`. A query made of operators alone lists the matching messages newest first (`why: filter`). Sign-in codes and links, spam and system mail are never indexed; mailboxes listed in the assistant's `search_opt_out_mailbox_ids` are searched by words only. A token bound to an agent identity searches only the mailboxes its agent operates. Requires `email:read`. Without `email:auth_secrets`, inbound authentication mail is left out before ranking: no word, operator or filter matches it, so a search for a sign-in code finds nothing.

## Query parameters

- `mailbox` string
- `q` string, required
- `mode` 'hybrid' | 'semantic' | 'keyword'
- `limit` integer
- `since` string, date-time
- `until` string, date-time
- `folder` 'inbox' | 'archive' | 'sent' | 'spam' | 'trash' | 'anywhere'
- `from` string
- `label` string

## Response `200`

Ranked results

- object
  - `results` object[]
    - `message` object
      - `id` string
      - `mailbox` string
      - `thread_key` string
      - `direction` string
      - `from` string
      - `to` string
      - `subject` string, nullable
      - `snippet` string: The message's own words, first 200 characters.
      - `created_at` string
      - `folder` string
      - `is_read` boolean
    - `score` number: Fused rank score with a small recency boost; higher is better.
    - `why` 'words' | 'meaning' | 'both' | 'filter': words: the query's words matched; meaning: only the embedding did; both; filter: operators alone selected it.
    - `highlight` string, nullable: A fragment with the matched words in brackets, when words matched.
  - `mode` 'hybrid' | 'semantic' | 'keyword'
  - `query` string
  - `took_ms` integer
  - `hint` string

## Other responses

- `400`: Missing or invalid query
- `401`: Missing or invalid API key
- `404`: Mailbox not found
- `429`: Rate limit exceeded - wait and retry

---

[API](https://skmtc.dev/domani/apis/domani-run-api.md) · [All operations](https://skmtc.dev/domani/apis/domani-run-api/llms.txt) · [OpenAPI document](https://skmtc.dev/domani/apis/domani-run-api/revisions/7679f8159533?raw)
