---
title: "Search mail"
method: GET
path: "/v1/search"
tags: ["Search"]
---

# Search mail

`GET /v1/search`

## Request

- Base URL: `https://api.mails.ai`
- URL: `https://api.mails.ai/v1/search`
- Auth: HTTP bearer

**Requires scope:** `read`. Full-text search over received and sent mail (subject, body text and addresses), newest first. Each hit says whether it was `received` or `sent` and carries a `snippet` around the match. Never leaves the workspace; a key tied to one agent searches that agent's mail; a test key searches test mail only and a live key live mail only. A search that runs past 5 seconds is stopped (400, narrow it), and a workspace may search 120 times a minute (429 with Retry-After past that).

## Query parameters

- `q` string
- `agent_id` string
- `from` string
- `to` string
- `after` string
- `before` string
- `has_attachment` boolean
- `folder` 'inbox' | 'archive' | 'spam' | 'trash' | 'sent' | 'all'
- `label` string
- `type` 'received' | 'sent'
- `limit` integer
- `cursor` string

## Response `200`

A page of hits.

- object
  - `data` SearchHit[], required: The items on this page, at most `limit` of them.
    - `type` 'received' | 'sent', required: `received` for a message an agent received, `sent` for one it sent; it says which kind of id `id` is.
    - `id` string, required: The message id (`rcv_…` or `msg_…`).
    - `thread_id` string, nullable, required: The thread (`thrd_…`) the message is in. Null for a message in no thread.
    - `agent_id` string, required: Id (`agt_…`) of the agent that received or sent the message.
    - `subject` string, nullable, required: The message's subject. Null when it has none.
    - `from` object, required: Who the message is from: the sender for received mail, the agent itself for sent mail.
      - `address` string, required: The sender's address for received mail, as on the received message's `from.address`; the sending agent's address for sent mail.
      - `name` string, nullable, required: The sender's display name on received mail, null when there is none. Always null on sent mail.
    - `to` string[], required: For received mail, the API address of the one agent it was delivered to. For sent mail, its `to` recipients; `cc` and `bcc` are not listed.
    - `date` string, date-time, required: Received time, or sent time for sent mail.
    - `has_attachment` boolean, required: For received mail, true when it carries at least one file attachment (images embedded in the HTML do not count); for sent mail, true when it was sent with any.
    - `folder` 'inbox' | 'archive' | 'spam' | 'trash' | 'sent', nullable, required: The folder of the message's thread. A message in no thread counts as `inbox`; null only when its thread cannot be found.
    - `labels` string[], required: The label names on the message's thread. Empty when the thread has none or the message is in no thread.
    - `snippet` string, required: Plain text from the message's body around the match (the start of the body when the match is only in the subject or an address, or when `q` is absent).
  - `has_more` boolean, required: True when more items follow this page: pass `next_cursor` as `cursor` to get them.
  - `next_cursor` string: Pass as `cursor` to fetch the next page. Absent on the last page.

## Other responses

- `400`: Invalid request — malformed JSON or a field failed validation. Also `feature_not_enabled` when this server has a feature switched off (custom domains before they are available).
- `401`: Missing or invalid API key.
- `403`: The API key lacks the required scope (`insufficient_scope`), or the token belongs to an app connected by sign-in and this operation is closed to connected apps (`connected_app_not_allowed`).
- `409`: Conflict — duplicate resource, an in-progress idempotent request, a draft or scheduled message that started sending while it was being changed, or a thread deleted for good while one of its messages is being sent (`duplicate_resource`); or an agent name more than one agent has (`agent_name_conflict`: use the agent's id); or a status change on an archived agent, since archiving cannot be undone (`agent_archived`).
- `429`: Rate or quota limit exceeded. The body's `resets_at` and `retry_after_seconds` say when the request can succeed. A `Retry-After` header is sent only when that is 60 seconds or less; a longer wait (an hourly, daily or monthly cap) sends none, so retry at `resets_at` or raise the limit instead of sleeping.
- `500`: Internal server error.

---

[API](https://skmtc.dev/mails/apis/mails-ai-api.md) · [All operations](https://skmtc.dev/mails/apis/mails-ai-api/llms.txt) · [OpenAPI document](https://skmtc.dev/mails/apis/mails-ai-api/revisions/4d4930a8c33b?raw)
